diff options
356 files changed, 7407 insertions, 3930 deletions
diff --git a/Android.bp b/Android.bp index 1b81306b3699..dc9c4d4b5a85 100644 --- a/Android.bp +++ b/Android.bp @@ -357,6 +357,7 @@ java_defaults { "core/java/android/view/autofill/IAutoFillManagerClient.aidl", "core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl", "core/java/android/view/autofill/IAutofillWindowPresenter.aidl", + "core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl", "core/java/android/view/contentcapture/IContentCaptureManager.aidl", "core/java/android/view/IApplicationToken.aidl", "core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl", @@ -387,6 +388,7 @@ java_defaults { "core/java/android/speech/tts/ITextToSpeechService.aidl", "core/java/com/android/internal/app/IAppOpsActiveCallback.aidl", "core/java/com/android/internal/app/IAppOpsCallback.aidl", + "core/java/com/android/internal/app/IAppOpsNotedCallback.aidl", "core/java/com/android/internal/app/IAppOpsService.aidl", "core/java/com/android/internal/app/IBatteryStats.aidl", "core/java/com/android/internal/app/ISoundTriggerService.aidl", diff --git a/CleanSpec.mk b/CleanSpec.mk index 478e4fe86d3b..d01e183df84d 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -251,6 +251,7 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.media. $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/overlay/ExperimentNavigationBarSlim) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/overlay/ExperimentNavigationBarSlim) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/ExperimentNavigationBarSlim) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/SystemUI) # ****************************************************************** # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER # ****************************************************************** diff --git a/api/current.txt b/api/current.txt index d60c1cc35dee..de4e82416193 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11565,18 +11565,19 @@ package android.content.pm { field public static final java.lang.String FEATURE_DEVICE_ADMIN = "android.software.device_admin"; field public static final java.lang.String FEATURE_EMBEDDED = "android.hardware.type.embedded"; field public static final java.lang.String FEATURE_ETHERNET = "android.hardware.ethernet"; - field public static final java.lang.String FEATURE_FACE = "android.hardware.face"; + field public static final java.lang.String FEATURE_FACE = "android.hardware.biometrics.face"; field public static final java.lang.String FEATURE_FAKETOUCH = "android.hardware.faketouch"; field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct"; field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = "android.hardware.faketouch.multitouch.jazzhand"; - field public static final java.lang.String FEATURE_FINGERPRINT = "android.hardware.fingerprint"; + field public static final java.lang.String FEATURE_FINGERPRINT = "android.hardware.biometrics.fingerprint"; + field public static final java.lang.String FEATURE_FINGERPRINT_PRE_29 = "android.hardware.fingerprint"; field public static final java.lang.String FEATURE_FREEFORM_WINDOW_MANAGEMENT = "android.software.freeform_window_management"; field public static final java.lang.String FEATURE_GAMEPAD = "android.hardware.gamepad"; field public static final java.lang.String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors"; field public static final java.lang.String FEATURE_HOME_SCREEN = "android.software.home_screen"; field public static final java.lang.String FEATURE_INPUT_METHODS = "android.software.input_methods"; field public static final java.lang.String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels"; - field public static final java.lang.String FEATURE_IRIS = "android.hardware.iris"; + field public static final java.lang.String FEATURE_IRIS = "android.hardware.biometrics.iris"; field public static final java.lang.String FEATURE_LEANBACK = "android.software.leanback"; field public static final java.lang.String FEATURE_LEANBACK_ONLY = "android.software.leanback_only"; field public static final java.lang.String FEATURE_LIVE_TV = "android.software.live_tv"; @@ -14861,6 +14862,7 @@ package android.graphics { method public int getAmbientShadowColor(); method public int getBottom(); method public float getCameraDistance(); + method public boolean getClipToBounds(); method public boolean getClipToOutline(); method public float getElevation(); method public int getHeight(); @@ -14881,6 +14883,7 @@ package android.graphics { method public float getTranslationY(); method public float getTranslationZ(); method public long getUniqueId(); + method public boolean getUseCompositingLayer(); method public int getWidth(); method public boolean hasDisplayList(); method public boolean hasIdentityMatrix(); @@ -24985,8 +24988,9 @@ package android.media { field public static final int RATING_KEY_BY_USER = 268435457; // 0x10000001 } - public class MediaMetadataRetriever { + public class MediaMetadataRetriever implements java.lang.AutoCloseable { ctor public MediaMetadataRetriever(); + method public void close(); method public java.lang.String extractMetadata(int); method public byte[] getEmbeddedPicture(); method public android.graphics.Bitmap getFrameAtIndex(int, android.media.MediaMetadataRetriever.BitmapParams); @@ -25308,24 +25312,24 @@ package android.media { method public java.lang.Object clearNextDataSources(); method public void clearPendingCommands(); method public void close(); - method public java.lang.Object deselectTrack(int); + method public java.lang.Object deselectTrack(android.media.DataSourceDesc, int); method public android.media.AudioAttributes getAudioAttributes(); method public int getAudioSessionId(); - method public long getBufferedPosition(); + method public long getBufferedPosition(android.media.DataSourceDesc); method public android.media.DataSourceDesc getCurrentDataSource(); method public long getCurrentPosition(); - method public long getDuration(); + method public long getDuration(android.media.DataSourceDesc); method public float getMaxPlayerVolume(); method public android.os.PersistableBundle getMetrics(); method public android.media.PlaybackParams getPlaybackParams(); method public float getPlayerVolume(); method public android.media.AudioDeviceInfo getPreferredDevice(); method public android.media.AudioDeviceInfo getRoutedDevice(); - method public int getSelectedTrack(int); + method public int getSelectedTrack(android.media.DataSourceDesc, int); method public int getState(); method public android.media.SyncParams getSyncParams(); method public android.media.MediaTimestamp getTimestamp(); - method public java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo(); + method public java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo(android.media.DataSourceDesc); method public android.media.VideoSize getVideoSize(); method public boolean isLooping(); method public java.lang.Object loopCurrent(boolean); @@ -25338,7 +25342,7 @@ package android.media { method public void reset(); method public java.lang.Object seekTo(long); method public java.lang.Object seekTo(long, int); - method public java.lang.Object selectTrack(int); + method public java.lang.Object selectTrack(android.media.DataSourceDesc, int); method public java.lang.Object setAudioAttributes(android.media.AudioAttributes); method public java.lang.Object setAudioSessionId(int); method public java.lang.Object setAuxEffectSendLevel(float); @@ -26073,7 +26077,12 @@ package android.media { public class ThumbnailUtils { ctor public ThumbnailUtils(); - method public static android.graphics.Bitmap createVideoThumbnail(java.lang.String, int); + method public static deprecated android.graphics.Bitmap createAudioThumbnail(java.lang.String, int); + method public static android.graphics.Bitmap createAudioThumbnail(java.io.File, android.util.Size, android.os.CancellationSignal) throws java.io.IOException; + method public static deprecated android.graphics.Bitmap createImageThumbnail(java.lang.String, int); + method public static android.graphics.Bitmap createImageThumbnail(java.io.File, android.util.Size, android.os.CancellationSignal) throws java.io.IOException; + method public static deprecated android.graphics.Bitmap createVideoThumbnail(java.lang.String, int); + method public static android.graphics.Bitmap createVideoThumbnail(java.io.File, android.util.Size, android.os.CancellationSignal) throws java.io.IOException; method public static android.graphics.Bitmap extractThumbnail(android.graphics.Bitmap, int, int); method public static android.graphics.Bitmap extractThumbnail(android.graphics.Bitmap, int, int, int); field public static final int OPTIONS_RECYCLE_INPUT = 2; // 0x2 @@ -28316,6 +28325,11 @@ package android.net { field public int serverAddress; } + public class InetAddresses { + method public static boolean isNumericAddress(java.lang.String); + method public static java.net.InetAddress parseNumericAddress(java.lang.String); + } + public final class IpPrefix implements android.os.Parcelable { method public boolean contains(java.net.InetAddress); method public int describeContents(); @@ -41482,6 +41496,7 @@ package android.service.wallpaper { method protected void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]); method public int getDesiredMinimumHeight(); method public int getDesiredMinimumWidth(); + method public android.content.Context getDisplayContext(); method public android.view.SurfaceHolder getSurfaceHolder(); method public boolean isPreview(); method public boolean isVisible(); @@ -46941,7 +46956,7 @@ package android.transition { ctor public deprecated Scene(android.view.ViewGroup, android.view.ViewGroup); method public void enter(); method public void exit(); - method public static android.transition.Scene getCurrentScene(android.view.View); + method public static android.transition.Scene getCurrentScene(android.view.ViewGroup); method public static android.transition.Scene getSceneForLayout(android.view.ViewGroup, int, android.content.Context); method public android.view.ViewGroup getSceneRoot(); method public void setEnterAction(java.lang.Runnable); diff --git a/api/system-current.txt b/api/system-current.txt index a83c32def7be..dfcae63f164e 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -205,6 +205,10 @@ package android { field public static final int config_sendPackageName = 17891328; // 0x1110000 } + public static final class R.color { + field public static final int system_notification_accent_color = 17170460; // 0x106001c + } + public static final class R.dimen { field public static final int config_mediaMetadataBitmapMaxSize = 17104904; // 0x1050008 field public static final int config_restrictedIconSize = 17104903; // 0x1050007 @@ -3713,6 +3717,7 @@ package android.net.wifi { method public boolean isWifiScannerSupported(); method public void registerNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback, android.os.Handler); method public void save(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener); + method public void setDeviceMobilityState(int); method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration); method public boolean startScan(android.os.WorkSource); method public void unregisterNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback); @@ -3720,6 +3725,10 @@ package android.net.wifi { field public static final int CHANGE_REASON_CONFIG_CHANGE = 2; // 0x2 field public static final int CHANGE_REASON_REMOVED = 1; // 0x1 field public static final java.lang.String CONFIGURED_NETWORKS_CHANGED_ACTION = "android.net.wifi.CONFIGURED_NETWORKS_CHANGE"; + field public static final int DEVICE_MOBILITY_STATE_HIGH_MVMT = 1; // 0x1 + field public static final int DEVICE_MOBILITY_STATE_LOW_MVMT = 2; // 0x2 + field public static final int DEVICE_MOBILITY_STATE_STATIONARY = 3; // 0x3 + field public static final int DEVICE_MOBILITY_STATE_UNKNOWN = 0; // 0x0 field public static final java.lang.String EXTRA_CHANGE_REASON = "changeReason"; field public static final java.lang.String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges"; field public static final java.lang.String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state"; @@ -5008,7 +5017,7 @@ package android.service.contentcapture { method public final java.util.Set<android.content.ComponentName> getContentCaptureDisabledActivities(); method public final java.util.Set<java.lang.String> getContentCaptureDisabledPackages(); method public void onActivitySnapshot(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.SnapshotData); - method public abstract void onContentCaptureEventsRequest(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.ContentCaptureEventsRequest); + method public void onContentCaptureEventsRequest(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.ContentCaptureEventsRequest); method public void onCreateContentCaptureSession(android.view.contentcapture.ContentCaptureContext, android.view.contentcapture.ContentCaptureSessionId); method public void onDestroyContentCaptureSession(android.view.contentcapture.ContentCaptureSessionId); method public final void setActivityContentCaptureEnabled(android.content.ComponentName, boolean); @@ -5689,6 +5698,83 @@ package android.telephony { field public static final java.lang.String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string"; } + public class DisconnectCause { + field public static final int ALREADY_DIALING = 72; // 0x48 + field public static final int ANSWERED_ELSEWHERE = 52; // 0x34 + field public static final int BUSY = 4; // 0x4 + field public static final int CALLING_DISABLED = 74; // 0x4a + field public static final int CALL_BARRED = 20; // 0x14 + field public static final int CALL_PULLED = 51; // 0x33 + field public static final int CANT_CALL_WHILE_RINGING = 73; // 0x49 + field public static final int CDMA_ACCESS_BLOCKED = 35; // 0x23 + field public static final int CDMA_ACCESS_FAILURE = 32; // 0x20 + field public static final int CDMA_ALREADY_ACTIVATED = 49; // 0x31 + field public static final int CDMA_DROP = 27; // 0x1b + field public static final int CDMA_INTERCEPT = 28; // 0x1c + field public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 26; // 0x1a + field public static final int CDMA_NOT_EMERGENCY = 34; // 0x22 + field public static final int CDMA_PREEMPTED = 33; // 0x21 + field public static final int CDMA_REORDER = 29; // 0x1d + field public static final int CDMA_RETRY_ORDER = 31; // 0x1f + field public static final int CDMA_SO_REJECT = 30; // 0x1e + field public static final int CONGESTION = 5; // 0x5 + field public static final int CS_RESTRICTED = 22; // 0x16 + field public static final int CS_RESTRICTED_EMERGENCY = 24; // 0x18 + field public static final int CS_RESTRICTED_NORMAL = 23; // 0x17 + field public static final int DATA_DISABLED = 54; // 0x36 + field public static final int DATA_LIMIT_REACHED = 55; // 0x37 + field public static final int DIALED_CALL_FORWARDING_WHILE_ROAMING = 57; // 0x39 + field public static final int DIALED_MMI = 39; // 0x27 + field public static final int DIAL_LOW_BATTERY = 62; // 0x3e + field public static final int DIAL_MODIFIED_TO_DIAL = 48; // 0x30 + field public static final int DIAL_MODIFIED_TO_DIAL_VIDEO = 66; // 0x42 + field public static final int DIAL_MODIFIED_TO_SS = 47; // 0x2f + field public static final int DIAL_MODIFIED_TO_USSD = 46; // 0x2e + field public static final int DIAL_VIDEO_MODIFIED_TO_DIAL = 69; // 0x45 + field public static final int DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 70; // 0x46 + field public static final int DIAL_VIDEO_MODIFIED_TO_SS = 67; // 0x43 + field public static final int DIAL_VIDEO_MODIFIED_TO_USSD = 68; // 0x44 + field public static final int EMERGENCY_PERM_FAILURE = 64; // 0x40 + field public static final int EMERGENCY_TEMP_FAILURE = 63; // 0x3f + field public static final int ERROR_UNSPECIFIED = 36; // 0x24 + field public static final int FDN_BLOCKED = 21; // 0x15 + field public static final int ICC_ERROR = 19; // 0x13 + field public static final int IMEI_NOT_ACCEPTED = 58; // 0x3a + field public static final int IMS_ACCESS_BLOCKED = 60; // 0x3c + field public static final int IMS_MERGED_SUCCESSFULLY = 45; // 0x2d + field public static final int IMS_SIP_ALTERNATE_EMERGENCY_CALL = 71; // 0x47 + field public static final int INCOMING_MISSED = 1; // 0x1 + field public static final int INCOMING_REJECTED = 16; // 0x10 + field public static final int INVALID_CREDENTIALS = 10; // 0xa + field public static final int INVALID_NUMBER = 7; // 0x7 + field public static final int LIMIT_EXCEEDED = 15; // 0xf + field public static final int LOCAL = 3; // 0x3 + field public static final int LOST_SIGNAL = 14; // 0xe + field public static final int LOW_BATTERY = 61; // 0x3d + field public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 53; // 0x35 + field public static final int MMI = 6; // 0x6 + field public static final int NORMAL = 2; // 0x2 + field public static final int NORMAL_UNSPECIFIED = 65; // 0x41 + field public static final int NOT_DISCONNECTED = 0; // 0x0 + field public static final int NOT_VALID = -1; // 0xffffffff + field public static final int NO_PHONE_NUMBER_SUPPLIED = 38; // 0x26 + field public static final int NUMBER_UNREACHABLE = 8; // 0x8 + field public static final int OTASP_PROVISIONING_IN_PROCESS = 76; // 0x4c + field public static final int OUTGOING_CANCELED = 44; // 0x2c + field public static final int OUTGOING_FAILURE = 43; // 0x2b + field public static final int OUT_OF_NETWORK = 11; // 0xb + field public static final int OUT_OF_SERVICE = 18; // 0x12 + field public static final int POWER_OFF = 17; // 0x11 + field public static final int SERVER_ERROR = 12; // 0xc + field public static final int SERVER_UNREACHABLE = 9; // 0x9 + field public static final int TIMED_OUT = 13; // 0xd + field public static final int TOO_MANY_ONGOING_CALLS = 75; // 0x4b + field public static final int UNOBTAINABLE_NUMBER = 25; // 0x19 + field public static final int VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED = 50; // 0x32 + field public static final int VOICEMAIL_NUMBER_MISSING = 40; // 0x28 + field public static final int WIFI_LOST = 59; // 0x3b + } + public class MbmsDownloadSession implements java.lang.AutoCloseable { field public static final java.lang.String MBMS_DOWNLOAD_SERVICE_ACTION = "android.telephony.action.EmbmsDownload"; } @@ -5777,14 +5863,134 @@ package android.telephony { } public class PhoneStateListener { + method public void onCallDisconnectCauseChanged(int, int); + method public void onPreciseCallStateChanged(android.telephony.PreciseCallState); method public void onRadioPowerStateChanged(int); method public void onSrvccStateChanged(int); method public void onVoiceActivationStateChanged(int); + field public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000 + field public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800 field public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000 field public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000 field public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000 } + public final class PreciseCallState implements android.os.Parcelable { + method public int describeContents(); + method public int getBackgroundCallState(); + method public int getForegroundCallState(); + method public int getRingingCallState(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.PreciseCallState> CREATOR; + field public static final int PRECISE_CALL_STATE_ACTIVE = 1; // 0x1 + field public static final int PRECISE_CALL_STATE_ALERTING = 4; // 0x4 + field public static final int PRECISE_CALL_STATE_DIALING = 3; // 0x3 + field public static final int PRECISE_CALL_STATE_DISCONNECTED = 7; // 0x7 + field public static final int PRECISE_CALL_STATE_DISCONNECTING = 8; // 0x8 + field public static final int PRECISE_CALL_STATE_HOLDING = 2; // 0x2 + field public static final int PRECISE_CALL_STATE_IDLE = 0; // 0x0 + field public static final int PRECISE_CALL_STATE_INCOMING = 5; // 0x5 + field public static final int PRECISE_CALL_STATE_NOT_VALID = -1; // 0xffffffff + field public static final int PRECISE_CALL_STATE_WAITING = 6; // 0x6 + } + + public class PreciseDisconnectCause { + field public static final int ACCESS_CLASS_BLOCKED = 260; // 0x104 + field public static final int ACCESS_INFORMATION_DISCARDED = 43; // 0x2b + field public static final int ACM_LIMIT_EXCEEDED = 68; // 0x44 + field public static final int BEARER_CAPABILITY_NOT_AUTHORIZED = 57; // 0x39 + field public static final int BEARER_NOT_AVAIL = 58; // 0x3a + field public static final int BEARER_SERVICE_NOT_IMPLEMENTED = 65; // 0x41 + field public static final int BUSY = 17; // 0x11 + field public static final int CALL_BARRED = 240; // 0xf0 + field public static final int CALL_REJECTED = 21; // 0x15 + field public static final int CDMA_ACCESS_BLOCKED = 1009; // 0x3f1 + field public static final int CDMA_ACCESS_FAILURE = 1006; // 0x3ee + field public static final int CDMA_DROP = 1001; // 0x3e9 + field public static final int CDMA_INTERCEPT = 1002; // 0x3ea + field public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 1000; // 0x3e8 + field public static final int CDMA_NOT_EMERGENCY = 1008; // 0x3f0 + field public static final int CDMA_PREEMPTED = 1007; // 0x3ef + field public static final int CDMA_REORDER = 1003; // 0x3eb + field public static final int CDMA_RETRY_ORDER = 1005; // 0x3ed + field public static final int CDMA_SO_REJECT = 1004; // 0x3ec + field public static final int CHANNEL_NOT_AVAIL = 44; // 0x2c + field public static final int CHANNEL_UNACCEPTABLE = 6; // 0x6 + field public static final int CONDITIONAL_IE_ERROR = 100; // 0x64 + field public static final int DESTINATION_OUT_OF_ORDER = 27; // 0x1b + field public static final int ERROR_UNSPECIFIED = 65535; // 0xffff + field public static final int FACILITY_REJECTED = 29; // 0x1d + field public static final int FDN_BLOCKED = 241; // 0xf1 + field public static final int IMEI_NOT_ACCEPTED = 243; // 0xf3 + field public static final int IMSI_UNKNOWN_IN_VLR = 242; // 0xf2 + field public static final int INCOMING_CALLS_BARRED_WITHIN_CUG = 55; // 0x37 + field public static final int INCOMPATIBLE_DESTINATION = 88; // 0x58 + field public static final int INFORMATION_ELEMENT_NON_EXISTENT = 99; // 0x63 + field public static final int INTERWORKING_UNSPECIFIED = 127; // 0x7f + field public static final int INVALID_MANDATORY_INFORMATION = 96; // 0x60 + field public static final int INVALID_NUMBER_FORMAT = 28; // 0x1c + field public static final int INVALID_TRANSACTION_IDENTIFIER = 81; // 0x51 + field public static final int MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 101; // 0x65 + field public static final int MESSAGE_TYPE_NON_IMPLEMENTED = 97; // 0x61 + field public static final int MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 98; // 0x62 + field public static final int NETWORK_DETACH = 261; // 0x105 + field public static final int NETWORK_OUT_OF_ORDER = 38; // 0x26 + field public static final int NETWORK_REJECT = 252; // 0xfc + field public static final int NETWORK_RESP_TIMEOUT = 251; // 0xfb + field public static final int NORMAL = 16; // 0x10 + field public static final int NORMAL_UNSPECIFIED = 31; // 0x1f + field public static final int NOT_VALID = -1; // 0xffffffff + field public static final int NO_ANSWER_FROM_USER = 19; // 0x13 + field public static final int NO_CIRCUIT_AVAIL = 34; // 0x22 + field public static final int NO_DISCONNECT_CAUSE_AVAILABLE = 0; // 0x0 + field public static final int NO_ROUTE_TO_DESTINATION = 3; // 0x3 + field public static final int NO_USER_RESPONDING = 18; // 0x12 + field public static final int NO_VALID_SIM = 249; // 0xf9 + field public static final int NUMBER_CHANGED = 22; // 0x16 + field public static final int OEM_CAUSE_1 = 61441; // 0xf001 + field public static final int OEM_CAUSE_10 = 61450; // 0xf00a + field public static final int OEM_CAUSE_11 = 61451; // 0xf00b + field public static final int OEM_CAUSE_12 = 61452; // 0xf00c + field public static final int OEM_CAUSE_13 = 61453; // 0xf00d + field public static final int OEM_CAUSE_14 = 61454; // 0xf00e + field public static final int OEM_CAUSE_15 = 61455; // 0xf00f + field public static final int OEM_CAUSE_2 = 61442; // 0xf002 + field public static final int OEM_CAUSE_3 = 61443; // 0xf003 + field public static final int OEM_CAUSE_4 = 61444; // 0xf004 + field public static final int OEM_CAUSE_5 = 61445; // 0xf005 + field public static final int OEM_CAUSE_6 = 61446; // 0xf006 + field public static final int OEM_CAUSE_7 = 61447; // 0xf007 + field public static final int OEM_CAUSE_8 = 61448; // 0xf008 + field public static final int OEM_CAUSE_9 = 61449; // 0xf009 + field public static final int ONLY_DIGITAL_INFORMATION_BEARER_AVAILABLE = 70; // 0x46 + field public static final int OPERATOR_DETERMINED_BARRING = 8; // 0x8 + field public static final int OUT_OF_SRV = 248; // 0xf8 + field public static final int PREEMPTION = 25; // 0x19 + field public static final int PROTOCOL_ERROR_UNSPECIFIED = 111; // 0x6f + field public static final int QOS_NOT_AVAIL = 49; // 0x31 + field public static final int RADIO_ACCESS_FAILURE = 253; // 0xfd + field public static final int RADIO_INTERNAL_ERROR = 250; // 0xfa + field public static final int RADIO_LINK_FAILURE = 254; // 0xfe + field public static final int RADIO_LINK_LOST = 255; // 0xff + field public static final int RADIO_OFF = 247; // 0xf7 + field public static final int RADIO_RELEASE_ABNORMAL = 259; // 0x103 + field public static final int RADIO_RELEASE_NORMAL = 258; // 0x102 + field public static final int RADIO_SETUP_FAILURE = 257; // 0x101 + field public static final int RADIO_UPLINK_FAILURE = 256; // 0x100 + field public static final int RECOVERY_ON_TIMER_EXPIRED = 102; // 0x66 + field public static final int REQUESTED_FACILITY_NOT_IMPLEMENTED = 69; // 0x45 + field public static final int REQUESTED_FACILITY_NOT_SUBSCRIBED = 50; // 0x32 + field public static final int RESOURCES_UNAVAILABLE_OR_UNSPECIFIED = 47; // 0x2f + field public static final int SEMANTICALLY_INCORRECT_MESSAGE = 95; // 0x5f + field public static final int SERVICE_OPTION_NOT_AVAILABLE = 63; // 0x3f + field public static final int SERVICE_OR_OPTION_NOT_IMPLEMENTED = 79; // 0x4f + field public static final int STATUS_ENQUIRY = 30; // 0x1e + field public static final int SWITCHING_CONGESTION = 42; // 0x2a + field public static final int TEMPORARY_FAILURE = 41; // 0x29 + field public static final int UNOBTAINABLE_NUMBER = 1; // 0x1 + field public static final int USER_NOT_MEMBER_OF_CUG = 87; // 0x57 + } + public class ServiceState implements android.os.Parcelable { method public android.telephony.NetworkRegistrationState getNetworkRegistrationState(int, int); method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates(); @@ -5822,11 +6028,13 @@ package android.telephony { public class SubscriptionInfo implements android.os.Parcelable { method public java.util.List<android.telephony.UiccAccessRule> getAccessRules(); + method public int getCardId(); } public class SubscriptionManager { method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList(); method public void requestEmbeddedSubscriptionInfoListRefresh(); + method public void requestEmbeddedSubscriptionInfoListRefresh(int); method public void setDefaultDataSubId(int); method public void setDefaultSmsSubId(int); field public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI; diff --git a/api/test-current.txt b/api/test-current.txt index d534501e1a06..46e7683c3cb7 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -655,11 +655,6 @@ package android.media { method public android.media.BufferingParams.Builder setResumePlaybackMarkMs(int); } - public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation { - method public android.media.BufferingParams getBufferingParams(); - method public void setBufferingParams(android.media.BufferingParams); - } - public final class PlaybackParams implements android.os.Parcelable { method public int getAudioStretchMode(); method public android.media.PlaybackParams setAudioStretchMode(int); diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp index 020c443582b2..8d0cee5b938d 100644 --- a/cmds/idmap2/idmap2/Lookup.cpp +++ b/cmds/idmap2/idmap2/Lookup.cpp @@ -36,6 +36,7 @@ #include "idmap2/CommandLineOptions.h" #include "idmap2/Idmap.h" +#include "idmap2/Result.h" #include "idmap2/Xml.h" #include "idmap2/ZipFile.h" @@ -53,39 +54,40 @@ using android::base::StringPrintf; using android::idmap2::CommandLineOptions; using android::idmap2::IdmapHeader; using android::idmap2::ResourceId; +using android::idmap2::Result; using android::idmap2::Xml; using android::idmap2::ZipFile; using android::util::Utf16ToUtf8; namespace { -std::pair<bool, ResourceId> WARN_UNUSED ParseResReference(const AssetManager2& am, - const std::string& res, - const std::string& fallback_package) { + +Result<ResourceId> WARN_UNUSED ParseResReference(const AssetManager2& am, const std::string& res, + const std::string& fallback_package) { // first, try to parse as a hex number char* endptr = nullptr; ResourceId resid; resid = strtol(res.c_str(), &endptr, 16); if (*endptr == '\0') { - return std::make_pair(true, resid); + return {resid}; } // next, try to parse as a package:type/name string resid = am.GetResourceId(res, "", fallback_package); if (is_valid_resid(resid)) { - return std::make_pair(true, resid); + return {resid}; } // end of the road: res could not be parsed - return std::make_pair(false, 0); + return {}; } -std::pair<bool, std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId resid) { +Result<std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId resid) { Res_value value; ResTable_config config; uint32_t flags; ApkAssetsCookie cookie = am.GetResource(resid, false, 0, &value, &config, &flags); if (cookie == kInvalidCookie) { - return std::make_pair(false, ""); + return {}; } std::string out; @@ -123,31 +125,31 @@ std::pair<bool, std::string> WARN_UNUSED GetValue(const AssetManager2& am, Resou out.append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data)); break; } - return std::make_pair(true, out); + return {out}; } -std::pair<bool, std::string> GetTargetPackageNameFromManifest(const std::string& apk_path) { +Result<std::string> GetTargetPackageNameFromManifest(const std::string& apk_path) { const auto zip = ZipFile::Open(apk_path); if (!zip) { - return std::make_pair(false, ""); + return {}; } const auto entry = zip->Uncompress("AndroidManifest.xml"); if (!entry) { - return std::make_pair(false, ""); + return {}; } const auto xml = Xml::Create(entry->buf, entry->size); if (!xml) { - return std::make_pair(false, ""); + return {}; } const auto tag = xml->FindTag("overlay"); if (!tag) { - return std::make_pair(false, ""); + return {}; } const auto iter = tag->find("targetPackage"); if (iter == tag->end()) { - return std::make_pair(false, ""); + return {}; } - return std::make_pair(true, iter->second); + return {iter->second}; } } // namespace @@ -195,14 +197,14 @@ bool Lookup(const std::vector<std::string>& args, std::ostream& out_error) { } apk_assets.push_back(std::move(target_apk)); - bool lookup_ok; - std::tie(lookup_ok, target_package_name) = + const Result<std::string> package_name = GetTargetPackageNameFromManifest(idmap_header->GetOverlayPath().to_string()); - if (!lookup_ok) { + if (!package_name) { out_error << "error: failed to parse android:targetPackage from overlay manifest" << std::endl; return false; } + target_package_name = *package_name; } else if (target_path != idmap_header->GetTargetPath()) { out_error << "error: different target APKs (expected target APK " << target_path << " but " << idmap_path << " has target APK " << idmap_header->GetTargetPath() << ")" @@ -227,21 +229,18 @@ bool Lookup(const std::vector<std::string>& args, std::ostream& out_error) { am.SetApkAssets(raw_pointer_apk_assets); am.SetConfiguration(config); - ResourceId resid; - bool lookup_ok; - std::tie(lookup_ok, resid) = ParseResReference(am, resid_str, target_package_name); - if (!lookup_ok) { + const Result<ResourceId> resid = ParseResReference(am, resid_str, target_package_name); + if (!resid) { out_error << "error: failed to parse resource ID" << std::endl; return false; } - std::string value; - std::tie(lookup_ok, value) = GetValue(am, resid); - if (!lookup_ok) { - out_error << StringPrintf("error: resource 0x%08x not found", resid) << std::endl; + const Result<std::string> value = GetValue(am, *resid); + if (!value) { + out_error << StringPrintf("error: resource 0x%08x not found", *resid) << std::endl; return false; } - std::cout << value << std::endl; + std::cout << *value << std::endl; return true; } diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp index cf72cb94da2c..86b00f1d6e95 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.cpp +++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp @@ -78,6 +78,18 @@ Status Idmap2Service::removeIdmap(const std::string& overlay_apk_path, } } +Status Idmap2Service::verifyIdmap(const std::string& overlay_apk_path, + int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) { + assert(_aidl_return); + const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path); + std::ifstream fin(idmap_path); + const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin); + fin.close(); + std::stringstream dev_null; + *_aidl_return = header && header->IsUpToDate(dev_null); + return ok(); +} + Status Idmap2Service::createIdmap(const std::string& target_apk_path, const std::string& overlay_apk_path, int32_t user_id, std::unique_ptr<std::string>* _aidl_return) { @@ -90,17 +102,6 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path, _aidl_return->reset(nullptr); - const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path); - std::ifstream fin(idmap_path); - const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin); - fin.close(); - // do not reuse error stream from IsUpToDate below, or error messages will be - // polluted with irrelevant data - std::stringstream dev_null; - if (header && header->IsUpToDate(dev_null)) { - return ok(); - } - const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); if (!target_apk) { return error("failed to load apk " + target_apk_path); @@ -119,6 +120,7 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path, } umask(0133); // u=rw,g=r,o=r + const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path); std::ofstream fout(idmap_path); if (fout.fail()) { return error("failed to open idmap path " + idmap_path); diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h index 2b32042d6aa3..4e5abc9069f4 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.h +++ b/cmds/idmap2/idmap2d/Idmap2Service.h @@ -39,6 +39,9 @@ class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 { binder::Status removeIdmap(const std::string& overlay_apk_path, int32_t user_id, bool* _aidl_return); + binder::Status verifyIdmap(const std::string& overlay_apk_path, int32_t user_id, + bool* _aidl_return); + binder::Status createIdmap(const std::string& target_apk_path, const std::string& overlay_apk_path, int32_t user_id, std::unique_ptr<std::string>* _aidl_return); diff --git a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl index 5d196101a7a6..d475417a0935 100644 --- a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl +++ b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl @@ -22,6 +22,7 @@ package android.os; interface IIdmap2 { @utf8InCpp String getIdmapPath(@utf8InCpp String overlayApkPath, int userId); boolean removeIdmap(@utf8InCpp String overlayApkPath, int userId); + boolean verifyIdmap(@utf8InCpp String overlayApkPath, int userId); @nullable @utf8InCpp String createIdmap(@utf8InCpp String targetApkPath, @utf8InCpp String overlayApkPath, int userId); } diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h index 88a835b6439c..d106f19af998 100644 --- a/cmds/idmap2/include/idmap2/ResourceUtils.h +++ b/cmds/idmap2/include/idmap2/ResourceUtils.h @@ -18,19 +18,18 @@ #define IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_ #include <string> -#include <utility> #include "android-base/macros.h" #include "androidfw/AssetManager2.h" #include "idmap2/Idmap.h" +#include "idmap2/Result.h" namespace android { namespace idmap2 { namespace utils { -std::pair<bool, std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am, - ResourceId resid); +Result<std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am, ResourceId resid); } // namespace utils } // namespace idmap2 diff --git a/cmds/idmap2/include/idmap2/Result.h b/cmds/idmap2/include/idmap2/Result.h new file mode 100644 index 000000000000..6189ea371ee1 --- /dev/null +++ b/cmds/idmap2/include/idmap2/Result.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IDMAP2_INCLUDE_IDMAP2_RESULT_H_ +#define IDMAP2_INCLUDE_IDMAP2_RESULT_H_ + +#include <optional> + +namespace android::idmap2 { + +template <typename T> +using Result = std::optional<T>; + +static constexpr std::nullopt_t kResultError = std::nullopt; + +} // namespace android::idmap2 + +#endif // IDMAP2_INCLUDE_IDMAP2_RESULT_H_ diff --git a/cmds/idmap2/include/idmap2/ZipFile.h b/cmds/idmap2/include/idmap2/ZipFile.h index 328bd367adfc..9edbbe0cce90 100644 --- a/cmds/idmap2/include/idmap2/ZipFile.h +++ b/cmds/idmap2/include/idmap2/ZipFile.h @@ -19,10 +19,10 @@ #include <memory> #include <string> -#include <utility> #include "android-base/macros.h" #include "ziparchive/zip_archive.h" +#include "idmap2/Result.h" namespace android { namespace idmap2 { @@ -43,7 +43,7 @@ class ZipFile { static std::unique_ptr<const ZipFile> Open(const std::string& path); std::unique_ptr<const MemoryChunk> Uncompress(const std::string& entryPath) const; - std::pair<bool, uint32_t> Crc(const std::string& entryPath) const; + Result<uint32_t> Crc(const std::string& entryPath) const; ~ZipFile(); diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp index 5a47e301b66c..1ef326793cb4 100644 --- a/cmds/idmap2/libidmap2/Idmap.cpp +++ b/cmds/idmap2/libidmap2/Idmap.cpp @@ -33,6 +33,7 @@ #include "idmap2/Idmap.h" #include "idmap2/ResourceUtils.h" +#include "idmap2/Result.h" #include "idmap2/ZipFile.h" namespace android { @@ -143,18 +144,16 @@ bool IdmapHeader::IsUpToDate(std::ostream& out_error) const { return false; } - bool status; - uint32_t target_crc; - std::tie(status, target_crc) = target_zip->Crc("resources.arsc"); - if (!status) { + Result<uint32_t> target_crc = target_zip->Crc("resources.arsc"); + if (!target_crc) { out_error << "error: failed to get target crc" << std::endl; return false; } - if (target_crc_ != target_crc) { + if (target_crc_ != *target_crc) { out_error << base::StringPrintf( "error: bad target crc: idmap version 0x%08x, file system version 0x%08x", - target_crc_, target_crc) + target_crc_, *target_crc) << std::endl; return false; } @@ -165,17 +164,16 @@ bool IdmapHeader::IsUpToDate(std::ostream& out_error) const { return false; } - uint32_t overlay_crc; - std::tie(status, overlay_crc) = overlay_zip->Crc("resources.arsc"); - if (!status) { + Result<uint32_t> overlay_crc = overlay_zip->Crc("resources.arsc"); + if (!overlay_crc) { out_error << "error: failed to get overlay crc" << std::endl; return false; } - if (overlay_crc_ != overlay_crc) { + if (overlay_crc_ != *overlay_crc) { out_error << base::StringPrintf( "error: bad overlay crc: idmap version 0x%08x, file system version 0x%08x", - overlay_crc_, overlay_crc) + overlay_crc_, *overlay_crc) << std::endl; return false; } @@ -322,17 +320,20 @@ std::unique_ptr<const Idmap> Idmap::FromApkAssets(const std::string& target_apk_ std::unique_ptr<IdmapHeader> header(new IdmapHeader()); header->magic_ = kIdmapMagic; header->version_ = kIdmapCurrentVersion; - bool crc_status; - std::tie(crc_status, header->target_crc_) = target_zip->Crc("resources.arsc"); - if (!crc_status) { + + Result<uint32_t> crc = target_zip->Crc("resources.arsc"); + if (!crc) { out_error << "error: failed to get zip crc for target" << std::endl; return nullptr; } - std::tie(crc_status, header->overlay_crc_) = overlay_zip->Crc("resources.arsc"); - if (!crc_status) { + header->target_crc_ = *crc; + + crc = overlay_zip->Crc("resources.arsc"); + if (!crc) { out_error << "error: failed to get zip crc for overlay" << std::endl; return nullptr; } + header->overlay_crc_ = *crc; if (target_apk_path.size() > sizeof(header->target_path_)) { out_error << "error: target apk path \"" << target_apk_path << "\" longer that maximum size " @@ -358,15 +359,14 @@ std::unique_ptr<const Idmap> Idmap::FromApkAssets(const std::string& target_apk_ const auto end = overlay_pkg->end(); for (auto iter = overlay_pkg->begin(); iter != end; ++iter) { const ResourceId overlay_resid = *iter; - bool lookup_ok; - std::string name; - std::tie(lookup_ok, name) = utils::ResToTypeEntryName(overlay_asset_manager, overlay_resid); - if (!lookup_ok) { + Result<std::string> name = utils::ResToTypeEntryName(overlay_asset_manager, overlay_resid); + if (!name) { continue; } // prepend "<package>:" to turn name into "<package>:<type>/<name>" - name = base::StringPrintf("%s:%s", target_pkg->GetPackageName().c_str(), name.c_str()); - const ResourceId target_resid = NameToResid(target_asset_manager, name); + const std::string full_name = + base::StringPrintf("%s:%s", target_pkg->GetPackageName().c_str(), name->c_str()); + const ResourceId target_resid = NameToResid(target_asset_manager, full_name); if (target_resid == 0) { continue; } diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp index 492e6f049d68..fb3bc5ba9ae8 100644 --- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp +++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp @@ -15,7 +15,6 @@ */ #include <string> -#include <utility> #include "android-base/macros.h" #include "android-base/stringprintf.h" @@ -23,6 +22,7 @@ #include "idmap2/PrettyPrintVisitor.h" #include "idmap2/ResourceUtils.h" +#include "idmap2/Result.h" namespace android { namespace idmap2 { @@ -63,11 +63,9 @@ void PrettyPrintVisitor::visit(const IdmapData::TypeEntry& te) { stream_ << base::StringPrintf("0x%08x -> 0x%08x", target_resid, overlay_resid); if (target_package_loaded) { - bool lookup_ok; - std::string name; - std::tie(lookup_ok, name) = utils::ResToTypeEntryName(target_am_, target_resid); - if (lookup_ok) { - stream_ << " " << name; + Result<std::string> name = utils::ResToTypeEntryName(target_am_, target_resid); + if (name) { + stream_ << " " << *name; } } stream_ << std::endl; diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp index 57cfc8ef85b4..7c24445ef902 100644 --- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp +++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp @@ -16,7 +16,6 @@ #include <cstdarg> #include <string> -#include <utility> #include "android-base/macros.h" #include "android-base/stringprintf.h" @@ -24,6 +23,7 @@ #include "idmap2/RawPrintVisitor.h" #include "idmap2/ResourceUtils.h" +#include "idmap2/Result.h" using android::ApkAssets; @@ -75,14 +75,13 @@ void RawPrintVisitor::visit(const IdmapData::TypeEntry& te) { const ResourceId target_resid = RESID(last_seen_package_id_, te.GetTargetTypeId(), te.GetEntryOffset() + i); const ResourceId overlay_resid = RESID(last_seen_package_id_, te.GetOverlayTypeId(), entry); - bool lookup_ok = false; - std::string name; + Result<std::string> name; if (target_package_loaded) { - std::tie(lookup_ok, name) = utils::ResToTypeEntryName(target_am_, target_resid); + name = utils::ResToTypeEntryName(target_am_, target_resid); } - if (lookup_ok) { + if (name) { print(static_cast<uint32_t>(entry), "0x%08x -> 0x%08x %s", target_resid, overlay_resid, - name.c_str()); + name->c_str()); } else { print(static_cast<uint32_t>(entry), "0x%08x -> 0x%08x", target_resid, overlay_resid); } diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp index e98f843931c8..5c897832e9d5 100644 --- a/cmds/idmap2/libidmap2/ResourceUtils.cpp +++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp @@ -15,12 +15,12 @@ */ #include <string> -#include <utility> #include "androidfw/StringPiece.h" #include "androidfw/Util.h" #include "idmap2/ResourceUtils.h" +#include "idmap2/Result.h" using android::StringPiece16; using android::util::Utf16ToUtf8; @@ -29,11 +29,10 @@ namespace android { namespace idmap2 { namespace utils { -std::pair<bool, std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am, - ResourceId resid) { +Result<std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am, ResourceId resid) { AssetManager2::ResourceName name; if (!am.GetResourceName(resid, &name)) { - return std::make_pair(false, ""); + return {}; } std::string out; if (name.type != nullptr) { @@ -47,7 +46,7 @@ std::pair<bool, std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& } else { out += Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len)); } - return std::make_pair(true, out); + return {out}; } } // namespace utils diff --git a/cmds/idmap2/libidmap2/ZipFile.cpp b/cmds/idmap2/libidmap2/ZipFile.cpp index 3f2079a380d6..9fb611dd8e8d 100644 --- a/cmds/idmap2/libidmap2/ZipFile.cpp +++ b/cmds/idmap2/libidmap2/ZipFile.cpp @@ -16,8 +16,8 @@ #include <memory> #include <string> -#include <utility> +#include "idmap2/Result.h" #include "idmap2/ZipFile.h" namespace android { @@ -57,10 +57,10 @@ std::unique_ptr<const MemoryChunk> ZipFile::Uncompress(const std::string& entryP return chunk; } -std::pair<bool, uint32_t> ZipFile::Crc(const std::string& entryPath) const { +Result<uint32_t> ZipFile::Crc(const std::string& entryPath) const { ::ZipEntry entry; int32_t status = ::FindEntry(handle_, ::ZipString(entryPath.c_str()), &entry); - return std::make_pair(status == 0, entry.crc32); + return status == 0 ? Result<uint32_t>(entry.crc32) : kResultError; } } // namespace idmap2 diff --git a/cmds/idmap2/tests/ResourceUtilsTests.cpp b/cmds/idmap2/tests/ResourceUtilsTests.cpp index 0547fa00de3d..7f60d7529a61 100644 --- a/cmds/idmap2/tests/ResourceUtilsTests.cpp +++ b/cmds/idmap2/tests/ResourceUtilsTests.cpp @@ -16,13 +16,13 @@ #include <memory> #include <string> -#include <utility> #include "gmock/gmock.h" #include "gtest/gtest.h" #include "androidfw/ApkAssets.h" #include "idmap2/ResourceUtils.h" +#include "idmap2/Result.h" #include "TestHelpers.h" @@ -52,17 +52,14 @@ class ResourceUtilsTests : public Idmap2Tests { }; TEST_F(ResourceUtilsTests, ResToTypeEntryName) { - bool lookup_ok; - std::string name; - std::tie(lookup_ok, name) = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000u); - ASSERT_TRUE(lookup_ok); - ASSERT_EQ(name, "integer/int1"); + Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000u); + ASSERT_TRUE(name); + ASSERT_EQ(*name, "integer/int1"); } TEST_F(ResourceUtilsTests, ResToTypeEntryNameNoSuchResourceId) { - bool lookup_ok; - std::tie(lookup_ok, std::ignore) = utils::ResToTypeEntryName(GetAssetManager(), 0x7f123456u); - ASSERT_FALSE(lookup_ok); + Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f123456u); + ASSERT_FALSE(name); } } // namespace idmap2 diff --git a/cmds/idmap2/tests/ZipFileTests.cpp b/cmds/idmap2/tests/ZipFileTests.cpp index a504d3126c05..6e4a501a51ac 100644 --- a/cmds/idmap2/tests/ZipFileTests.cpp +++ b/cmds/idmap2/tests/ZipFileTests.cpp @@ -16,8 +16,8 @@ #include <cstdio> // fclose #include <string> -#include <utility> +#include "idmap2/Result.h" #include "idmap2/ZipFile.h" #include "gmock/gmock.h" @@ -44,14 +44,12 @@ TEST(ZipFileTests, Crc) { auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk"); ASSERT_THAT(zip, NotNull()); - bool status; - uint32_t crc; - std::tie(status, crc) = zip->Crc("AndroidManifest.xml"); - ASSERT_TRUE(status); - ASSERT_EQ(crc, 0x762f3d24); + Result<uint32_t> crc = zip->Crc("AndroidManifest.xml"); + ASSERT_TRUE(crc); + ASSERT_EQ(*crc, 0x762f3d24); - std::tie(status, std::ignore) = zip->Crc("does-not-exist"); - ASSERT_FALSE(status); + Result<uint32_t> crc2 = zip->Crc("does-not-exist"); + ASSERT_FALSE(crc2); } TEST(ZipFileTests, Uncompress) { diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 5df47cd045b0..0c05be1170d1 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -171,6 +171,7 @@ message Atom { DocsUIUserActionReported docs_ui_user_action_reported = 112; WifiEnabledStateChanged wifi_enabled_state_changed = 113; WifiRunningStateChanged wifi_running_state_changed = 114; + AppCompacted app_compacted = 115; } // Pulled events will start at field 10000. @@ -874,6 +875,7 @@ message KernelWakeupReported { /** * Logs when Wifi is toggled on/off. + * Note that Wifi may still perform certain functions (e.g. location scanning) even when disabled. * * Logged from: * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java @@ -888,7 +890,8 @@ message WifiEnabledStateChanged { /** * Logs when an app causes Wifi to run. In this context, 'to run' means to use Wifi Client Mode. - * TODO: Include support for Hotspot. + * TODO: Include support for Hotspot, perhaps by using an extra field to denote 'mode'. + * Note that Wifi Scanning is monitored separately in WifiScanStateChanged. * * Logged from: * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java @@ -3647,3 +3650,65 @@ message DocsUIUserActionReported { message DocsUIInvalidScopedAccessRequestReported { optional android.stats.docsui.InvalidScopedAccess type = 1; } + +/** + * Logs when an app's memory is compacted. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java + */ +message AppCompacted { + // The pid of the process being compacted. + optional int32 pid = 1; + + // The name of the process being compacted. + optional string process_name = 2; + + // The type of compaction. + enum Action { + UNKNOWN = 0; + SOME = 1; + FULL = 2; + } + optional Action action = 3 [default = UNKNOWN]; + + // Total RSS in kilobytes consumed by the process prior to compaction. + optional int64 before_rss_total_kilobytes = 4; + + // File RSS in kilobytes consumed by the process prior to compaction. + optional int64 before_rss_file_kilobytes = 5; + + // Anonymous RSS in kilobytes consumed by the process prior to compaction. + optional int64 before_rss_anon_kilobytes = 6; + + // Swap in kilobytes consumed by the process prior to compaction. + optional int64 before_swap_kilobytes = 7; + + // Total RSS in kilobytes consumed by the process after compaction. + optional int64 after_rss_total_kilobytes = 8; + + // File RSS in kilobytes consumed by the process after compaction. + optional int64 after_rss_file_kilobytes = 9; + + // Anonymous RSS in kilobytes consumed by the process after compaction. + optional int64 after_rss_anon_kilobytes = 10; + + // Swap in kilobytes consumed by the process after compaction. + optional int64 after_swap_kilobytes = 11; + + // The time taken to perform compaction in milliseconds. + optional int64 time_to_compact_millis = 12; + + // The last compaction action performed for this app. + optional Action last_action = 13; + + // The last time that compaction was attempted on this process in seconds + // since boot. + optional int64 last_compact_timestamp = 14; + + // The oom_adj at the time of compaction. + optional int32 oom_adj = 15; + + // The process state at the time of compaction. + optional android.app.ProcessStateEnum process_state = 16 [default = PROCESS_STATE_UNKNOWN]; +} diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp index 16b7e79c0ed7..5fea90b61c80 100644 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ b/cmds/statsd/src/config/ConfigManager.cpp @@ -106,14 +106,14 @@ void ConfigManager::UpdateConfig(const ConfigKey& key, const StatsdConfig& confi // Add to set. mConfigs[key.GetUid()].insert(key); - for (sp<ConfigListener> listener : mListeners) { + for (const sp<ConfigListener>& listener : mListeners) { broadcastList.push_back(listener); } } const int64_t timestampNs = getElapsedRealtimeNs(); // Tell everyone - for (sp<ConfigListener> listener : broadcastList) { + for (const sp<ConfigListener>& listener : broadcastList) { listener->OnConfigUpdated(timestampNs, key, config); } } @@ -137,7 +137,7 @@ void ConfigManager::RemoveConfig(const ConfigKey& key) { if (uidIt != mConfigs.end() && uidIt->second.find(key) != uidIt->second.end()) { // Remove from map uidIt->second.erase(key); - for (sp<ConfigListener> listener : mListeners) { + for (const sp<ConfigListener>& listener : mListeners) { broadcastList.push_back(listener); } } @@ -153,7 +153,7 @@ void ConfigManager::RemoveConfig(const ConfigKey& key) { remove_saved_configs(key); } - for (sp<ConfigListener> listener:broadcastList) { + for (const sp<ConfigListener>& listener:broadcastList) { listener->OnConfigRemoved(key); } } @@ -183,7 +183,7 @@ void ConfigManager::RemoveConfigs(int uid) { mConfigs.erase(uidIt); - for (sp<ConfigListener> listener : mListeners) { + for (const sp<ConfigListener>& listener : mListeners) { broadcastList.push_back(listener); } } @@ -191,7 +191,7 @@ void ConfigManager::RemoveConfigs(int uid) { // Remove separately so if they do anything in the callback they can't mess up our iteration. for (auto& key : removed) { // Tell everyone - for (sp<ConfigListener> listener:broadcastList) { + for (const sp<ConfigListener>& listener:broadcastList) { listener->OnConfigRemoved(key); } } @@ -213,7 +213,7 @@ void ConfigManager::RemoveAllConfigs() { } mConfigReceivers.clear(); - for (sp<ConfigListener> listener : mListeners) { + for (const sp<ConfigListener>& listener : mListeners) { broadcastList.push_back(listener); } } @@ -221,7 +221,7 @@ void ConfigManager::RemoveAllConfigs() { // Remove separately so if they do anything in the callback they can't mess up our iteration. for (auto& key : removed) { // Tell everyone - for (sp<ConfigListener> listener:broadcastList) { + for (const sp<ConfigListener>& listener:broadcastList) { listener->OnConfigRemoved(key); } } diff --git a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp index c8c392016e52..d8229599635c 100644 --- a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp +++ b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp @@ -281,7 +281,7 @@ bool getIPowerDataLocked(vector<shared_ptr<LogEvent>>* data) { (long long)state.residencyInMsecSinceBoot, (long long)state.totalTransitions, state.supportedOnlyInSuspend ? 1 : 0); - for (auto voter : state.voters) { + for (const auto& voter : state.voters) { auto voterPtr = make_shared<LogEvent>( android::util::SUBSYSTEM_SLEEP_STATE, wallClockTimestampNs, elapsedTimestampNs); diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index 13579d283de0..7cc57c12063c 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -154,7 +154,7 @@ void CountMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, flushIfNeededLocked(dumpTimeNs); } protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked()); + protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked()); if (mPastBuckets.empty()) { diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 0425671b6367..69bafc354643 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -463,7 +463,7 @@ void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, flushIfNeededLocked(dumpTimeNs); } protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked()); + protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked()); if (mPastBuckets.empty()) { VLOG(" Duration metric, empty return"); diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index ea125d02f337..c53c4ce5e0d6 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -110,7 +110,7 @@ void EventMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, std::set<string> *str_set, ProtoOutputStream* protoOutput) { protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked()); + protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked()); if (mProto->size() <= 0) { return; } diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index 98a33f5280e6..ec602445d0ed 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -194,7 +194,7 @@ void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, } protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked()); + protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked()); if (mPastBuckets.empty()) { return; diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index ac34f4760a12..dd969c0e9c20 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -412,7 +412,7 @@ void MetricsManager::onPeriodicAlarmFired( // Returns the total byte size of all metrics managed by a single config source. size_t MetricsManager::byteSize() { size_t totalSize = 0; - for (auto metricProducer : mAllMetricProducers) { + for (const auto& metricProducer : mAllMetricProducers) { totalSize += metricProducer->byteSize(); } return totalSize; diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index 7475b53c0a84..cf56e2d43385 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -192,7 +192,7 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, flushIfNeededLocked(dumpTimeNs); } protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked()); + protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked()); if (mPastBuckets.empty() && mSkippedBuckets.empty()) { return; diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index b317361a5a3d..180a1ae07523 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -598,7 +598,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } noReportMetricIds.insert(no_report_metric); } - for (auto it : allMetricProducers) { + for (const auto& it : allMetricProducers) { uidMap.addListener(it); } return true; diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp index 59f3f0448e0e..e9c43cdbf31f 100644 --- a/cmds/statsd/src/packages/UidMap.cpp +++ b/cmds/statsd/src/packages/UidMap.cpp @@ -152,7 +152,7 @@ void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid, // listener removes itself before we call it. It's then the listener's job to handle it (expect // the callback to be called after listener is removed, and the listener should properly // ignore it). - for (auto weakPtr : broadcastList) { + for (const auto& weakPtr : broadcastList) { auto strongPtr = weakPtr.promote(); if (strongPtr != NULL) { strongPtr->onUidMapReceived(timestamp); @@ -200,7 +200,7 @@ void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const i StatsdStats::getInstance().setUidMapChanges(mChanges.size()); } - for (auto weakPtr : broadcastList) { + for (const auto& weakPtr : broadcastList) { auto strongPtr = weakPtr.promote(); if (strongPtr != NULL) { strongPtr->notifyAppUpgrade(timestamp, appName, uid, versionCode); @@ -269,7 +269,7 @@ void UidMap::removeApp(const int64_t& timestamp, const String16& app_16, const i getListenerListCopyLocked(&broadcastList); } - for (auto weakPtr : broadcastList) { + for (const auto& weakPtr : broadcastList) { auto strongPtr = weakPtr.promote(); if (strongPtr != NULL) { strongPtr->notifyAppRemoved(timestamp, app, uid); diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index 237f8b902015..d52be441f6b6 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -279,7 +279,10 @@ TEST(StatsLogProcessorTest, TestOnDumpReportEraseData) { // Dump report again. There should be no data since we erased it. processor->onDumpReport(cfgKey, 5, true, true /* DO erase data. */, ADB_DUMP, &bytes); output.ParseFromArray(bytes.data(), bytes.size()); - bool noData = (output.reports_size() == 0) || (output.reports(0).metrics_size() == 0); + // We don't care whether statsd has a report, as long as it has no count metrics in it. + bool noData = output.reports_size() == 0 + || output.reports(0).metrics_size() == 0 + || output.reports(0).metrics(0).count_metrics().data_size() == 0; EXPECT_TRUE(noData); } diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp index 79bed52f0202..960fbdab1c15 100644 --- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp +++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp @@ -71,12 +71,12 @@ bool detectAnomaliesPass(AnomalyTracker& tracker, const std::shared_ptr<DimToValMap>& currentBucket, const std::set<const MetricDimensionKey>& trueList, const std::set<const MetricDimensionKey>& falseList) { - for (MetricDimensionKey key : trueList) { + for (const MetricDimensionKey& key : trueList) { if (!tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) { return false; } } - for (MetricDimensionKey key : falseList) { + for (const MetricDimensionKey& key : falseList) { if (tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) { return false; } diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java index 8464b8df03d0..91d6490fe5a1 100644 --- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java +++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java @@ -62,7 +62,7 @@ public class Utils { if (process.waitFor() == 0) { logger.fine("Adb command successful."); } else { - logger.severe("Abnormal adb shell cmd termination for: " + String.join(",", commands)); + logger.severe("Abnormal adb shell termination for: " + String.join(",", commands)); throw new RuntimeException("Error running adb command: " + err.toString()); } } @@ -118,6 +118,52 @@ public class Utils { logger.addHandler(handler); } + /** + * Attempt to determine whether tool will work with this statsd, i.e. whether statsd is + * minCodename or higher. + * Algorithm: true if (sdk >= minSdk) || (sdk == minSdk-1 && codeName.startsWith(minCodeName)) + * If all else fails, assume it will work (letting future commands deal with any errors). + */ + public static boolean isAcceptableStatsd(Logger logger, int minSdk, String minCodename) { + BufferedReader in = null; + try { + File outFileSdk = File.createTempFile("shelltools_sdk", "tmp"); + outFileSdk.deleteOnExit(); + runCommand(outFileSdk, logger, + "adb", "shell", "getprop", "ro.build.version.sdk"); + in = new BufferedReader(new InputStreamReader(new FileInputStream(outFileSdk))); + // If NullPointerException/NumberFormatException/etc., just catch and return true. + int sdk = Integer.parseInt(in.readLine().trim()); + if (sdk >= minSdk) { + return true; + } else if (sdk == minSdk - 1) { // Could be minSdk-1, or could be minSdk development. + in.close(); + File outFileCode = File.createTempFile("shelltools_codename", "tmp"); + outFileCode.deleteOnExit(); + runCommand(outFileCode, logger, + "adb", "shell", "getprop", "ro.build.version.codename"); + in = new BufferedReader(new InputStreamReader(new FileInputStream(outFileCode))); + return in.readLine().startsWith(minCodename); + } else { + return false; + } + } catch (Exception e) { + logger.fine("Could not determine whether statsd version is compatibile " + + "with tool: " + e.toString()); + } finally { + try { + if (in != null) { + in.close(); + } + } catch (IOException e) { + logger.fine("Could not close temporary file: " + e.toString()); + } + } + // Could not determine whether statsd is acceptable version. + // Just assume it is; if it isn't, we'll just get future errors via adb and deal with them. + return true; + } + public static class LocalToolsFormatter extends Formatter { public String format(LogRecord record) { return record.getMessage() + "\n"; diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java index 67fb906c2570..2eb46605b28d 100644 --- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java +++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java @@ -37,6 +37,9 @@ import java.util.logging.Logger; public class LocalDrive { private static final boolean DEBUG = false; + public static final int MIN_SDK = 29; + public static final String MIN_CODENAME = "Q"; + public static final long DEFAULT_CONFIG_ID = 56789; public static final String BINARY_FLAG = "--binary"; @@ -46,7 +49,7 @@ public class LocalDrive { public static final String HELP_STRING = "Usage:\n\n" + - "statsd_local upload CONFIG_FILE [CONFIG_ID] [--binary]\n" + + "statsd_localdrive upload CONFIG_FILE [CONFIG_ID] [--binary]\n" + " Uploads the given statsd config file (in binary or human-readable-text format).\n" + " If a config with this id already exists, removes it first.\n" + " CONFIG_FILE Location of config file on host.\n" + @@ -56,12 +59,12 @@ public class LocalDrive { // Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID "\n" + - "statsd_local update CONFIG_FILE [CONFIG_ID] [--binary]\n" + + "statsd_localdrive update CONFIG_FILE [CONFIG_ID] [--binary]\n" + " Same as upload, but does not remove the old config first (if it already exists).\n" + // Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID "\n" + - "statsd_local get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]\n" + + "statsd_localdrive get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]\n" + " Prints the output statslog data (in binary or human-readable-text format).\n" + " CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" + " --binary Output should be in binary, instead of default human-readable text.\n" + @@ -72,13 +75,13 @@ public class LocalDrive { // --include_current_bucket --proto "\n" + - "statsd_local remove [CONFIG_ID]\n" + + "statsd_localdrive remove [CONFIG_ID]\n" + " Removes the config.\n" + " CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" + // Equivalent to: adb shell cmd stats config remove SHELL_UID CONFIG_ID "\n" + - "statsd_local clear [CONFIG_ID]\n" + + "statsd_localdrive clear [CONFIG_ID]\n" + " Clears the data associated with the config.\n" + " CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" + // Similar to: adb shell cmd stats dump-report SHELL_UID CONFIG_ID @@ -92,6 +95,12 @@ public class LocalDrive { public static void main(String[] args) { Utils.setUpLogger(sLogger, DEBUG); + if (!Utils.isAcceptableStatsd(sLogger, MIN_SDK, MIN_CODENAME)) { + sLogger.severe("LocalDrive only works with statsd versions for Android " + + MIN_CODENAME + " or higher."); + return; + } + if (args.length > 0) { switch (args[0]) { case "clear": diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 1b45d172cb89..497e193c38ec 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -6152,7 +6152,7 @@ public final class ActivityThread extends ClientTransactionHandler { try { synchronized (getGetProviderLock(auth, userId)) { holder = ActivityManager.getService().getContentProvider( - getApplicationThread(), auth, userId, stable); + getApplicationThread(), c.getOpPackageName(), auth, userId, stable); } } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 6905cb5cea73..a2784237247c 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -41,8 +41,10 @@ import android.os.UserManager; import android.provider.Settings; import android.util.ArrayMap; +import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IAppOpsActiveCallback; import com.android.internal.app.IAppOpsCallback; +import com.android.internal.app.IAppOpsNotedCallback; import com.android.internal.app.IAppOpsService; import com.android.internal.util.Preconditions; @@ -86,10 +88,20 @@ public class AppOpsManager { */ final Context mContext; + @UnsupportedAppUsage final IAppOpsService mService; - final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers = new ArrayMap<>(); - final ArrayMap<OnOpActiveChangedListener, IAppOpsActiveCallback> mActiveWatchers = + + @GuardedBy("mModeWatchers") + private final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers = + new ArrayMap<>(); + + @GuardedBy("mActiveWatchers") + private final ArrayMap<OnOpActiveChangedListener, IAppOpsActiveCallback> mActiveWatchers = + new ArrayMap<>(); + + @GuardedBy("mNotedWatchers") + private final ArrayMap<OnOpNotedListener, IAppOpsNotedCallback> mNotedWatchers = new ArrayMap<>(); static IBinder sToken; @@ -2471,6 +2483,23 @@ public class AppOpsManager { } /** + * Callback for notification of an op being noted. + * + * @hide + */ + public interface OnOpNotedListener { + /** + * Called when an op was noted. + * + * @param code The op code. + * @param uid The UID performing the operation. + * @param packageName The package performing the operation. + * @param result The result of the note. + */ + void onOpNoted(String code, int uid, String packageName, int result); + } + + /** * Callback for notification of changes to operation state. * This allows you to see the raw op codes instead of strings. * @hide @@ -2819,7 +2848,7 @@ public class AppOpsManager { */ public void stopWatchingMode(OnOpChangedListener callback) { synchronized (mModeWatchers) { - IAppOpsCallback cb = mModeWatchers.get(callback); + IAppOpsCallback cb = mModeWatchers.remove(callback); if (cb != null) { try { mService.stopWatchingMode(cb); @@ -2893,7 +2922,7 @@ public class AppOpsManager { @TestApi public void stopWatchingActive(@NonNull OnOpActiveChangedListener callback) { synchronized (mActiveWatchers) { - final IAppOpsActiveCallback cb = mActiveWatchers.get(callback); + final IAppOpsActiveCallback cb = mActiveWatchers.remove(callback); if (cb != null) { try { mService.stopWatchingActive(cb); @@ -2904,6 +2933,74 @@ public class AppOpsManager { } } + /** + * Start watching for noted app ops. An app op may be immediate or long running. + * Immediate ops are noted while long running ones are started and stopped. This + * method allows registering a listener to be notified when an app op is noted. If + * an op is being noted by any package you will get a callback. To change the + * watched ops for a registered callback you need to unregister and register it again. + * + * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission + * you can watch changes only for your UID. + * + * @param ops The ops to watch. + * @param callback Where to report changes. + * + * @see #startWatchingActive(int[], OnOpActiveChangedListener) + * @see #stopWatchingNoted(OnOpNotedListener) + * @see #noteOp(String, int, String) + * + * @hide + */ + @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true) + public void startWatchingNoted(@NonNull String[] ops, @NonNull OnOpNotedListener callback) { + IAppOpsNotedCallback cb; + synchronized (mNotedWatchers) { + cb = mNotedWatchers.get(callback); + if (cb != null) { + return; + } + cb = new IAppOpsNotedCallback.Stub() { + @Override + public void opNoted(int op, int uid, String packageName, int mode) { + callback.onOpNoted(sOpToString[op], uid, packageName, mode); + } + }; + mNotedWatchers.put(callback, cb); + } + try { + final int[] opCodes = new int[ops.length]; + for (int i = 0; i < opCodes.length; i++) { + opCodes[i] = strOpToOp(ops[i]); + } + mService.startWatchingNoted(opCodes, cb); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Stop watching for noted app ops. An app op may be immediate or long running. + * Unregistering a non-registered callback has no effect. + * + * @see #startWatchingNoted(String[], OnOpNotedListener) + * @see #noteOp(String, int, String) + * + * @hide + */ + public void stopWatchingNoted(@NonNull OnOpNotedListener callback) { + synchronized (mNotedWatchers) { + final IAppOpsNotedCallback cb = mNotedWatchers.get(callback); + if (cb != null) { + try { + mService.stopWatchingNoted(cb); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + } + private String buildSecurityExceptionMsg(int op, int uid, String packageName) { return packageName + " from uid " + uid + " not allowed to perform " + sOpNames[op]; } diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 88fb025bf406..fb519b625012 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -124,7 +124,7 @@ interface IActivityManager { int ignoreWindowingMode); void moveTaskToFront(int task, int flags, in Bundle options); int getTaskForActivity(in IBinder token, in boolean onlyRoot); - ContentProviderHolder getContentProvider(in IApplicationThread caller, + ContentProviderHolder getContentProvider(in IApplicationThread caller, in String callingPackage, in String name, int userId, boolean stable); void publishContentProviders(in IApplicationThread caller, in List<ContentProviderHolder> providers); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 566017b7372e..9d604bbd75aa 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2288,21 +2288,28 @@ public abstract class PackageManager { * {@link #hasSystemFeature}: The device has biometric hardware to detect a fingerprint. */ @SdkConstant(SdkConstantType.FEATURE) - public static final String FEATURE_FINGERPRINT = "android.hardware.fingerprint"; + public static final String FEATURE_FINGERPRINT_PRE_29 = "android.hardware.fingerprint"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device has biometric hardware to detect a fingerprint. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_FINGERPRINT = "android.hardware.biometrics.fingerprint"; /** * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device has biometric hardware to perform face authentication. */ @SdkConstant(SdkConstantType.FEATURE) - public static final String FEATURE_FACE = "android.hardware.face"; + public static final String FEATURE_FACE = "android.hardware.biometrics.face"; /** * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device has biometric hardware to perform iris authentication. */ @SdkConstant(SdkConstantType.FEATURE) - public static final String FEATURE_IRIS = "android.hardware.iris"; + public static final String FEATURE_IRIS = "android.hardware.biometrics.iris"; /** * Feature for {@link #getSystemAvailableFeatures} and diff --git a/core/java/android/net/INetdEventCallback.aidl b/core/java/android/net/INetdEventCallback.aidl index 4b1a08ded9d6..0877a1a47e2b 100644 --- a/core/java/android/net/INetdEventCallback.aidl +++ b/core/java/android/net/INetdEventCallback.aidl @@ -45,6 +45,20 @@ oneway interface INetdEventCallback { in String[] ipAddresses, int ipAddressesCount, long timestamp, int uid); /** + * Represents adding or removing a NAT64 prefix. + * This method must not block or perform long-running operations. + * + * @param netId the ID of the network the prefix was performed on. + * @param added true if the NAT64 prefix was added, or false if the NAT64 prefix was removed. + * There is only one prefix at a time for each netId. If a prefix is added, it replaces + * the previous-added prefix. + * @param prefixString the detected NAT64 prefix as a string literal. + * @param prefixLength the prefix length associated with this NAT64 prefix. + */ + void onNat64PrefixEvent(int netId, boolean added, @utf8InCpp String prefixString, + int prefixLength); + + /** * Represents a private DNS validation success or failure. * This method must not block or perform long-running operations. * diff --git a/core/java/android/net/InetAddresses.java b/core/java/android/net/InetAddresses.java new file mode 100644 index 000000000000..8e6c69a97edb --- /dev/null +++ b/core/java/android/net/InetAddresses.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import libcore.net.InetAddressUtils; + +import java.net.InetAddress; + +/** + * Utility methods for {@link InetAddress} implementations. + */ +public class InetAddresses { + + private InetAddresses() {} + + /** + * Checks to see if the {@code address} is a numeric address (such as {@code "192.0.2.1"} or + * {@code "2001:db8::1:2"}). + * + * <p>A numeric address is either an IPv4 address containing exactly 4 decimal numbers or an + * IPv6 numeric address. IPv4 addresses that consist of either hexadecimal or octal digits or + * do not have exactly 4 numbers are not treated as numeric. + * + * <p>This method will never do a DNS lookup. + * + * @param address the address to parse. + * @return true if the supplied address is numeric, false otherwise. + */ + public static boolean isNumericAddress(String address) { + return InetAddressUtils.isNumericAddress(address); + } + + /** + * Returns an InetAddress corresponding to the given numeric address (such + * as {@code "192.168.0.1"} or {@code "2001:4860:800d::68"}). + * + * <p>See {@link #isNumericAddress(String)} (String)} for a definition as to what constitutes a + * numeric address. + * + * <p>This method will never do a DNS lookup. + * + * @param address the address to parse, must be numeric. + * @return an {@link InetAddress} instance corresponding to the address. + * @throws IllegalArgumentException if {@code address} is not a numeric address. + */ + public static InetAddress parseNumericAddress(String address) { + return InetAddressUtils.parseNumericAddress(address); + } +} diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java index c431e40ede2f..4eab49cd0fdf 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/core/java/android/net/NetworkUtils.java @@ -21,6 +21,7 @@ import static android.system.OsConstants.AF_INET6; import android.annotation.NonNull; import android.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.system.Os; import android.util.Log; @@ -299,8 +300,10 @@ public class NetworkUtils { * @param addrString * @return the InetAddress * @hide + * @deprecated Use {@link InetAddresses#parseNumericAddress(String)}, if possible. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @Deprecated public static InetAddress numericToInetAddress(String addrString) throws IllegalArgumentException { return InetAddress.parseNumericAddress(addrString); diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 428d9e156e37..e84a518c4986 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -23,6 +23,7 @@ import static android.os.ParcelFileDescriptor.MODE_READ_WRITE; import static android.os.ParcelFileDescriptor.MODE_TRUNCATE; import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY; import static android.system.OsConstants.F_OK; +import static android.system.OsConstants.O_ACCMODE; import static android.system.OsConstants.O_APPEND; import static android.system.OsConstants.O_CREAT; import static android.system.OsConstants.O_RDONLY; @@ -1259,11 +1260,11 @@ public class FileUtils { int res = 0; if (mode.startsWith("rw")) { - res |= O_RDWR | O_CREAT; + res = O_RDWR | O_CREAT; } else if (mode.startsWith("w")) { - res |= O_WRONLY | O_CREAT; + res = O_WRONLY | O_CREAT; } else if (mode.startsWith("r")) { - res |= O_RDONLY; + res = O_RDONLY; } else { throw new IllegalArgumentException("Bad mode: " + mode); } @@ -1279,12 +1280,12 @@ public class FileUtils { /** {@hide} */ public static String translateModePosixToString(int mode) { String res = ""; - if ((mode & O_RDWR) == O_RDWR) { - res += "rw"; - } else if ((mode & O_WRONLY) == O_WRONLY) { - res += "w"; - } else if ((mode & O_RDONLY) == O_RDONLY) { - res += "r"; + if ((mode & O_ACCMODE) == O_RDWR) { + res = "rw"; + } else if ((mode & O_ACCMODE) == O_WRONLY) { + res = "w"; + } else if ((mode & O_ACCMODE) == O_RDONLY) { + res = "r"; } else { throw new IllegalArgumentException("Bad mode: " + mode); } @@ -1300,12 +1301,12 @@ public class FileUtils { /** {@hide} */ public static int translateModePosixToPfd(int mode) { int res = 0; - if ((mode & O_RDWR) == O_RDWR) { - res |= MODE_READ_WRITE; - } else if ((mode & O_WRONLY) == O_WRONLY) { - res |= MODE_WRITE_ONLY; - } else if ((mode & O_RDONLY) == O_RDONLY) { - res |= MODE_READ_ONLY; + if ((mode & O_ACCMODE) == O_RDWR) { + res = MODE_READ_WRITE; + } else if ((mode & O_ACCMODE) == O_WRONLY) { + res = MODE_WRITE_ONLY; + } else if ((mode & O_ACCMODE) == O_RDONLY) { + res = MODE_READ_ONLY; } else { throw new IllegalArgumentException("Bad mode: " + mode); } @@ -1325,11 +1326,11 @@ public class FileUtils { public static int translateModePfdToPosix(int mode) { int res = 0; if ((mode & MODE_READ_WRITE) == MODE_READ_WRITE) { - res |= O_RDWR; + res = O_RDWR; } else if ((mode & MODE_WRITE_ONLY) == MODE_WRITE_ONLY) { - res |= O_WRONLY; + res = O_WRONLY; } else if ((mode & MODE_READ_ONLY) == MODE_READ_ONLY) { - res |= O_RDONLY; + res = O_RDONLY; } else { throw new IllegalArgumentException("Bad mode: " + mode); } @@ -1428,4 +1429,3 @@ public class FileUtils { } } } - diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index be8cf0e9137a..fdd74882eb39 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -390,7 +390,7 @@ interface INetworkManagementService /** * Setup a new VPN. */ - void createVirtualNetwork(int netId, boolean hasDNS, boolean secure); + void createVirtualNetwork(int netId, boolean secure); /** * Remove a network. diff --git a/core/java/android/os/NativeHandle.java b/core/java/android/os/NativeHandle.java index fbecc8ec1cd9..f7ffc37f085f 100644 --- a/core/java/android/os/NativeHandle.java +++ b/core/java/android/os/NativeHandle.java @@ -16,6 +16,8 @@ package android.os; +import static android.system.OsConstants.F_DUPFD_CLOEXEC; + import android.annotation.NonNull; import android.annotation.SystemApi; import android.system.ErrnoException; @@ -108,7 +110,10 @@ public final class NativeHandle implements Closeable { FileDescriptor[] fds = new FileDescriptor[mFds.length]; try { for (int i = 0; i < mFds.length; i++) { - fds[i] = Os.dup(mFds[i]); + FileDescriptor newFd = new FileDescriptor(); + int fdint = Os.fcntlInt(mFds[i], F_DUPFD_CLOEXEC, 0); + newFd.setInt$(fdint); + fds[i] = newFd; } } catch (ErrnoException e) { e.rethrowAsIOException(); diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index 6de1ff4bc097..63912ec327a4 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -17,7 +17,11 @@ package android.os; import static android.system.OsConstants.AF_UNIX; +import static android.system.OsConstants.F_DUPFD; +import static android.system.OsConstants.F_DUPFD_CLOEXEC; +import static android.system.OsConstants.O_CLOEXEC; import static android.system.OsConstants.SEEK_SET; +import static android.system.OsConstants.SOCK_CLOEXEC; import static android.system.OsConstants.SOCK_SEQPACKET; import static android.system.OsConstants.SOCK_STREAM; import static android.system.OsConstants.S_IROTH; @@ -37,6 +41,7 @@ import android.system.StructStat; import android.util.Log; import dalvik.system.CloseGuard; +import dalvik.system.VMRuntime; import libcore.io.IoUtils; import libcore.io.Memory; @@ -293,7 +298,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { } private static FileDescriptor openInternal(File file, int mode) throws FileNotFoundException { - final int flags = FileUtils.translateModePfdToPosix(mode); + final int flags = FileUtils.translateModePfdToPosix(mode) | ifAtLeastQ(O_CLOEXEC); int realMode = S_IRWXU | S_IRWXG; if ((mode & MODE_WORLD_READABLE) != 0) realMode |= S_IROTH; @@ -315,7 +320,9 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { */ public static ParcelFileDescriptor dup(FileDescriptor orig) throws IOException { try { - final FileDescriptor fd = Os.dup(orig); + final FileDescriptor fd = new FileDescriptor(); + int intfd = Os.fcntlInt(orig, (isAtLeastQ() ? F_DUPFD_CLOEXEC : F_DUPFD), 0); + fd.setInt$(intfd); return new ParcelFileDescriptor(fd); } catch (ErrnoException e) { throw e.rethrowAsIOException(); @@ -351,7 +358,9 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { original.setInt$(fd); try { - final FileDescriptor dup = Os.dup(original); + final FileDescriptor dup = new FileDescriptor(); + int intfd = Os.fcntlInt(original, (isAtLeastQ() ? F_DUPFD_CLOEXEC : F_DUPFD), 0); + dup.setInt$(intfd); return new ParcelFileDescriptor(dup); } catch (ErrnoException e) { throw e.rethrowAsIOException(); @@ -413,7 +422,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { */ public static ParcelFileDescriptor[] createPipe() throws IOException { try { - final FileDescriptor[] fds = Os.pipe(); + final FileDescriptor[] fds = Os.pipe2(ifAtLeastQ(O_CLOEXEC)); return new ParcelFileDescriptor[] { new ParcelFileDescriptor(fds[0]), new ParcelFileDescriptor(fds[1]) }; @@ -435,7 +444,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { public static ParcelFileDescriptor[] createReliablePipe() throws IOException { try { final FileDescriptor[] comm = createCommSocketPair(); - final FileDescriptor[] fds = Os.pipe(); + final FileDescriptor[] fds = Os.pipe2(ifAtLeastQ(O_CLOEXEC)); return new ParcelFileDescriptor[] { new ParcelFileDescriptor(fds[0], comm[0]), new ParcelFileDescriptor(fds[1], comm[1]) }; @@ -459,7 +468,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { try { final FileDescriptor fd0 = new FileDescriptor(); final FileDescriptor fd1 = new FileDescriptor(); - Os.socketpair(AF_UNIX, type, 0, fd0, fd1); + Os.socketpair(AF_UNIX, type | ifAtLeastQ(SOCK_CLOEXEC), 0, fd0, fd1); return new ParcelFileDescriptor[] { new ParcelFileDescriptor(fd0), new ParcelFileDescriptor(fd1) }; @@ -489,7 +498,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { final FileDescriptor[] comm = createCommSocketPair(); final FileDescriptor fd0 = new FileDescriptor(); final FileDescriptor fd1 = new FileDescriptor(); - Os.socketpair(AF_UNIX, type, 0, fd0, fd1); + Os.socketpair(AF_UNIX, type | ifAtLeastQ(SOCK_CLOEXEC), 0, fd0, fd1); return new ParcelFileDescriptor[] { new ParcelFileDescriptor(fd0, comm[0]), new ParcelFileDescriptor(fd1, comm[1]) }; @@ -505,7 +514,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { // across multiple IO operations. final FileDescriptor comm1 = new FileDescriptor(); final FileDescriptor comm2 = new FileDescriptor(); - Os.socketpair(AF_UNIX, SOCK_SEQPACKET, 0, comm1, comm2); + Os.socketpair(AF_UNIX, SOCK_SEQPACKET | ifAtLeastQ(SOCK_CLOEXEC), 0, comm1, comm2); IoUtils.setBlocking(comm1, false); IoUtils.setBlocking(comm2, false); return new FileDescriptor[] { comm1, comm2 }; @@ -1111,4 +1120,12 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { return "{" + status + ": " + msg + "}"; } } + + private static boolean isAtLeastQ() { + return (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q); + } + + private static int ifAtLeastQ(int value) { + return isAtLeastQ() ? value : 0; + } } diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 8b1d3c6c839f..9594a713a506 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -132,6 +132,8 @@ public class StorageManager { public static final String PROP_VIRTUAL_DISK = "persist.sys.virtual_disk"; /** {@hide} */ public static final String PROP_ISOLATED_STORAGE = "persist.sys.isolated_storage"; + /** {@hide} */ + public static final String PROP_ISOLATED_STORAGE_SNAPSHOT = "sys.isolated_storage_snapshot"; /** {@hide} */ public static final String PROP_FORCE_AUDIO = "persist.fw.force_audio"; @@ -1540,7 +1542,9 @@ public class StorageManager { /** {@hide} */ @TestApi public static boolean hasIsolatedStorage() { - return SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false); + // Prefer to use snapshot for current boot when available + return SystemProperties.getBoolean(PROP_ISOLATED_STORAGE_SNAPSHOT, + SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false)); } /** diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index f38f74046934..457453fd210b 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -1002,6 +1002,7 @@ public final class MediaStore { public static final int MICRO_KIND = 3; public static final Point MINI_SIZE = new Point(512, 384); + public static final Point FULL_SCREEN_SIZE = new Point(1024, 786); public static final Point MICRO_SIZE = new Point(96, 96); } @@ -1127,6 +1128,8 @@ public final class MediaStore { final Point size; if (kind == ThumbnailConstants.MICRO_KIND) { size = ThumbnailConstants.MICRO_SIZE; + } else if (kind == ThumbnailConstants.FULL_SCREEN_KIND) { + size = ThumbnailConstants.FULL_SCREEN_SIZE; } else if (kind == ThumbnailConstants.MINI_KIND) { size = ThumbnailConstants.MINI_SIZE; } else { diff --git a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java index df58f52dc59c..028180dd3bf0 100644 --- a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java +++ b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java @@ -17,6 +17,7 @@ package android.service.contentcapture; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.content.pm.ParceledListSlice; import android.os.Parcel; import android.os.Parcelable; import android.view.contentcapture.ContentCaptureEvent; @@ -31,10 +32,10 @@ import java.util.List; @SystemApi public final class ContentCaptureEventsRequest implements Parcelable { - private final List<ContentCaptureEvent> mEvents; + private final ParceledListSlice<ContentCaptureEvent> mEvents; /** @hide */ - public ContentCaptureEventsRequest(@NonNull List<ContentCaptureEvent> events) { + public ContentCaptureEventsRequest(@NonNull ParceledListSlice<ContentCaptureEvent> events) { mEvents = events; } @@ -43,7 +44,7 @@ public final class ContentCaptureEventsRequest implements Parcelable { */ @NonNull public List<ContentCaptureEvent> getEvents() { - return mEvents; + return mEvents.getList(); } @Override @@ -53,7 +54,7 @@ public final class ContentCaptureEventsRequest implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { - parcel.writeTypedList(mEvents, flags); + parcel.writeParcelable(mEvents, flags); } public static final Parcelable.Creator<ContentCaptureEventsRequest> CREATOR = @@ -61,8 +62,7 @@ public final class ContentCaptureEventsRequest implements Parcelable { @Override public ContentCaptureEventsRequest createFromParcel(Parcel parcel) { - return new ContentCaptureEventsRequest(parcel - .createTypedArrayList(ContentCaptureEvent.CREATOR)); + return new ContentCaptureEventsRequest(parcel.readParcelable(null)); } @Override diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index 58848fce43d6..953ccf1e4575 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -24,15 +24,27 @@ import android.annotation.SystemApi; import android.app.Service; import android.content.ComponentName; import android.content.Intent; +import android.content.pm.ParceledListSlice; +import android.os.Binder; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; +import android.util.ArrayMap; import android.util.Log; +import android.util.Slog; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.ContentCaptureEvent; +import android.view.contentcapture.ContentCaptureManager; +import android.view.contentcapture.ContentCaptureSession; import android.view.contentcapture.ContentCaptureSessionId; +import android.view.contentcapture.IContentCaptureDirectManager; +import com.android.internal.os.IResultReceiver; + +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.List; import java.util.Set; @@ -63,39 +75,54 @@ public abstract class ContentCaptureService extends Service { private Handler mHandler; - private final IContentCaptureService mInterface = new IContentCaptureService.Stub() { + /** + * Binder that receives calls from the system server. + */ + private final IContentCaptureService mServerInterface = new IContentCaptureService.Stub() { @Override - public void onSessionLifecycle(ContentCaptureContext context, String sessionId) - throws RemoteException { - if (context != null) { - mHandler.sendMessage( - obtainMessage(ContentCaptureService::handleOnCreateSession, - ContentCaptureService.this, context, sessionId)); - } else { - mHandler.sendMessage( - obtainMessage(ContentCaptureService::handleOnDestroySession, - ContentCaptureService.this, sessionId)); - } + public void onSessionStarted(ContentCaptureContext context, String sessionId, int uid, + IResultReceiver clientReceiver) { + mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnCreateSession, + ContentCaptureService.this, context, sessionId, uid, clientReceiver)); } @Override - public void onContentCaptureEventsRequest(String sessionId, - ContentCaptureEventsRequest request) { + public void onActivitySnapshot(String sessionId, SnapshotData snapshotData) { mHandler.sendMessage( - obtainMessage(ContentCaptureService::handleOnContentCaptureEventsRequest, - ContentCaptureService.this, sessionId, request)); + obtainMessage(ContentCaptureService::handleOnActivitySnapshot, + ContentCaptureService.this, sessionId, snapshotData)); + } + @Override + public void onSessionFinished(String sessionId) { + mHandler.sendMessage(obtainMessage(ContentCaptureService::handleFinishSession, + ContentCaptureService.this, sessionId)); } + }; + + /** + * Binder that receives calls from the app. + */ + private final IContentCaptureDirectManager mClientInterface = + new IContentCaptureDirectManager.Stub() { @Override - public void onActivitySnapshot(String sessionId, SnapshotData snapshotData) { - mHandler.sendMessage( - obtainMessage(ContentCaptureService::handleOnActivitySnapshot, - ContentCaptureService.this, sessionId, snapshotData)); + public void sendEvents(String sessionId, + @SuppressWarnings("rawtypes") ParceledListSlice events) { + mHandler.sendMessage(obtainMessage(ContentCaptureService::handleSendEvents, + ContentCaptureService.this, sessionId, Binder.getCallingUid(), events)); } }; + /** + * List of sessions per UID. + * + * <p>This map is populated when an session is started, which is called by the system server + * and can be trusted. Then subsequent calls made by the app are verified against this map. + */ + private final ArrayMap<String, Integer> mSessionsByUid = new ArrayMap<>(); + @CallSuper @Override public void onCreate() { @@ -107,7 +134,7 @@ public abstract class ContentCaptureService extends Service { @Override public final IBinder onBind(Intent intent) { if (SERVICE_INTERFACE.equals(intent.getAction())) { - return mInterface.asBinder(); + return mServerInterface.asBinder(); } Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent); return null; @@ -196,8 +223,10 @@ public abstract class ContentCaptureService extends Service { * @param sessionId the session's Id * @param request the events */ - public abstract void onContentCaptureEventsRequest(@NonNull ContentCaptureSessionId sessionId, - @NonNull ContentCaptureEventsRequest request); + public void onContentCaptureEventsRequest(@NonNull ContentCaptureSessionId sessionId, + @NonNull ContentCaptureEventsRequest request) { + if (VERBOSE) Log.v(TAG, "onContentCaptureEventsRequest(id=" + sessionId + ")"); + } /** * Notifies the service of {@link SnapshotData snapshot data} associated with a session. @@ -212,10 +241,22 @@ public abstract class ContentCaptureService extends Service { * Destroys the content capture session. * * @param sessionId the id of the session to destroy - */ + * */ public void onDestroyContentCaptureSession(@NonNull ContentCaptureSessionId sessionId) { - if (VERBOSE) { - Log.v(TAG, "onDestroyContentCaptureSession(id=" + sessionId + ")"); + if (VERBOSE) Log.v(TAG, "onDestroyContentCaptureSession(id=" + sessionId + ")"); + } + + @Override + @CallSuper + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + final int size = mSessionsByUid.size(); + pw.print("Number sessions: "); pw.println(size); + if (size > 0) { + final String prefix = " "; + for (int i = 0; i < size; i++) { + pw.print(prefix); pw.print(mSessionsByUid.keyAt(i)); + pw.print(": uid="); pw.println(mSessionsByUid.valueAt(i)); + } } } @@ -223,13 +264,19 @@ public abstract class ContentCaptureService extends Service { // so we don't need to create a temporary InteractionSessionId for each event. private void handleOnCreateSession(@NonNull ContentCaptureContext context, - @NonNull String sessionId) { + @NonNull String sessionId, int uid, IResultReceiver clientReceiver) { + mSessionsByUid.put(sessionId, uid); onCreateContentCaptureSession(context, new ContentCaptureSessionId(sessionId)); + setClientState(clientReceiver, ContentCaptureSession.STATE_ACTIVE, + mClientInterface.asBinder()); } - private void handleOnContentCaptureEventsRequest(@NonNull String sessionId, - @NonNull ContentCaptureEventsRequest request) { - onContentCaptureEventsRequest(new ContentCaptureSessionId(sessionId), request); + private void handleSendEvents(@NonNull String sessionId, int uid, + @NonNull ParceledListSlice<ContentCaptureEvent> events) { + if (handleIsRightCallerFor(sessionId, uid)) { + onContentCaptureEventsRequest(new ContentCaptureSessionId(sessionId), + new ContentCaptureEventsRequest(events)); + } } private void handleOnActivitySnapshot(@NonNull String sessionId, @@ -237,7 +284,52 @@ public abstract class ContentCaptureService extends Service { onActivitySnapshot(new ContentCaptureSessionId(sessionId), snapshotData); } - private void handleOnDestroySession(@NonNull String sessionId) { + private void handleFinishSession(@NonNull String sessionId) { + mSessionsByUid.remove(sessionId); onDestroyContentCaptureSession(new ContentCaptureSessionId(sessionId)); } + + /** + * Checks if the given {@code uid} owns the session. + */ + private boolean handleIsRightCallerFor(@NonNull String sessionId, int uid) { + final Integer rightUid = mSessionsByUid.get(sessionId); + if (rightUid == null) { + if (VERBOSE) Log.v(TAG, "No session for " + sessionId); + // Just ignore, as the session could have finished + return false; + } + if (rightUid != uid) { + Log.e(TAG, "invalid call from UID " + uid + ": session " + sessionId + " belongs to " + + rightUid); + //TODO(b/111276913): log metrics as this could be a malicious app forging a sessionId + return false; + } + return true; + + } + + /** + * Sends the state of the {@link ContentCaptureManager} in the cleint app. + * + * @param clientReceiver receiver in the client app. + * @param binder handle to the {@code IContentCaptureDirectManager} object that resides in the + * service. + * @hide + */ + public static void setClientState(@NonNull IResultReceiver clientReceiver, + int sessionStatus, @Nullable IBinder binder) { + try { + final Bundle extras; + if (binder != null) { + extras = new Bundle(); + extras.putBinder(ContentCaptureSession.EXTRA_BINDER, binder); + } else { + extras = null; + } + clientReceiver.send(sessionStatus, extras); + } catch (RemoteException e) { + Slog.w(TAG, "Error async reporting result to client: " + e); + } + } } diff --git a/core/java/android/service/contentcapture/IContentCaptureService.aidl b/core/java/android/service/contentcapture/IContentCaptureService.aidl index 8167be9e6838..20e8e99d4ce1 100644 --- a/core/java/android/service/contentcapture/IContentCaptureService.aidl +++ b/core/java/android/service/contentcapture/IContentCaptureService.aidl @@ -16,10 +16,11 @@ package android.service.contentcapture; -import android.service.contentcapture.ContentCaptureEventsRequest; import android.service.contentcapture.SnapshotData; import android.view.contentcapture.ContentCaptureContext; +import com.android.internal.os.IResultReceiver; + import java.util.List; /** @@ -28,11 +29,8 @@ import java.util.List; * @hide */ oneway interface IContentCaptureService { - - // Called when session is created (context not null) or destroyed (context null) - void onSessionLifecycle(in ContentCaptureContext context, String sessionId); - - void onContentCaptureEventsRequest(String sessionId, in ContentCaptureEventsRequest request); - + void onSessionStarted(in ContentCaptureContext context, String sessionId, int uid, + in IResultReceiver clientReceiver); + void onSessionFinished(String sessionId); void onActivitySnapshot(String sessionId, in SnapshotData snapshotData); } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index a095b0d8b239..f295b705e4e6 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -25,6 +25,7 @@ import android.app.Service; import android.app.WallpaperColors; import android.app.WallpaperInfo; import android.app.WallpaperManager; +import android.content.Context; import android.content.Intent; import android.content.res.TypedArray; import android.graphics.Bitmap; @@ -52,12 +53,12 @@ import android.view.InputChannel; import android.view.InputDevice; import android.view.InputEvent; import android.view.InputEventReceiver; +import android.view.InsetsState; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.View; import android.view.ViewGroup; import android.view.WindowInsets; -import android.view.InsetsState; import android.view.WindowManager; import android.view.WindowManagerGlobal; @@ -211,7 +212,8 @@ public abstract class WallpaperService extends Service { private final Supplier<Long> mClockFunction; private final Handler mHandler; - Display mDisplay; + private Display mDisplay; + private Context mDisplayContext; private int mDisplayState; final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() { @@ -1038,6 +1040,7 @@ public abstract class WallpaperService extends Service { mIWallpaperEngine.mDisplayManager.registerDisplayListener(mDisplayListener, mCaller.getHandler()); mDisplay = mIWallpaperEngine.mDisplay; + mDisplayContext = createDisplayContext(mDisplay); mDisplayState = mDisplay.getState(); if (DEBUG) Log.v(TAG, "onCreate(): " + this); @@ -1050,6 +1053,18 @@ public abstract class WallpaperService extends Service { } /** + * The {@link Context} with resources that match the current display the wallpaper is on. + * For multiple display environment, multiple engines can be created to render on each + * display, but these displays may have different densities. Use this context to get the + * corresponding resources for currently display, avoiding the context of the service. + * + * @return A {@link Context} for current display. + */ + public Context getDisplayContext() { + return mDisplayContext; + } + + /** * Executes life cycle event and updates internal ambient mode state based on * message sent from handler. * diff --git a/core/java/android/transition/Scene.java b/core/java/android/transition/Scene.java index b1fc17a4ecd1..8d4db54ab7b1 100644 --- a/core/java/android/transition/Scene.java +++ b/core/java/android/transition/Scene.java @@ -96,7 +96,7 @@ public final class Scene { * the hierarchy specified by the layoutId resource file. * * <p>This method is hidden because layoutId-based scenes should be - * created by the caching factory method {@link Scene#getCurrentScene(View)}.</p> + * created by the caching factory method {@link Scene#getCurrentScene(ViewGroup)}.</p> * * @param sceneRoot The root of the hierarchy in which scene changes * and transitions will take place. @@ -194,28 +194,28 @@ public final class Scene { } /** - * Set the scene that the given view is in. The current scene is set only - * on the root view of a scene, not for every view in that hierarchy. This + * Set the scene that the given ViewGroup is in. The current scene is set only + * on the root ViewGroup of a scene, not for every view in that hierarchy. This * information is used by Scene to determine whether there is a previous * scene which should be exited before the new scene is entered. * - * @param sceneRoot The view on which the current scene is being set + * @param sceneRoot The ViewGroup on which the current scene is being set */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) - static void setCurrentScene(@NonNull View sceneRoot, @Nullable Scene scene) { + static void setCurrentScene(@NonNull ViewGroup sceneRoot, @Nullable Scene scene) { sceneRoot.setTagInternal(com.android.internal.R.id.current_scene, scene); } /** - * Gets the current {@link Scene} set on the given view. A scene is set on a view - * only if that view is the scene root. + * Gets the current {@link Scene} set on the given ViewGroup. A scene is set on a ViewGroup + * only if that ViewGroup is the scene root. * - * @param sceneRoot The view on which the current scene will be returned - * @return The current Scene set on this view. A value of null indicates that + * @param sceneRoot The ViewGroup on which the current scene will be returned + * @return The current Scene set on this ViewGroup. A value of null indicates that * no Scene is currently set. */ @Nullable - public static Scene getCurrentScene(@NonNull View sceneRoot) { + public static Scene getCurrentScene(@NonNull ViewGroup sceneRoot) { return (Scene) sceneRoot.getTag(com.android.internal.R.id.current_scene); } diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index ade7577dc666..8b97e0e4a107 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -46,6 +46,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put("settings_mobile_network_v2", "true"); DEFAULT_FLAGS.put("settings_network_and_internet_v2", "false"); DEFAULT_FLAGS.put("settings_seamless_transfer", "false"); + DEFAULT_FLAGS.put("settings_slice_injection", "false"); DEFAULT_FLAGS.put("settings_systemui_theme", "true"); DEFAULT_FLAGS.put("settings_wifi_dpp", "false"); DEFAULT_FLAGS.put("settings_wifi_mac_randomization", "false"); diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java index ada78532d63f..60eeeeaa77ff 100644 --- a/core/java/android/view/NotificationHeaderView.java +++ b/core/java/android/view/NotificationHeaderView.java @@ -201,22 +201,16 @@ public class NotificationHeaderView extends ViewGroup { int bottom = top + childHeight; int layoutLeft = left; int layoutRight = right; - if (child == mExpandButton && mShowExpandButtonAtEnd) { - layoutRight = end - mContentEndMargin; - end = layoutLeft = layoutRight - child.getMeasuredWidth(); - } - if (child == mProfileBadge) { - int paddingEnd = getPaddingEnd(); - if (mShowWorkBadgeAtEnd) { - paddingEnd = mContentEndMargin; + if ((child == mExpandButton && mShowExpandButtonAtEnd) + || child == mProfileBadge + || child == mAppOps) { + if (end == getMeasuredWidth()) { + layoutRight = end - mContentEndMargin; + } else { + layoutRight = end - params.getMarginEnd(); } - layoutRight = end - paddingEnd; - end = layoutLeft = layoutRight - child.getMeasuredWidth(); - } - if (child == mAppOps) { - int paddingEnd = mContentEndMargin; - layoutRight = end - paddingEnd; - end = layoutLeft = layoutRight - child.getMeasuredWidth(); + layoutLeft = layoutRight - child.getMeasuredWidth(); + end = layoutLeft - params.getMarginStart(); } if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) { int ltrLeft = layoutLeft; diff --git a/core/java/android/view/TouchDelegate.java b/core/java/android/view/TouchDelegate.java index bef9f07afb5f..2ea95e90a8bf 100644 --- a/core/java/android/view/TouchDelegate.java +++ b/core/java/android/view/TouchDelegate.java @@ -157,6 +157,11 @@ public class TouchDelegate { * Forward hover events to the delegate view if the event is within the bounds * specified in the constructor and touch exploration is enabled. * + * <p>This method is provided for accessibility purposes so touch exploration, which is + * commonly used by screen readers, can properly place accessibility focus on views that + * use touch delegates. Therefore, touch exploration must be enabled for hover events + * to be dispatched through the delegate.</p> + * * @param event The hover event to forward * @return True if the event was consumed by the delegate, false otherwise. * diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index 632955d335cf..f411cf751b47 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -27,9 +27,11 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; +import android.content.pm.ParceledListSlice; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.IBinder.DeathRecipient; import android.os.RemoteException; import android.os.SystemClock; import android.util.Log; @@ -45,6 +47,8 @@ import dalvik.system.CloseGuard; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; @@ -100,6 +104,13 @@ public final class ContentCaptureSession implements AutoCloseable { public static final int STATE_DISABLED = 3; /** + * Session is disabled because its id already existed on server. + * + * @hide + */ + public static final int STATE_DISABLED_DUPLICATED_ID = 4; + + /** * Handler message used to flush the buffer. */ private static final int MSG_FLUSH = 1; @@ -116,6 +127,13 @@ public final class ContentCaptureSession implements AutoCloseable { // TODO(b/121044064): use settings private static final int FLUSHING_FREQUENCY_MS = 5_000; + + /** + * Name of the {@link IResultReceiver} extra used to pass the binder interface to the service. + * @hide + */ + public static final String EXTRA_BINDER = "binder"; + private final CloseGuard mCloseGuard = CloseGuard.get(); @NonNull @@ -127,8 +145,21 @@ public final class ContentCaptureSession implements AutoCloseable { @NonNull private final Handler mHandler; + /** + * Interface to the system_server binder object - it's only used to start the session (and + * notify when the session is finished). + */ @Nullable - private final IContentCaptureManager mService; + private final IContentCaptureManager mSystemServerInterface; + + /** + * Direct interface to the service binder object - it's used to send the events, including the + * last ones (when the session is finished) + */ + @Nullable + private IContentCaptureDirectManager mDirectServiceInterface; + @Nullable + private DeathRecipient mDirectServiceVulture; @Nullable private final String mId = UUID.randomUUID().toString(); @@ -165,11 +196,11 @@ public final class ContentCaptureSession implements AutoCloseable { /** @hide */ protected ContentCaptureSession(@NonNull Context context, @NonNull Handler handler, - @Nullable IContentCaptureManager service, @NonNull AtomicBoolean disabled, + @Nullable IContentCaptureManager systemServerInterface, @NonNull AtomicBoolean disabled, @Nullable ContentCaptureContext clientContext) { mContext = context; mHandler = handler; - mService = service; + mSystemServerInterface = systemServerInterface; mDisabled = disabled; mClientContext = clientContext; mCloseGuard.open("destroy"); @@ -227,6 +258,8 @@ public final class ContentCaptureSession implements AutoCloseable { Log.v(TAG, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId); } + flush(); + mHandler.sendMessage(obtainMessage(ContentCaptureSession::handleDestroySession, this)); mCloseGuard.close(); } @@ -267,11 +300,20 @@ public final class ContentCaptureSession implements AutoCloseable { final int flags = 0; // TODO(b/111276913): get proper flags try { - mService.startSession(mContext.getUserId(), mApplicationToken, componentName, - mId, mClientContext, flags, new IResultReceiver.Stub() { + mSystemServerInterface.startSession(mContext.getUserId(), mApplicationToken, + componentName, mId, mClientContext, flags, new IResultReceiver.Stub() { @Override public void send(int resultCode, Bundle resultData) { - handleSessionStarted(resultCode); + IBinder binder = null; + if (resultData != null) { + binder = resultData.getBinder(EXTRA_BINDER); + if (binder == null) { + Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result"); + handleResetState(); + return; + } + } + handleSessionStarted(resultCode, binder); } }); } catch (RemoteException e) { @@ -280,12 +322,38 @@ public final class ContentCaptureSession implements AutoCloseable { } } - private void handleSessionStarted(int resultCode) { + /** + * Callback from {@code system_server} after call to + * {@link IContentCaptureManager#startSession(int, IBinder, ComponentName, String, + * ContentCaptureContext, int, IResultReceiver)}. + * + * @param resultCode session state + * @param binder handle to {@link IContentCaptureDirectManager} + */ + private void handleSessionStarted(int resultCode, @Nullable IBinder binder) { mState = resultCode; - mDisabled.set(mState == STATE_DISABLED); + if (binder != null) { + mDirectServiceInterface = IContentCaptureDirectManager.Stub.asInterface(binder); + mDirectServiceVulture = () -> { + Log.w(TAG, "Destroying session " + mId + " because service died"); + destroy(); + }; + try { + binder.linkToDeath(mDirectServiceVulture, 0); + } catch (RemoteException e) { + Log.w(TAG, "Failed to link to death on " + binder + ": " + e); + } + } + if (resultCode == STATE_DISABLED || resultCode == STATE_DISABLED_DUPLICATED_ID) { + mDisabled.set(true); + handleResetSession(/* resetState= */ false); + } else { + mDisabled.set(false); + } if (VERBOSE) { Log.v(TAG, "handleSessionStarted() result: code=" + resultCode + ", id=" + mId - + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get()); + + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get() + + ", binder=" + binder); } } @@ -307,7 +375,7 @@ public final class ContentCaptureSession implements AutoCloseable { final boolean bufferEvent = numberEvents < MAX_BUFFER_SIZE; if (bufferEvent && !forceFlush) { - handleScheduleFlush(); + handleScheduleFlush(/* checkExisting= */ true); return; } @@ -331,8 +399,8 @@ public final class ContentCaptureSession implements AutoCloseable { handleForceFlush(); } - private void handleScheduleFlush() { - if (mHandler.hasMessages(MSG_FLUSH)) { + private void handleScheduleFlush(boolean checkExisting) { + if (checkExisting && mHandler.hasMessages(MSG_FLUSH)) { // "Renew" the flush message by removing the previous one mHandler.removeMessages(MSG_FLUSH); } @@ -356,52 +424,80 @@ public final class ContentCaptureSession implements AutoCloseable { private void handleForceFlush() { if (mEvents == null) return; + if (mDirectServiceInterface == null) { + Log.w(TAG, "handleForceFlush(): client not available yet"); + if (!mHandler.hasMessages(MSG_FLUSH)) { + handleScheduleFlush(/* checkExisting= */ false); + } + return; + } + final int numberEvents = mEvents.size(); try { if (DEBUG) { Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getActivityDebugName()); } mHandler.removeMessages(MSG_FLUSH); - mService.sendEvents(mContext.getUserId(), mId, mEvents); - // TODO(b/111276913): decide whether we should clear or set it to null, as each has - // its own advantages: clearing will save extra allocations while the session is - // active, while setting to null would save memory if there's no more event coming. - mEvents.clear(); + + final ParceledListSlice<ContentCaptureEvent> events = handleClearEvents(); + mDirectServiceInterface.sendEvents(mId, events); } catch (RemoteException e) { Log.w(TAG, "Error sending " + numberEvents + " for " + getActivityDebugName() + ": " + e); } } + /** + * Resets the buffer and return a {@link ParceledListSlice} with the previous events. + */ + @NonNull + private ParceledListSlice<ContentCaptureEvent> handleClearEvents() { + // NOTE: we must save a reference to the current mEvents and then set it to to null, + // otherwise clearing it would clear it in the receiving side if the service is also local. + final List<ContentCaptureEvent> events = mEvents == null + ? Collections.emptyList() + : mEvents; + mEvents = null; + return new ParceledListSlice<>(events); + } + private void handleDestroySession() { //TODO(b/111276913): right now both the ContentEvents and lifecycle sessions are sent // to system_server, so it's ok to call both in sequence here. But once we split // them so the events are sent directly to the service, we need to make sure they're // sent in order. - try { - if (DEBUG) { - Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with " - + (mEvents == null ? 0 : mEvents.size()) + " event(s) for " - + getActivityDebugName()); - } + if (DEBUG) { + Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with " + + (mEvents == null ? 0 : mEvents.size()) + " event(s) for " + + getActivityDebugName()); + } - mService.finishSession(mContext.getUserId(), mId, mEvents); + try { + mSystemServerInterface.finishSession(mContext.getUserId(), mId); } catch (RemoteException e) { - Log.e(TAG, "Error destroying session " + mId + " for " + getActivityDebugName() - + ": " + e); - } finally { - handleResetState(); + Log.e(TAG, "Error destroying system-service session " + mId + " for " + + getActivityDebugName() + ": " + e); } } + private void handleResetState() { + handleResetSession(/* resetState= */ true); + } + // TODO(b/111276913): once we support multiple sessions, we might need to move some of these // clearings out. - private void handleResetState() { - mState = STATE_UNKNOWN; + private void handleResetSession(boolean resetState) { + if (resetState) { + mState = STATE_UNKNOWN; + } mContentCaptureSessionId = null; mApplicationToken = null; mComponentName = null; mEvents = null; + if (mDirectServiceInterface != null) { + mDirectServiceInterface.asBinder().unlinkToDeath(mDirectServiceVulture, 0); + } + mDirectServiceInterface = null; mHandler.removeMessages(MSG_FLUSH); } @@ -492,15 +588,20 @@ public final class ContentCaptureSession implements AutoCloseable { } private boolean isContentCaptureEnabled() { - return mService != null && !mDisabled.get(); + return mSystemServerInterface != null && !mDisabled.get(); } void dump(@NonNull String prefix, @NonNull PrintWriter pw) { pw.print(prefix); pw.print("id: "); pw.println(mId); pw.print(prefix); pw.print("mContext: "); pw.println(mContext); pw.print(prefix); pw.print("user: "); pw.println(mContext.getUserId()); - if (mService != null) { - pw.print(prefix); pw.print("mService: "); pw.println(mService); + if (mSystemServerInterface != null) { + pw.print(prefix); pw.print("mSystemServerInterface: "); + pw.println(mSystemServerInterface); + } + if (mDirectServiceInterface != null) { + pw.print(prefix); pw.print("mDirectServiceInterface: "); + pw.println(mDirectServiceInterface); } if (mClientContext != null) { // NOTE: we don't dump clientContent because it could have PII @@ -563,6 +664,8 @@ public final class ContentCaptureSession implements AutoCloseable { return "ACTIVE"; case STATE_DISABLED: return "DISABLED"; + case STATE_DISABLED_DUPLICATED_ID: + return "DISABLED_DUPLICATED_ID"; default: return "INVALID:" + state; } diff --git a/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl b/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl new file mode 100644 index 000000000000..145fc16707a1 --- /dev/null +++ b/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.contentcapture; + +import android.content.pm.ParceledListSlice; +import android.view.contentcapture.ContentCaptureEvent; + +/** + * Interface between an app (ContentCaptureManager / ContentCaptureSession) and the app providing + * the ContentCaptureService implementation. + * + * @hide + */ +oneway interface IContentCaptureDirectManager { + void sendEvents(in String sessionId, in ParceledListSlice events); +} diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl index 2002c5c1fc9a..cbd37017038e 100644 --- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl +++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl @@ -26,12 +26,14 @@ import com.android.internal.os.IResultReceiver; import java.util.List; /** - * {@hide} - */ + * Interface between an app (ContentCaptureManager / ContentCaptureSession) and the system-server + * implementation service (ContentCaptureManagerService). + * + * @hide + */ oneway interface IContentCaptureManager { void startSession(int userId, IBinder activityToken, in ComponentName componentName, String sessionId, in ContentCaptureContext clientContext, int flags, in IResultReceiver result); - void finishSession(int userId, String sessionId, in List<ContentCaptureEvent> events); - void sendEvents(int userId, in String sessionId, in List<ContentCaptureEvent> events); + void finishSession(int userId, String sessionId); } diff --git a/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl b/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl new file mode 100644 index 000000000000..fa5c30a03e78 --- /dev/null +++ b/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +// Iterface to observe op note/checks of ops +oneway interface IAppOpsNotedCallback { + void opNoted(int op, int uid, String packageName, int mode); +} diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index 049103bfebb2..e571656b9135 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -21,6 +21,7 @@ import android.content.pm.ParceledListSlice; import android.os.Bundle; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsActiveCallback; +import com.android.internal.app.IAppOpsNotedCallback; interface IAppOpsService { // These first methods are also called by native code, so must @@ -61,4 +62,7 @@ interface IAppOpsService { boolean isOperationActive(int code, int uid, String packageName); void startWatchingModeWithFlags(int op, String packageName, int flags, IAppOpsCallback callback); + + void startWatchingNoted(in int[] ops, IAppOpsNotedCallback callback); + void stopWatchingNoted(IAppOpsNotedCallback callback); } diff --git a/core/java/com/android/internal/app/procstats/AssociationState.java b/core/java/com/android/internal/app/procstats/AssociationState.java index 4da339165655..2df515835026 100644 --- a/core/java/com/android/internal/app/procstats/AssociationState.java +++ b/core/java/com/android/internal/app/procstats/AssociationState.java @@ -17,6 +17,7 @@ package com.android.internal.app.procstats; +import android.annotation.Nullable; import android.os.Parcel; import android.os.SystemClock; import android.os.UserHandle; @@ -192,9 +193,16 @@ public final class AssociationState { */ String mProcess; - SourceKey(int uid, String process) { + /** + * Optional package name, or null; consider this final. Not final just to avoid a + * temporary object during lookup. + */ + @Nullable String mPackage; + + SourceKey(int uid, String process, String pkg) { mUid = uid; mProcess = process; + mPackage = pkg; } public boolean equals(Object o) { @@ -202,12 +210,14 @@ public final class AssociationState { return false; } SourceKey s = (SourceKey) o; - return s.mUid == mUid && Objects.equals(s.mProcess, mProcess); + return s.mUid == mUid && Objects.equals(s.mProcess, mProcess) + && Objects.equals(s.mPackage, mPackage); } @Override public int hashCode() { - return Integer.hashCode(mUid) ^ (mProcess == null ? 0 : mProcess.hashCode()); + return Integer.hashCode(mUid) ^ (mProcess == null ? 0 : mProcess.hashCode()) + ^ (mPackage == null ? 0 : (mPackage.hashCode() * 33)); } @Override @@ -217,6 +227,8 @@ public final class AssociationState { UserHandle.formatUid(sb, mUid); sb.append(' '); sb.append(mProcess); + sb.append(' '); + sb.append(mPackage); sb.append('}'); return sb.toString(); } @@ -227,7 +239,7 @@ public final class AssociationState { */ private final ArrayMap<SourceKey, SourceState> mSources = new ArrayMap<>(); - private final SourceKey mTmpSourceKey = new SourceKey(0, null); + private final SourceKey mTmpSourceKey = new SourceKey(0, null, null); private ProcessState mProc; @@ -266,12 +278,13 @@ public final class AssociationState { mProc = proc; } - public SourceState startSource(int uid, String processName) { + public SourceState startSource(int uid, String processName, String packageName) { mTmpSourceKey.mUid = uid; mTmpSourceKey.mProcess = processName; + mTmpSourceKey.mPackage = packageName; SourceState src = mSources.get(mTmpSourceKey); if (src == null) { - SourceKey key = new SourceKey(uid, processName); + SourceKey key = new SourceKey(uid, processName, packageName); src = new SourceState(key); mSources.put(key, src); } @@ -379,6 +392,7 @@ public final class AssociationState { final SourceState src = mSources.valueAt(isrc); out.writeInt(key.mUid); stats.writeCommonString(out, key.mProcess); + stats.writeCommonString(out, key.mPackage); out.writeInt(src.mCount); out.writeLong(src.mDuration); out.writeInt(src.mActiveCount); @@ -405,7 +419,8 @@ public final class AssociationState { for (int isrc = 0; isrc < NSRC; isrc++) { final int uid = in.readInt(); final String procName = stats.readCommonString(in, parcelVersion); - final SourceKey key = new SourceKey(uid, procName); + final String pkgName = stats.readCommonString(in, parcelVersion); + final SourceKey key = new SourceKey(uid, procName, pkgName); final SourceState src = new SourceState(key); src.mCount = in.readInt(); src.mDuration = in.readLong(); @@ -445,10 +460,11 @@ public final class AssociationState { } } - public boolean hasProcess(String procName) { + public boolean hasProcessOrPackage(String procName) { final int NSRC = mSources.size(); for (int isrc = 0; isrc < NSRC; isrc++) { - if (mSources.keyAt(isrc).mProcess.equals(procName)) { + final SourceKey key = mSources.keyAt(isrc); + if (procName.equals(key.mProcess) || procName.equals(key.mPackage)) { return true; } } @@ -466,14 +482,20 @@ public final class AssociationState { for (int isrc = 0; isrc < NSRC; isrc++) { final SourceKey key = mSources.keyAt(isrc); final SourceState src = mSources.valueAt(isrc); - if (reqPackage != null && !reqPackage.equals(key.mProcess)) { + if (reqPackage != null && !reqPackage.equals(key.mProcess) + && !reqPackage.equals(key.mPackage)) { continue; } pw.print(prefixInner); pw.print("<- "); pw.print(key.mProcess); - pw.print(" / "); + pw.print("/"); UserHandle.formatUid(pw, key.mUid); + if (key.mPackage != null) { + pw.print(" ("); + pw.print(key.mPackage); + pw.print(")"); + } pw.println(":"); pw.print(prefixInner); pw.print(" Total count "); @@ -683,6 +705,7 @@ public final class AssociationState { final SourceState src = mSources.valueAt(isrc); final long sourceToken = proto.start(PackageAssociationProcessStatsProto.SOURCES); proto.write(PackageAssociationSourceProcessStatsProto.PROCESS_NAME, key.mProcess); + proto.write(PackageAssociationSourceProcessStatsProto.PACKAGE_NAME, key.mPackage); proto.write(PackageAssociationSourceProcessStatsProto.PROCESS_UID, key.mUid); proto.write(PackageAssociationSourceProcessStatsProto.TOTAL_COUNT, src.mCount); long duration = src.mDuration; diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java index 9ee583a97b8d..9b9b77196a53 100644 --- a/core/java/com/android/internal/app/procstats/ProcessStats.java +++ b/core/java/com/android/internal/app/procstats/ProcessStats.java @@ -178,7 +178,7 @@ public final class ProcessStats implements Parcelable { {"proc", "pkg-proc", "pkg-svc", "pkg-asc", "pkg-all", "all"}; // Current version of the parcel format. - private static final int PARCEL_VERSION = 34; + private static final int PARCEL_VERSION = 35; // In-memory Parcel magic number, used to detect attempts to unmarshall bad data private static final int MAGIC = 0x50535454; @@ -1490,7 +1490,7 @@ public final class ProcessStats implements Parcelable { // package, so that if so we print those. for (int iasc = 0; iasc < NASCS; iasc++) { AssociationState asc = pkgState.mAssociations.valueAt(iasc); - if (asc.hasProcess(reqPackage)) { + if (asc.hasProcessOrPackage(reqPackage)) { onlyAssociations = true; break; } @@ -1591,7 +1591,7 @@ public final class ProcessStats implements Parcelable { for (int iasc = 0; iasc < NASCS; iasc++) { AssociationState asc = pkgState.mAssociations.valueAt(iasc); if (!pkgMatch && !reqPackage.equals(asc.getProcessName())) { - if (!onlyAssociations || !asc.hasProcess(reqPackage)) { + if (!onlyAssociations || !asc.hasProcessOrPackage(reqPackage)) { continue; } } diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index 5118e5f0473e..9087dd219d97 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -67,7 +67,8 @@ interface IStatusBarService in NotificationVisibility[] noLongerVisibleKeys); void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded); void onNotificationDirectReplied(String key); - void onNotificationSmartRepliesAdded(in String key, in int replyCount); + void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount, int smartActionCount, + boolean generatedByAsssistant); void onNotificationSmartReplySent(in String key, in int replyIndex, in CharSequence reply, boolean generatedByAssistant); void onNotificationSettingsViewed(String key); void setSystemUiVisibility(int displayId, int vis, int mask, String cause); diff --git a/core/java/com/android/server/net/BaseNetdEventCallback.java b/core/java/com/android/server/net/BaseNetdEventCallback.java index 97247aada49d..a65214a1edca 100644 --- a/core/java/com/android/server/net/BaseNetdEventCallback.java +++ b/core/java/com/android/server/net/BaseNetdEventCallback.java @@ -32,6 +32,12 @@ public class BaseNetdEventCallback extends INetdEventCallback.Stub { } @Override + public void onNat64PrefixEvent(int netId, boolean added, String prefixString, + int prefixLength) { + // default no-op + } + + @Override public void onPrivateDnsValidationEvent(int netId, String ipAddress, String hostname, boolean validated) { // default no-op diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 43f8d00bb5f1..21fa75eb35e0 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -274,6 +274,7 @@ cc_library_shared { "libicuuc", "libmedia", "libmediametrics", + "libmeminfo", "libaudioclient", "libjpeg", "libusbhost", diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index c32de0a5737e..12ca78a7ce92 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -725,9 +725,10 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { return NULL; } - // Map the pixels in place and take ownership of the ashmem region. - nativeBitmap = sk_sp<Bitmap>(GraphicsJNI::mapAshmemBitmap(env, bitmap.get(), - dupFd, const_cast<void*>(blob.data()), size, !isMutable)); + // Map the pixels in place and take ownership of the ashmem region. We must also respect the + // rowBytes value already set on the bitmap instead of attempting to compute our own. + nativeBitmap = Bitmap::createFrom(bitmap->info(), bitmap->rowBytes(), dupFd, + const_cast<void*>(blob.data()), size, !isMutable); if (!nativeBitmap) { close(dupFd); blob.release(); @@ -1097,21 +1098,20 @@ static jobject Bitmap_copyPreserveInternalConfig(JNIEnv* env, jobject, jlong bit SkBitmap src; hwuiBitmap.getSkBitmap(&src); - SkBitmap result; - HeapAllocator allocator; - if (!bitmapCopyTo(&result, hwuiBitmap.info().colorType(), src, &allocator)) { + if (src.pixelRef() == nullptr) { doThrowRE(env, "Could not copy a hardware bitmap."); return NULL; } - return createBitmap(env, allocator.getStorageObjAndReset(), getPremulBitmapCreateFlags(false)); + + sk_sp<Bitmap> bitmap = Bitmap::createFrom(src.info(), *src.pixelRef()); + return createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(false)); } static jobject Bitmap_createHardwareBitmap(JNIEnv* env, jobject, jobject graphicBuffer) { sp<GraphicBuffer> buffer(graphicBufferForJavaObject(env, graphicBuffer)); - // Bitmap::createFrom currently assumes SRGB color space for RGBA images. // To support any color space, we need to pass an additional ColorSpace argument to // java Bitmap.createHardwareBitmap. - sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer); + sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, SkColorSpace::MakeSRGB()); if (!bitmap.get()) { ALOGW("failed to create hardware bitmap from graphic buffer"); return NULL; diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index 26af15e79e2d..67d0c8aced61 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -424,36 +424,6 @@ jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region) /////////////////////////////////////////////////////////////////////////////// -android::Bitmap* GraphicsJNI::mapAshmemBitmap(JNIEnv* env, SkBitmap* bitmap, - int fd, void* addr, size_t size, bool readOnly) { - const SkImageInfo& info = bitmap->info(); - if (info.colorType() == kUnknown_SkColorType) { - doThrowIAE(env, "unknown bitmap configuration"); - return nullptr; - } - - if (!addr) { - // Map existing ashmem region if not already mapped. - int flags = readOnly ? (PROT_READ) : (PROT_READ | PROT_WRITE); - size = ashmem_get_size_region(fd); - addr = mmap(NULL, size, flags, MAP_SHARED, fd, 0); - if (addr == MAP_FAILED) { - return nullptr; - } - } - - // we must respect the rowBytes value already set on the bitmap instead of - // attempting to compute our own. - const size_t rowBytes = bitmap->rowBytes(); - - auto wrapper = new android::Bitmap(addr, fd, size, info, rowBytes); - wrapper->getSkBitmap(bitmap); - if (readOnly) { - bitmap->pixelRef()->setImmutable(); - } - return wrapper; -} - SkColorSpaceTransferFn GraphicsJNI::getNativeTransferParameters(JNIEnv* env, jobject transferParams) { SkColorSpaceTransferFn p; p.fA = (float) env->GetDoubleField(transferParams, gTransferParams_aFieldID); diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index cee3c46dd67f..b0bd68336e08 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -85,9 +85,6 @@ public: static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap); - static android::Bitmap* mapAshmemBitmap(JNIEnv* env, SkBitmap* bitmap, - int fd, void* addr, size_t size, bool readOnly); - /** * Given a bitmap we natively allocate a memory block to store the contents * of that bitmap. The memory is then attached to the bitmap via an diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index fa1da4bfbf3a..888dab19c247 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -32,6 +32,7 @@ #include <atomic> #include <iomanip> #include <string> +#include <vector> #include <debuggerd/client.h> #include <log/log.h> @@ -41,6 +42,7 @@ #include <nativehelper/JNIHelp.h> #include <nativehelper/ScopedUtfChars.h> #include "jni.h" +#include <meminfo/sysmeminfo.h> #include <memtrack/memtrack.h> #include <memunreachable/memunreachable.h> #include "android_os_Debug.h" @@ -712,6 +714,8 @@ static long get_allocated_vmalloc_memory() { return vmalloc_allocated_size; } +// The 1:1 mapping of MEMINFO_* enums here must match with the constants from +// Debug.java. enum { MEMINFO_TOTAL, MEMINFO_FREE, @@ -731,138 +735,43 @@ enum { MEMINFO_COUNT }; -static long long get_zram_mem_used() -{ -#define ZRAM_SYSFS "/sys/block/zram0/" - UniqueFile mm_stat_file = MakeUniqueFile(ZRAM_SYSFS "mm_stat", "re"); - if (mm_stat_file) { - long long mem_used_total = 0; - - int matched = fscanf(mm_stat_file.get(), "%*d %*d %lld %*d %*d %*d %*d", &mem_used_total); - if (matched != 1) - ALOGW("failed to parse " ZRAM_SYSFS "mm_stat"); - - return mem_used_total; - } - - UniqueFile mem_used_total_file = MakeUniqueFile(ZRAM_SYSFS "mem_used_total", "re"); - if (mem_used_total_file) { - long long mem_used_total = 0; - - int matched = fscanf(mem_used_total_file.get(), "%lld", &mem_used_total); - if (matched != 1) - ALOGW("failed to parse " ZRAM_SYSFS "mem_used_total"); - - return mem_used_total; - } - - return 0; -} - static void android_os_Debug_getMemInfo(JNIEnv *env, jobject clazz, jlongArray out) { - char buffer[4096]; - size_t numFound = 0; - if (out == NULL) { jniThrowNullPointerException(env, "out == null"); return; } - int fd = open("/proc/meminfo", O_RDONLY | O_CLOEXEC); - - if (fd < 0) { - ALOGW("Unable to open /proc/meminfo: %s\n", strerror(errno)); + int outLen = env->GetArrayLength(out); + if (outLen < MEMINFO_COUNT) { + jniThrowRuntimeException(env, "outLen < MEMINFO_COUNT"); return; } - int len = read(fd, buffer, sizeof(buffer)-1); - close(fd); - - if (len < 0) { - ALOGW("Empty /proc/meminfo"); + // Read system memory info including ZRAM. The values are stored in the vector + // in the same order as MEMINFO_* enum + std::vector<uint64_t> mem(MEMINFO_COUNT); + std::vector<std::string> tags(::android::meminfo::SysMemInfo::kDefaultSysMemInfoTags); + tags.insert(tags.begin() + MEMINFO_ZRAM_TOTAL, "Zram:"); + ::android::meminfo::SysMemInfo smi; + if (!smi.ReadMemInfo(tags, &mem)) { + jniThrowRuntimeException(env, "SysMemInfo read failed"); return; } - buffer[len] = 0; - - static const char* const tags[] = { - "MemTotal:", - "MemFree:", - "Buffers:", - "Cached:", - "Shmem:", - "Slab:", - "SReclaimable:", - "SUnreclaim:", - "SwapTotal:", - "SwapFree:", - "ZRam:", - "Mapped:", - "VmallocUsed:", - "PageTables:", - "KernelStack:", - NULL - }; - static const int tagsLen[] = { - 9, - 8, - 8, - 7, - 6, - 5, - 13, - 11, - 10, - 9, - 5, - 7, - 12, - 11, - 12, - 0 - }; - long mem[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - - char* p = buffer; - while (*p && numFound < (sizeof(tagsLen) / sizeof(tagsLen[0]))) { - int i = 0; - while (tags[i]) { - if (strncmp(p, tags[i], tagsLen[i]) == 0) { - p += tagsLen[i]; - while (*p == ' ') p++; - char* num = p; - while (*p >= '0' && *p <= '9') p++; - if (*p != 0) { - *p = 0; - p++; - } - mem[i] = atoll(num); - numFound++; - break; - } - i++; - } - while (*p && *p != '\n') { - p++; - } - if (*p) p++; - } - - mem[MEMINFO_ZRAM_TOTAL] = get_zram_mem_used() / 1024; - // Recompute Vmalloc Used since the value in meminfo - // doesn't account for I/O remapping which doesn't use RAM. - mem[MEMINFO_VMALLOC_USED] = get_allocated_vmalloc_memory() / 1024; - int maxNum = env->GetArrayLength(out); - if (maxNum > MEMINFO_COUNT) { - maxNum = MEMINFO_COUNT; - } jlong* outArray = env->GetLongArrayElements(out, 0); if (outArray != NULL) { - for (int i=0; i<maxNum; i++) { + outLen = MEMINFO_COUNT; + for (int i = 0; i < outLen; i++) { + // TODO: move get_allocated_vmalloc_memory() to libmeminfo + if (i == MEMINFO_VMALLOC_USED) { + outArray[i] = get_allocated_vmalloc_memory() / 1024; + continue; + } outArray[i] = mem[i]; } } + env->ReleaseLongArrayElements(out, outArray, 0); } diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index 102a0b7b8957..0c1a8aa18370 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -25,8 +25,12 @@ #include <cutils/sched_policy.h> #include <utils/String8.h> #include <utils/Vector.h> +#include <meminfo/sysmeminfo.h> #include <processgroup/processgroup.h> +#include <string> +#include <vector> + #include "core_jni_helpers.h" #include "android_util_Binder.h" @@ -39,9 +43,11 @@ #include <inttypes.h> #include <pwd.h> #include <signal.h> +#include <string.h> #include <sys/errno.h> #include <sys/resource.h> #include <sys/stat.h> +#include <sys/sysinfo.h> #include <sys/types.h> #include <unistd.h> @@ -603,66 +609,34 @@ static int pid_compare(const void* v1, const void* v2) return *((const jint*)v1) - *((const jint*)v2); } -static jlong getFreeMemoryImpl(const char* const sums[], const size_t sumsLen[], size_t num) +static jlong android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz) { - int fd = open("/proc/meminfo", O_RDONLY | O_CLOEXEC); + static const std::vector<std::string> memFreeTags = { + ::android::meminfo::SysMemInfo::kMemFree, + ::android::meminfo::SysMemInfo::kMemCached, + }; + std::vector<uint64_t> mem(memFreeTags.size()); + ::android::meminfo::SysMemInfo smi; - if (fd < 0) { - ALOGW("Unable to open /proc/meminfo"); - return -1; + if (!smi.ReadMemInfo(memFreeTags, &mem)) { + jniThrowRuntimeException(env, "SysMemInfo read failed to get Free Memory"); + return -1L; } - char buffer[2048]; - const int len = read(fd, buffer, sizeof(buffer)-1); - close(fd); - - if (len < 0) { - ALOGW("Unable to read /proc/meminfo"); - return -1; - } - buffer[len] = 0; - - size_t numFound = 0; - jlong mem = 0; - - char* p = buffer; - while (*p && numFound < num) { - int i = 0; - while (sums[i]) { - if (strncmp(p, sums[i], sumsLen[i]) == 0) { - p += sumsLen[i]; - while (*p == ' ') p++; - char* num = p; - while (*p >= '0' && *p <= '9') p++; - if (*p != 0) { - *p = 0; - p++; - if (*p == 0) p--; - } - mem += atoll(num) * 1024; - numFound++; - break; - } - i++; - } - p++; - } - - return numFound > 0 ? mem : -1; -} - -static jlong android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz) -{ - static const char* const sums[] = { "MemFree:", "Cached:", NULL }; - static const size_t sumsLen[] = { strlen("MemFree:"), strlen("Cached:"), 0 }; - return getFreeMemoryImpl(sums, sumsLen, 2); + jlong sum = 0; + std::for_each(mem.begin(), mem.end(), [&](uint64_t val) { sum += val; }); + return sum * 1024; } static jlong android_os_Process_getTotalMemory(JNIEnv* env, jobject clazz) { - static const char* const sums[] = { "MemTotal:", NULL }; - static const size_t sumsLen[] = { strlen("MemTotal:"), 0 }; - return getFreeMemoryImpl(sums, sumsLen, 1); + struct sysinfo si; + if (sysinfo(&si) == -1) { + ALOGE("sysinfo failed: %s", strerror(errno)); + return -1; + } + + return si.totalram; } void android_os_Process_readProcLines(JNIEnv* env, jobject clazz, jstring fileStr, diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp index e0fd1a634557..40a133b9383b 100644 --- a/core/jni/android_view_DisplayListCanvas.cpp +++ b/core/jni/android_view_DisplayListCanvas.cpp @@ -168,6 +168,11 @@ static void android_view_DisplayListCanvas_drawCircleProps(jlong canvasPtr, canvas->drawCircle(xProp, yProp, radiusProp, paintProp); } +static void android_view_DisplayListCanvas_drawWebViewFunctor(jlong canvasPtr, jint functor) { + Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr); + canvas->drawWebViewFunctor(functor); +} + // ---------------------------------------------------------------------------- // JNI Glue // ---------------------------------------------------------------------------- @@ -192,6 +197,7 @@ static JNINativeMethod gMethods[] = { { "nDrawTextureLayer", "(JJ)V", (void*) android_view_DisplayListCanvas_drawTextureLayer }, { "nDrawCircle", "(JJJJJ)V", (void*) android_view_DisplayListCanvas_drawCircleProps }, { "nDrawRoundRect", "(JJJJJJJJ)V",(void*) android_view_DisplayListCanvas_drawRoundRectProps }, + { "nDrawWebViewFunctor", "(JI)V", (void*) android_view_DisplayListCanvas_drawWebViewFunctor }, }; int register_android_view_DisplayListCanvas(JNIEnv* env) { diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp index 752624b0a0be..0d75de9ee95f 100644 --- a/core/jni/android_view_RenderNode.cpp +++ b/core/jni/android_view_RenderNode.cpp @@ -338,6 +338,11 @@ static jboolean android_view_RenderNode_hasOverlappingRendering(jlong renderNode return renderNode->stagingProperties().hasOverlappingRendering(); } +static jboolean android_view_RenderNode_getClipToBounds(jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->stagingProperties().getClipToBounds(); +} + static jboolean android_view_RenderNode_getClipToOutline(jlong renderNodePtr) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); return renderNode->stagingProperties().getOutline().getShouldClip(); @@ -409,6 +414,11 @@ static jboolean android_view_RenderNode_hasIdentityMatrix(jlong renderNodePtr) { return !renderNode->stagingProperties().hasTransformMatrix(); } +static jint android_view_RenderNode_getLayerType(jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return static_cast<int>(renderNode->stagingProperties().layerProperties().type()); +} + // ---------------------------------------------------------------------------- // RenderProperties - computed getters // ---------------------------------------------------------------------------- @@ -623,10 +633,12 @@ static const JNINativeMethod gMethods[] = { // ---------------------------------------------------------------------------- { "nIsValid", "(J)Z", (void*) android_view_RenderNode_isValid }, { "nSetLayerType", "(JI)Z", (void*) android_view_RenderNode_setLayerType }, + { "nGetLayerType", "(J)I", (void*) android_view_RenderNode_getLayerType }, { "nSetLayerPaint", "(JJ)Z", (void*) android_view_RenderNode_setLayerPaint }, { "nSetStaticMatrix", "(JJ)Z", (void*) android_view_RenderNode_setStaticMatrix }, { "nSetAnimationMatrix", "(JJ)Z", (void*) android_view_RenderNode_setAnimationMatrix }, { "nSetClipToBounds", "(JZ)Z", (void*) android_view_RenderNode_setClipToBounds }, + { "nGetClipToBounds", "(J)Z", (void*) android_view_RenderNode_getClipToBounds }, { "nSetClipBounds", "(JIIII)Z", (void*) android_view_RenderNode_setClipBounds }, { "nSetClipBoundsEmpty", "(J)Z", (void*) android_view_RenderNode_setClipBoundsEmpty }, { "nSetProjectBackwards", "(JZ)Z", (void*) android_view_RenderNode_setProjectBackwards }, diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 702741eb813c..5a8ab3c1bdc4 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -31,8 +31,6 @@ #include <gui/BufferQueue.h> #include <gui/Surface.h> -#include <EGL/egl.h> -#include <EGL/eglext.h> #include <private/EGL/cache.h> #include <utils/Looper.h> @@ -58,6 +56,7 @@ #include <renderthread/RenderTask.h> #include <renderthread/RenderThread.h> #include <pipeline/skia/ShaderCache.h> +#include <utils/Color.h> namespace android { @@ -1011,10 +1010,9 @@ static jobject android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode( buffer->getWidth(), buffer->getHeight(), width, height); // Continue I guess? } - sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer); - // Bitmap::createFrom currently can only attach to a GraphicBuffer with PIXEL_FORMAT_RGBA_8888 - // format and SRGB color space. - // To support any color space, we could extract it from BufferItem and pass it to Bitmap. + + sk_sp<SkColorSpace> cs = uirenderer::DataSpaceToColorSpace(bufferItem.mDataSpace); + sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, cs); return bitmap::createBitmap(env, bitmap.release(), android::bitmap::kBitmapCreateFlag_Premultiplied); } diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 7032081ef6a0..ff4591fb7f45 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -418,7 +418,7 @@ static int UnmountTree(const char* path) { } endmntent(fp); - for (auto path : toUnmount) { + for (const auto& path : toUnmount) { if (umount2(path.c_str(), MNT_DETACH)) { ALOGW("Failed to unmount %s: %s", path.c_str(), strerror(errno)); } diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto index ce19ce3519b2..71ebcc1e3659 100644 --- a/core/proto/android/service/procstats.proto +++ b/core/proto/android/service/procstats.proto @@ -186,7 +186,7 @@ message PackageServiceStatsProto { repeated PackageServiceOperationStatsProto operation_stats = 2; } -// Next Tag: 7 +// Next Tag: 8 message PackageAssociationSourceProcessStatsProto { option (android.msg_privacy).dest = DEST_AUTOMATIC; @@ -194,6 +194,8 @@ message PackageAssociationSourceProcessStatsProto { optional int32 process_uid = 1; // Process name. optional string process_name = 2; + // Package name. + optional string package_name = 7; // Total count of the times this association appeared. optional int32 total_count = 3; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 7fa3e66bae25..cc8927fdf730 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1642,6 +1642,12 @@ <permission android:name="android.permission.NETWORK_BYPASS_PRIVATE_DNS" android:protectionLevel="signature" /> + <!-- #SystemApi @hide Allows device mobility state to be set so that Wifi scan interval can be increased + when the device is stationary in order to save power. + <p>Not for use by third-party applications. --> + <permission android:name="android.permission.WIFI_SET_DEVICE_MOBILITY_STATE" + android:protectionLevel="signature|privileged" /> + <!-- ======================================= --> <!-- Permissions for short range, peripheral networks --> <!-- ======================================= --> diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml index 5ba1cf259551..0f53549a966c 100644 --- a/core/res/res/layout/notification_template_header.xml +++ b/core/res/res/layout/notification_template_header.xml @@ -111,6 +111,7 @@ android:background="@null" android:layout_width="@dimen/notification_header_expand_icon_size" android:layout_height="@dimen/notification_header_expand_icon_size" + android:layout_marginStart="4dp" android:paddingTop="@dimen/notification_expand_button_padding_top" android:visibility="gone" android:contentDescription="@string/expand_button_content_description_collapsed" diff --git a/core/res/res/values/colors_car.xml b/core/res/res/values/colors_car.xml index ea7c00919527..f4aeff7249ab 100644 --- a/core/res/res/values/colors_car.xml +++ b/core/res/res/values/colors_car.xml @@ -50,7 +50,7 @@ <color name="car_headline4_dark">@android:color/black</color> <color name="car_headline4">@color/car_headline4_light</color> - <color name="car_body1_light">@color/car_grey_100</color> + <color name="car_body1_light">@color/car_grey_50</color> <color name="car_body1_dark">@color/car_grey_900</color> <color name="car_body1">@color/car_body1_light</color> @@ -58,7 +58,7 @@ <color name="car_body2_dark">@color/car_grey_700</color> <color name="car_body2">@color/car_body2_light</color> - <color name="car_body3_light">@android:color/white</color> + <color name="car_body3_light">@color/car_grey_400</color> <color name="car_body3_dark">@android:color/black</color> <color name="car_body3">@color/car_body3_light</color> @@ -137,7 +137,7 @@ <color name="car_toast_background">#E6282a2d</color> <!-- Misc colors --> - <color name="car_highlight_light">@color/car_teal_700</color> + <color name="car_highlight_light">@color/car_teal_200</color> <color name="car_highlight_dark">@color/car_teal_200</color> <color name="car_highlight">@color/car_highlight_dark</color> <color name="car_accent_light">@color/car_highlight_light</color> @@ -148,6 +148,7 @@ <color name="car_user_switcher_user_image_fgcolor">@color/car_grey_900</color> <!-- Color palette for cars --> + <color name="car_grey_972">#ff090A0C</color> <color name="car_grey_958">#ff0e1013</color> <color name="car_grey_928">#ff17181b</color> <color name="car_grey_900">#ff202124</color> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 9277daec7027..dd0b1ee83e14 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1165,6 +1165,10 @@ <!-- The default suggested battery % at which we enable battery saver automatically. --> <integer name="config_lowBatteryAutoTriggerDefaultLevel">15</integer> + <!-- The app which will handle routine based automatic battery saver, if empty the UI for + routine based battery saver will be hidden --> + <string name="config_batterySaverScheduleProvider"></string> + <!-- Close low battery warning when battery level reaches the lowBatteryWarningLevel plus this --> <integer name="config_lowBatteryCloseWarningBump">5</integer> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 6f75d90c46c4..799d9d858b59 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2978,6 +2978,11 @@ <public name="config_mediaMetadataBitmapMaxSize" /> </public-group> + <public-group type="color" first-id="0x0106001c"> + <!-- @hide @SystemApi --> + <public name="system_notification_accent_color" /> + </public-group> + <!-- =============================================================== DO NOT ADD UN-GROUPED ITEMS HERE diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index d68681d8c51d..87fdc1fb575b 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3463,6 +3463,7 @@ <java-symbol type="integer" name="config_lowBatteryAutoTriggerDefaultLevel" /> <java-symbol type="bool" name="config_batterySaverStickyBehaviourDisabled" /> <java-symbol type="integer" name="config_dynamicPowerSavingsDefaultDisableThreshold" /> + <java-symbol type="string" name="config_batterySaverScheduleProvider" /> <!-- For car devices --> <java-symbol type="string" name="car_loading_profile" /> diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java index 55e21a76f170..514ea0cc013e 100644 --- a/core/tests/coretests/src/android/os/FileUtilsTest.java +++ b/core/tests/coretests/src/android/os/FileUtilsTest.java @@ -517,6 +517,28 @@ public class FileUtilsTest { } @Test + public void testMalformedTransate_int() throws Exception { + try { + // The non-standard Linux access mode 3 should throw + // an IllegalArgumentException. + translateModePosixToPfd(O_RDWR | O_WRONLY); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testMalformedTransate_string() throws Exception { + try { + // The non-standard Linux access mode 3 should throw + // an IllegalArgumentException. + translateModePosixToString(O_RDWR | O_WRONLY); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + @Test public void testTranslateMode_Invalid() throws Exception { try { translateModeStringToPosix("rwx"); diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java index 96ab977b799c..5a86885459f2 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java @@ -278,13 +278,12 @@ public abstract class OverlayBaseTest { } } - final String no = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, " + - "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " + - "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip " + - "ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit " + - "esse cillum dolore eu fugiat nulla pariatur. " + - "Excepteur sint occaecat cupidatat non proident, " + - "sunt in culpa qui officia deserunt mollit anim id est laborum."; + final String no = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do " + + "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim " + + "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo " + + "consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse " + + "cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non " + + "proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; final String so = "Lorem ipsum: single overlay."; final String mo = "Lorem ipsum: multiple overlays."; diff --git a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java index 6d5276f2423e..27986cce0835 100644 --- a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java +++ b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java @@ -1,17 +1,17 @@ /* * Copyright (C) 2018 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.om.hosttest; diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java b/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java index a174d774b80e..86a86794004c 100644 --- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java +++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java @@ -1,17 +1,17 @@ /* * Copyright (C) 2018 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.om.hosttest.update_overlay_test; diff --git a/data/etc/Android.mk b/data/etc/Android.mk index 61ef426f510a..ff8c4f12d9d1 100644 --- a/data/etc/Android.mk +++ b/data/etc/Android.mk @@ -54,6 +54,7 @@ LOCAL_MODULE := privapp_whitelist_com.android.settings LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_RELATIVE_PATH := permissions LOCAL_MODULE_STEM := com.android.settings.xml +LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := com.android.settings.xml include $(BUILD_PREBUILT) @@ -63,6 +64,7 @@ LOCAL_MODULE := privapp_whitelist_com.android.systemui LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_RELATIVE_PATH := permissions LOCAL_MODULE_STEM := com.android.systemui.xml +LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := com.android.systemui.xml include $(BUILD_PREBUILT) diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index af570b39925b..c21642559bb9 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -297,6 +297,8 @@ applications that come with the platform <permission name="android.permission.POWER_SAVER" /> <permission name="android.permission.READ_FRAME_BUFFER"/> <permission name="android.permission.READ_LOWPAN_CREDENTIAL"/> + <!-- Needed for test only --> + <permission name="android.permission.READ_PRECISE_PHONE_STATE" /> <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> <permission name="android.permission.REAL_GET_TASKS"/> <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> diff --git a/graphics/java/android/graphics/RecordingCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java index 67ad4045868e..515532ffda52 100644 --- a/graphics/java/android/graphics/RecordingCanvas.java +++ b/graphics/java/android/graphics/RecordingCanvas.java @@ -189,6 +189,14 @@ public final class RecordingCanvas extends DisplayListCanvas { nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunctor, releasedCallback); } + /** + * Calls the provided functor that was created via WebViewFunctor_create() + * @hide + */ + public void drawWebViewFunctor(int functor) { + nDrawWebViewFunctor(mNativeCanvasWrapper, functor); + } + /////////////////////////////////////////////////////////////////////////// // Display list /////////////////////////////////////////////////////////////////////////// @@ -303,4 +311,6 @@ public final class RecordingCanvas extends DisplayListCanvas { @CriticalNative private static native void nDrawRoundRect(long renderer, long propLeft, long propTop, long propRight, long propBottom, long propRx, long propRy, long propPaint); + @CriticalNative + private static native void nDrawWebViewFunctor(long canvas, int functor); } diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java index d6f08b92a648..3b1d44b44ed4 100644 --- a/graphics/java/android/graphics/RenderNode.java +++ b/graphics/java/android/graphics/RenderNode.java @@ -446,7 +446,21 @@ public final class RenderNode { } /** - * Sets the clip bounds of the RenderNode. + * Gets whether or not a compositing layer is forced to be used. The default & recommended + * is false, as it is typically faster to avoid using compositing layers. + * See {@link #setUseCompositingLayer(boolean, Paint)}. + * + * @return true if a compositing layer is forced, false otherwise + */ + public boolean getUseCompositingLayer() { + return nGetLayerType(mNativeRenderNode) != 0; + } + + /** + * Sets the clip bounds of the RenderNode. If null, the clip bounds is removed from the + * RenderNode. If non-null, the RenderNode will be clipped to this rect. If + * {@link #setClipToBounds(boolean)} is true, then the RenderNode will be clipped to the + * intersection of this rectangle and the bounds of the render node. * * @param rect the bounds to clip to. If null, the clip bounds are reset * @return True if the clip bounds changed, false otherwise @@ -460,16 +474,30 @@ public final class RenderNode { } /** - * Set whether the Render node should clip itself to its bounds. This property is controlled by - * the view's parent. + * Set whether the Render node should clip itself to its bounds. This defaults to true, + * and is useful to the renderer in enable quick-rejection of chunks of the tree as well as + * better partial invalidation support. Clipping can be further restricted or controlled + * through the combination of this property as well as {@link #setClipBounds(Rect)}, which + * allows for a different clipping rectangle to be used in addition to or instead of the + * {@link #setLeftTopRightBottom(int, int, int, int)} or the RenderNode. * - * @param clipToBounds true if the display list should clip to its bounds + * @param clipToBounds true if the display list should clip to its bounds, false otherwise. */ public boolean setClipToBounds(boolean clipToBounds) { return nSetClipToBounds(mNativeRenderNode, clipToBounds); } /** + * Returns whether or not the RenderNode is clipping to its bounds. See + * {@link #setClipToBounds(boolean)} and {@link #setLeftTopRightBottom(int, int, int, int)} + * + * @return true if the render node clips to its bounds, false otherwise. + */ + public boolean getClipToBounds() { + return nGetClipToBounds(mNativeRenderNode); + } + + /** * Sets whether the RenderNode should be drawn immediately after the * closest ancestor RenderNode containing a projection receiver. * @@ -1339,12 +1367,18 @@ public final class RenderNode { private static native boolean nSetLayerType(long renderNode, int layerType); @CriticalNative + private static native int nGetLayerType(long renderNode); + + @CriticalNative private static native boolean nSetLayerPaint(long renderNode, long paint); @CriticalNative private static native boolean nSetClipToBounds(long renderNode, boolean clipToBounds); @CriticalNative + private static native boolean nGetClipToBounds(long renderNode); + + @CriticalNative private static native boolean nSetClipBounds(long renderNode, int left, int top, int right, int bottom); diff --git a/libs/androidfw/TEST_MAPPING b/libs/androidfw/TEST_MAPPING new file mode 100644 index 000000000000..a58b47fcff9d --- /dev/null +++ b/libs/androidfw/TEST_MAPPING @@ -0,0 +1,8 @@ +{ + "presubmit": [ + { + "name": "libandroidfw_tests", + "host": true + } + ] +}
\ No newline at end of file diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index 441356b95d36..22d587a7f5c4 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -365,6 +365,6 @@ TEST(LoadedArscTest, ResourceIdentifierIterator) { // structs with size fields (like Res_value, ResTable_entry) should be // backwards and forwards compatible (aka checking the size field against // sizeof(Res_value) might not be backwards compatible. -TEST(LoadedArscTest, LoadingShouldBeForwardsAndBackwardsCompatible) { ASSERT_TRUE(false); } +// TEST(LoadedArscTest, LoadingShouldBeForwardsAndBackwardsCompatible) { ASSERT_TRUE(false); } } // namespace android diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 6585bfc929ba..7e69e3a8a73f 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -9,6 +9,8 @@ cc_defaults { "hwui_lto", ], + cpp_std: "experimental", + cflags: [ "-DEGL_EGLEXT_PROTOTYPES", "-DGL_GLEXT_PROTOTYPES", @@ -226,6 +228,7 @@ cc_defaults { "RenderProperties.cpp", "SkiaCanvas.cpp", "TreeInfo.cpp", + "WebViewFunctorManager.cpp", "VectorDrawable.cpp", "protos/graphicsstats.proto", ], @@ -330,6 +333,7 @@ cc_test { "tests/unit/TypefaceTests.cpp", "tests/unit/VectorDrawableTests.cpp", "tests/unit/VectorDrawableAtlasTests.cpp", + "tests/unit/WebViewFunctorManagerTests.cpp", ], } diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp index a97c12cad9fd..b9860ada18fc 100644 --- a/libs/hwui/HardwareBitmapUploader.cpp +++ b/libs/hwui/HardwareBitmapUploader.cpp @@ -253,7 +253,8 @@ sk_sp<Bitmap> HardwareBitmapUploader::allocateHardwareBitmap(const SkBitmap& sou eglDestroySyncKHR(display, fence); } - return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info(), Bitmap::computePalette(bitmap))); + return Bitmap::createFrom(buffer.get(), bitmap.refColorSpace(), bitmap.alphaType(), + Bitmap::computePalette(bitmap)); } } // namespace android::uirenderer diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 35cf707665cb..de8777b4e79a 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -65,6 +65,7 @@ public: void applyColorTransform(ColorTransform transform); bool hasText() const { return mHasText; } + size_t usedSize() const { return fUsed; } private: friend class RecordingCanvas; diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 00ce28ad196f..1ff7ffe6bf87 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -288,7 +288,10 @@ void RenderNode::syncDisplayList(TreeObserver& observer, TreeInfo* info) { mDisplayList = mStagingDisplayList; mStagingDisplayList = nullptr; if (mDisplayList) { - mDisplayList->syncContents(); + WebViewSyncData syncData { + .applyForceDark = info && !info->disableForceDark + }; + mDisplayList->syncContents(syncData); handleForceDark(info); } } diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index 04379ae68a0d..ddb7e4e4ce74 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -98,15 +98,15 @@ public: LayerProperties& operator=(const LayerProperties& other); + // Strongly recommend using effectiveLayerType instead + LayerType type() const { return mType; } + private: LayerProperties(); ~LayerProperties(); void reset(); bool setColorFilter(SkColorFilter* filter); - // Private since external users should go through properties().effectiveLayerType() - LayerType type() const { return mType; } - friend class RenderProperties; LayerType mType = LayerType::None; diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp new file mode 100644 index 000000000000..20e77b453706 --- /dev/null +++ b/libs/hwui/WebViewFunctorManager.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "WebViewFunctorManager.h" + +#include <private/hwui/WebViewFunctor.h> +#include "Properties.h" + +#include <log/log.h> +#include <utils/Trace.h> +#include <atomic> + +namespace android::uirenderer { + +RenderMode WebViewFunctor_queryPlatformRenderMode() { + auto pipelineType = Properties::getRenderPipelineType(); + switch (pipelineType) { + case RenderPipelineType::SkiaGL: + return RenderMode::OpenGL_ES; + case RenderPipelineType::SkiaVulkan: + return RenderMode::Vulkan; + default: + LOG_ALWAYS_FATAL("Unknown render pipeline type: %d", (int)pipelineType); + } +} + +int WebViewFunctor_create(const WebViewFunctorCallbacks& prototype, RenderMode functorMode) { + if (functorMode != RenderMode::OpenGL_ES && functorMode != RenderMode::Vulkan) { + ALOGW("Unknown rendermode %d", (int)functorMode); + return -1; + } + if (functorMode == RenderMode::Vulkan && + WebViewFunctor_queryPlatformRenderMode() != RenderMode::Vulkan) { + ALOGW("Unable to map from GLES platform to a vulkan functor"); + return -1; + } + return WebViewFunctorManager::instance().createFunctor(prototype, functorMode); +} + +void WebViewFunctor_release(int functor) { + WebViewFunctorManager::instance().releaseFunctor(functor); +} + +static std::atomic_int sNextId{1}; + +WebViewFunctor::WebViewFunctor(const WebViewFunctorCallbacks& callbacks, RenderMode functorMode) { + mFunctor = sNextId++; + mCallbacks = callbacks; + mMode = functorMode; +} + +WebViewFunctor::~WebViewFunctor() { + destroyContext(); + + ATRACE_NAME("WebViewFunctor::onDestroy"); + mCallbacks.onDestroyed(mFunctor); +} + +void WebViewFunctor::sync(const WebViewSyncData& syncData) const { + ATRACE_NAME("WebViewFunctor::sync"); + mCallbacks.onSync(mFunctor, syncData); +} + +void WebViewFunctor::drawGl(const DrawGlInfo& drawInfo) { + ATRACE_NAME("WebViewFunctor::drawGl"); + if (!mHasContext) { + mHasContext = true; + } + mCallbacks.gles.draw(mFunctor, drawInfo); +} + +void WebViewFunctor::destroyContext() { + if (mHasContext) { + mHasContext = false; + ATRACE_NAME("WebViewFunctor::onContextDestroyed"); + mCallbacks.onContextDestroyed(mFunctor); + } +} + +WebViewFunctorManager& WebViewFunctorManager::instance() { + static WebViewFunctorManager sInstance; + return sInstance; +} + +int WebViewFunctorManager::createFunctor(const WebViewFunctorCallbacks& callbacks, + RenderMode functorMode) { + auto object = std::make_unique<WebViewFunctor>(callbacks, functorMode); + int id = object->id(); + auto handle = object->createHandle(); + { + std::lock_guard _lock{mLock}; + mActiveFunctors.push_back(std::move(handle)); + mFunctors.push_back(std::move(object)); + } + return id; +} + +void WebViewFunctorManager::releaseFunctor(int functor) { + sp<WebViewFunctor::Handle> toRelease; + { + std::lock_guard _lock{mLock}; + for (auto iter = mActiveFunctors.begin(); iter != mActiveFunctors.end(); iter++) { + if ((*iter)->id() == functor) { + toRelease = std::move(*iter); + mActiveFunctors.erase(iter); + break; + } + } + } +} + +void WebViewFunctorManager::onContextDestroyed() { + // WARNING: SKETCHY + // Because we know that we always remove from mFunctors on RenderThread, the same + // thread that always invokes onContextDestroyed, we know that the functor pointers + // will remain valid without the lock held. + // However, we won't block new functors from being added in the meantime. + mLock.lock(); + const size_t size = mFunctors.size(); + WebViewFunctor* toDestroyContext[size]; + for (size_t i = 0; i < size; i++) { + toDestroyContext[i] = mFunctors[i].get(); + } + mLock.unlock(); + for (size_t i = 0; i < size; i++) { + toDestroyContext[i]->destroyContext(); + } +} + +void WebViewFunctorManager::destroyFunctor(int functor) { + std::unique_ptr<WebViewFunctor> toRelease; + { + std::lock_guard _lock{mLock}; + for (auto iter = mFunctors.begin(); iter != mFunctors.end(); iter++) { + if ((*iter)->id() == functor) { + toRelease = std::move(*iter); + mFunctors.erase(iter); + break; + } + } + } +} + +sp<WebViewFunctor::Handle> WebViewFunctorManager::handleFor(int functor) { + std::lock_guard _lock{mLock}; + for (auto& iter : mActiveFunctors) { + if (iter->id() == functor) { + return iter; + } + } + return nullptr; +} + +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h new file mode 100644 index 000000000000..2a621dd411e3 --- /dev/null +++ b/libs/hwui/WebViewFunctorManager.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <private/hwui/WebViewFunctor.h> +#include <renderthread/RenderProxy.h> + +#include <utils/LightRefBase.h> +#include <mutex> +#include <vector> + +namespace android::uirenderer { + +class WebViewFunctorManager; + +class WebViewFunctor { +public: + WebViewFunctor(const WebViewFunctorCallbacks& callbacks, RenderMode functorMode); + ~WebViewFunctor(); + + class Handle : public LightRefBase<Handle> { + public: + ~Handle() { renderthread::RenderProxy::destroyFunctor(id()); } + + int id() const { return mReference.id(); } + + void sync(const WebViewSyncData& syncData) const { mReference.sync(syncData); } + + void drawGl(const DrawGlInfo& drawInfo) const { mReference.drawGl(drawInfo); } + + private: + friend class WebViewFunctor; + + Handle(WebViewFunctor& ref) : mReference(ref) {} + + WebViewFunctor& mReference; + }; + + int id() const { return mFunctor; } + void sync(const WebViewSyncData& syncData) const; + void drawGl(const DrawGlInfo& drawInfo); + void destroyContext(); + + sp<Handle> createHandle() { + LOG_ALWAYS_FATAL_IF(mCreatedHandle); + mCreatedHandle = true; + return sp<Handle>{new Handle(*this)}; + } + +private: + WebViewFunctorCallbacks mCallbacks; + int mFunctor; + RenderMode mMode; + bool mHasContext = false; + bool mCreatedHandle = false; +}; + +class WebViewFunctorManager { +public: + static WebViewFunctorManager& instance(); + + int createFunctor(const WebViewFunctorCallbacks& callbacks, RenderMode functorMode); + void releaseFunctor(int functor); + void onContextDestroyed(); + void destroyFunctor(int functor); + + sp<WebViewFunctor::Handle> handleFor(int functor); + +private: + WebViewFunctorManager() = default; + ~WebViewFunctorManager() = default; + + std::mutex mLock; + std::vector<std::unique_ptr<WebViewFunctor>> mFunctors; + std::vector<sp<WebViewFunctor::Handle>> mActiveFunctors; +}; + +} // namespace android::uirenderer diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 6c77f9ee1845..6e0258c9ecb2 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -75,20 +75,33 @@ sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(SkBitmap* bitmap) { return allocateBitmap(bitmap, &Bitmap::allocateAshmemBitmap); } -static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) { - void* addr = calloc(size, 1); - if (!addr) { +sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) { + // Create new ashmem region with read/write priv + int fd = ashmem_create_region("bitmap", size); + if (fd < 0) { return nullptr; } - return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes)); + + void* addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) { + close(fd); + return nullptr; + } + + if (ashmem_set_prot_region(fd, PROT_READ) < 0) { + munmap(addr, size); + close(fd); + return nullptr; + } + return sk_sp<Bitmap>(new Bitmap(addr, fd, size, info, rowBytes)); } -sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(SkBitmap& bitmap) { +sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(const SkBitmap& bitmap) { return uirenderer::HardwareBitmapUploader::allocateHardwareBitmap(bitmap); } sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap) { - return allocateBitmap(bitmap, &android::allocateHeapBitmap); + return allocateBitmap(bitmap, &Bitmap::allocateHeapBitmap); } sk_sp<Bitmap> Bitmap::allocateHeapBitmap(const SkImageInfo& info) { @@ -97,28 +110,15 @@ sk_sp<Bitmap> Bitmap::allocateHeapBitmap(const SkImageInfo& info) { LOG_ALWAYS_FATAL("trying to allocate too large bitmap"); return nullptr; } - return android::allocateHeapBitmap(size, info, info.minRowBytes()); + return allocateHeapBitmap(size, info, info.minRowBytes()); } -sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) { - // Create new ashmem region with read/write priv - int fd = ashmem_create_region("bitmap", size); - if (fd < 0) { - return nullptr; - } - - void* addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (addr == MAP_FAILED) { - close(fd); - return nullptr; - } - - if (ashmem_set_prot_region(fd, PROT_READ) < 0) { - munmap(addr, size); - close(fd); +sk_sp<Bitmap> Bitmap::allocateHeapBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) { + void* addr = calloc(size, 1); + if (!addr) { return nullptr; } - return sk_sp<Bitmap>(new Bitmap(addr, fd, size, info, rowBytes)); + return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes)); } void FreePixelRef(void* addr, void* context) { @@ -132,17 +132,38 @@ sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, SkPixelRef& pixelRef) pixelRef.rowBytes())); } -sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer) { - return createFrom(graphicBuffer, SkColorSpace::MakeSRGB()); -} -sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer, sk_sp<SkColorSpace> colorSpace) { +sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer, sk_sp<SkColorSpace> colorSpace, + SkAlphaType alphaType, BitmapPalette palette) { // As we will be effectively texture-sampling the buffer (using either EGL or Vulkan), we can - // view the colorspace as RGBA8888. + // view the format as RGBA8888. SkImageInfo info = SkImageInfo::Make(graphicBuffer->getWidth(), graphicBuffer->getHeight(), - kRGBA_8888_SkColorType, kPremul_SkAlphaType, - colorSpace); - return sk_sp<Bitmap>(new Bitmap(graphicBuffer.get(), info)); + kRGBA_8888_SkColorType, alphaType, colorSpace); + return sk_sp<Bitmap>(new Bitmap(graphicBuffer.get(), info, palette)); +} + +sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, size_t rowBytes, int fd, void* addr, + size_t size, bool readOnly) { + if (info.colorType() == kUnknown_SkColorType) { + LOG_ALWAYS_FATAL("unknown bitmap configuration"); + return nullptr; + } + + if (!addr) { + // Map existing ashmem region if not already mapped. + int flags = readOnly ? (PROT_READ) : (PROT_READ | PROT_WRITE); + size = ashmem_get_size_region(fd); + addr = mmap(NULL, size, flags, MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) { + return nullptr; + } + } + + sk_sp<Bitmap> bitmap(new Bitmap(addr, fd, size, info, rowBytes)); + if (readOnly) { + bitmap->setImmutable(); + } + return bitmap; } void Bitmap::setColorSpace(sk_sp<SkColorSpace> colorSpace) { diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h index d446377ec1d9..2138040d9690 100644 --- a/libs/hwui/hwui/Bitmap.h +++ b/libs/hwui/hwui/Bitmap.h @@ -54,28 +54,31 @@ typedef void (*FreeFunc)(void* addr, void* context); class ANDROID_API Bitmap : public SkPixelRef { public: + /* The allocate factories not only construct the Bitmap object but also allocate the + * backing store whose type is determined by the specific method that is called. + * + * The factories that accept SkBitmap* as a param will modify those params by + * installing the returned bitmap as their SkPixelRef. + * + * The factories that accept const SkBitmap& as a param will copy the contents of the + * provided bitmap into the newly allocated buffer. + */ + static sk_sp<Bitmap> allocateAshmemBitmap(SkBitmap* bitmap); + static sk_sp<Bitmap> allocateHardwareBitmap(const SkBitmap& bitmap); static sk_sp<Bitmap> allocateHeapBitmap(SkBitmap* bitmap); static sk_sp<Bitmap> allocateHeapBitmap(const SkImageInfo& info); - static sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& bitmap); - - static sk_sp<Bitmap> allocateAshmemBitmap(SkBitmap* bitmap); - static sk_sp<Bitmap> allocateAshmemBitmap(size_t allocSize, const SkImageInfo& info, - size_t rowBytes); - - static sk_sp<Bitmap> createFrom(sp<GraphicBuffer> graphicBuffer); + /* The createFrom factories construct a new Bitmap object by wrapping the already allocated + * memory that is provided as an input param. + */ static sk_sp<Bitmap> createFrom(sp<GraphicBuffer> graphicBuffer, - sk_sp<SkColorSpace> colorSpace); - + sk_sp<SkColorSpace> colorSpace, + SkAlphaType alphaType = kPremul_SkAlphaType, + BitmapPalette palette = BitmapPalette::Unknown); + static sk_sp<Bitmap> createFrom(const SkImageInfo& info, size_t rowBytes, int fd, void* addr, + size_t size, bool readOnly); static sk_sp<Bitmap> createFrom(const SkImageInfo&, SkPixelRef&); - Bitmap(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes); - Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info, - size_t rowBytes); - Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes); - Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, - BitmapPalette palette = BitmapPalette::Unknown); - int rowBytesAsPixels() const { return rowBytes() >> mInfo.shiftPerPixel(); } void reconfigure(const SkImageInfo& info, size_t rowBytes); @@ -123,6 +126,15 @@ public: } private: + static sk_sp<Bitmap> allocateAshmemBitmap(size_t size, const SkImageInfo& i, size_t rowBytes); + static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& i, size_t rowBytes); + + Bitmap(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes); + Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info, + size_t rowBytes); + Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes); + Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette); + virtual ~Bitmap(); void* getStorage() const; diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index a5f21d8e6d73..71814c365c31 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -178,6 +178,9 @@ public: virtual void drawRenderNode(uirenderer::RenderNode* renderNode) = 0; virtual void callDrawGLFunction(Functor* functor, uirenderer::GlFunctorLifecycleListener* listener) = 0; + virtual void drawWebViewFunctor(int /*functor*/) { + LOG_ALWAYS_FATAL("Not supported"); + } // ---------------------------------------------------------------------------- // Canvas state operations diff --git a/libs/hwui/pipeline/skia/FunctorDrawable.h b/libs/hwui/pipeline/skia/FunctorDrawable.h index af3a056864a7..cf2f93b95e71 100644 --- a/libs/hwui/pipeline/skia/FunctorDrawable.h +++ b/libs/hwui/pipeline/skia/FunctorDrawable.h @@ -21,7 +21,9 @@ #include <SkCanvas.h> #include <SkDrawable.h> +#include <WebViewFunctorManager.h> #include <utils/Functor.h> +#include <variant> namespace android { namespace uirenderer { @@ -35,17 +37,43 @@ namespace skiapipeline { class FunctorDrawable : public SkDrawable { public: FunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas) - : mFunctor(functor), mListener(listener), mBounds(canvas->getLocalClipBounds()) {} + : mBounds(canvas->getLocalClipBounds()) + , mAnyFunctor(std::in_place_type<LegacyFunctor>, functor, listener) {} + + FunctorDrawable(int functor, SkCanvas* canvas) + : mBounds(canvas->getLocalClipBounds()) + , mAnyFunctor(std::in_place_type<NewFunctor>, functor) {} + virtual ~FunctorDrawable() {} - virtual void syncFunctor() const = 0; + virtual void syncFunctor(const WebViewSyncData& data) const { + if (mAnyFunctor.index() == 0) { + std::get<0>(mAnyFunctor).handle->sync(data); + } else { + (*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeSync, nullptr); + } + } protected: virtual SkRect onGetBounds() override { return mBounds; } - Functor* mFunctor; - sp<GlFunctorLifecycleListener> mListener; const SkRect mBounds; + + struct LegacyFunctor { + explicit LegacyFunctor(Functor* functor, GlFunctorLifecycleListener* listener) + : functor(functor), listener(listener) {} + Functor* functor; + sp<GlFunctorLifecycleListener> listener; + }; + + struct NewFunctor { + explicit NewFunctor(int functor) { + handle = WebViewFunctorManager::instance().handleFor(functor); + } + sp<WebViewFunctor::Handle> handle; + }; + + std::variant<NewFunctor, LegacyFunctor> mAnyFunctor; }; } // namespace skiapipeline diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp index 4a87e7502c6f..240efb41285c 100644 --- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp @@ -17,29 +17,28 @@ #include "GLFunctorDrawable.h" #include <GrContext.h> #include <private/hwui/DrawGlInfo.h> +#include "FunctorDrawable.h" #include "GlFunctorLifecycleListener.h" +#include "GrBackendSurface.h" +#include "GrRenderTarget.h" +#include "GrRenderTargetContext.h" #include "RenderNode.h" #include "SkAndroidFrameworkUtils.h" #include "SkClipStack.h" #include "SkRect.h" -#include "GrBackendSurface.h" -#include "GrRenderTarget.h" -#include "GrRenderTargetContext.h" namespace android { namespace uirenderer { namespace skiapipeline { GLFunctorDrawable::~GLFunctorDrawable() { - if (mListener.get() != nullptr) { - mListener->onGlFunctorReleased(mFunctor); + if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) { + if (lp->listener) { + lp->listener->onGlFunctorReleased(lp->functor); + } } } -void GLFunctorDrawable::syncFunctor() const { - (*mFunctor)(DrawGlInfo::kModeSync, nullptr); -} - static void setScissor(int viewportHeight, const SkIRect& clip) { SkASSERT(!clip.isEmpty()); // transform to Y-flipped GL space, and prevent negatives @@ -49,14 +48,14 @@ static void setScissor(int viewportHeight, const SkIRect& clip) { } static bool GetFboDetails(SkCanvas* canvas, GLuint* outFboID, SkISize* outFboSize) { - GrRenderTargetContext *renderTargetContext = + GrRenderTargetContext* renderTargetContext = canvas->internal_private_accessTopLayerRenderTargetContext(); if (!renderTargetContext) { ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw"); return false; } - GrRenderTarget *renderTarget = renderTargetContext->accessRenderTarget(); + GrRenderTarget* renderTarget = renderTargetContext->accessRenderTarget(); if (!renderTarget) { ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw"); return false; @@ -94,16 +93,16 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { sk_sp<SkSurface> tmpSurface; // we are in a state where there is an unclipped saveLayer if (fboID != 0 && !surfaceBounds.contains(clipBounds)) { - // create an offscreen layer and clear it - SkImageInfo surfaceInfo = canvas->imageInfo().makeWH(clipBounds.width(), clipBounds.height()); - tmpSurface = SkSurface::MakeRenderTarget(canvas->getGrContext(), SkBudgeted::kYes, - surfaceInfo); + SkImageInfo surfaceInfo = + canvas->imageInfo().makeWH(clipBounds.width(), clipBounds.height()); + tmpSurface = + SkSurface::MakeRenderTarget(canvas->getGrContext(), SkBudgeted::kYes, surfaceInfo); tmpSurface->getCanvas()->clear(SK_ColorTRANSPARENT); GrGLFramebufferInfo fboInfo; if (!tmpSurface->getBackendRenderTarget(SkSurface::kFlushWrite_BackendHandleAccess) - .getGLFramebufferInfo(&fboInfo)) { + .getGLFramebufferInfo(&fboInfo)) { ALOGW("Unable to extract renderTarget info from offscreen canvas; aborting GLFunctor"); return; } @@ -144,7 +143,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { bool clearStencilAfterFunctor = false; if (CC_UNLIKELY(clipRegion.isComplex())) { // clear the stencil - //TODO: move stencil clear and canvas flush to SkAndroidFrameworkUtils::clipWithStencil + // TODO: move stencil clear and canvas flush to SkAndroidFrameworkUtils::clipWithStencil glDisable(GL_SCISSOR_TEST); glStencilMask(0x1); glClearStencil(0); @@ -163,7 +162,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { // GL ops get inserted here if previous flush is missing, which could dirty the stencil bool stencilWritten = SkAndroidFrameworkUtils::clipWithStencil(tmpCanvas); - tmpCanvas->flush(); //need this flush for the single op that draws into the stencil + tmpCanvas->flush(); // need this flush for the single op that draws into the stencil // ensure that the framebuffer that the webview will render into is bound before after we // draw into the stencil @@ -188,7 +187,11 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { setScissor(info.height, clipRegion.getBounds()); } - (*mFunctor)(DrawGlInfo::kModeDraw, &info); + if (mAnyFunctor.index() == 0) { + std::get<0>(mAnyFunctor).handle->drawGl(info); + } else { + (*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeDraw, &info); + } if (clearStencilAfterFunctor) { // clear stencil buffer as it may be used by Skia diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h index 215979cba2e3..2ea4f67428bc 100644 --- a/libs/hwui/pipeline/skia/GLFunctorDrawable.h +++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h @@ -31,11 +31,9 @@ namespace skiapipeline { */ class GLFunctorDrawable : public FunctorDrawable { public: - GLFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas) - : FunctorDrawable(functor, listener, canvas) {} - virtual ~GLFunctorDrawable(); + using FunctorDrawable::FunctorDrawable; - void syncFunctor() const override; + virtual ~GLFunctorDrawable(); protected: void onDraw(SkCanvas* canvas) override; diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp index f08ac17e4082..eed19420a78a 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.cpp +++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include <utils/MathUtils.h> #include "LayerDrawable.h" +#include <utils/MathUtils.h> #include "GrBackendSurface.h" #include "SkColorFilter.h" @@ -44,10 +44,9 @@ static bool shouldFilter(const SkMatrix& matrix) { if (!matrix.isScaleTranslate()) return true; // We only care about meaningful scale here - bool noScale = MathUtils::isOne(matrix.getScaleX()) - && MathUtils::isOne(matrix.getScaleY()); - bool pixelAligned = SkScalarIsInt(matrix.getTranslateX()) - && SkScalarIsInt(matrix.getTranslateY()); + bool noScale = MathUtils::isOne(matrix.getScaleX()) && MathUtils::isOne(matrix.getScaleY()); + bool pixelAligned = + SkScalarIsInt(matrix.getTranslateX()) && SkScalarIsInt(matrix.getTranslateY()); return !(noScale && pixelAligned); } @@ -120,11 +119,12 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer // Integer translation is defined as when src rect and dst rect align fractionally. // Skia TextureOp has the above logic build-in, but not NonAAFillRectOp. TextureOp works // only for SrcOver blending and without color filter (readback uses Src blending). - bool isIntegerTranslate = isBasicallyTranslate(totalMatrix) - && SkScalarFraction(skiaDestRect.fLeft + totalMatrix[SkMatrix::kMTransX]) - == SkScalarFraction(skiaSrcRect.fLeft) - && SkScalarFraction(skiaDestRect.fTop + totalMatrix[SkMatrix::kMTransY]) - == SkScalarFraction(skiaSrcRect.fTop); + bool isIntegerTranslate = + isBasicallyTranslate(totalMatrix) && + SkScalarFraction(skiaDestRect.fLeft + totalMatrix[SkMatrix::kMTransX]) == + SkScalarFraction(skiaSrcRect.fLeft) && + SkScalarFraction(skiaDestRect.fTop + totalMatrix[SkMatrix::kMTransY]) == + SkScalarFraction(skiaSrcRect.fTop); if (layer->getForceFilter() || !isIntegerTranslate) { paint.setFilterQuality(kLow_SkFilterQuality); } diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h index 95dc6d0cf096..7cd515ae9fcb 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.h +++ b/libs/hwui/pipeline/skia/LayerDrawable.h @@ -32,8 +32,8 @@ class LayerDrawable : public SkDrawable { public: explicit LayerDrawable(DeferredLayerUpdater* layerUpdater) : mLayerUpdater(layerUpdater) {} - static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer, - const SkRect* srcRect, const SkRect* dstRect, bool useLayerTransform); + static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer, const SkRect* srcRect, + const SkRect* dstRect, bool useLayerTransform); protected: virtual SkRect onGetBounds() override { diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp index 4494cb05df10..df1537e2d824 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp @@ -127,6 +127,7 @@ public: mNode.markDrawEnd(mCanvas); } } + private: SkCanvas& mCanvas; RenderNode& mNode; @@ -140,7 +141,7 @@ void RenderNodeDrawable::forceDraw(SkCanvas* canvas) { // ensures that we paint the layer even if it is not currently visible in the // event that the properties change and it becomes visible. if ((mProjectedDisplayList == nullptr && !renderNode->isRenderable()) || - (renderNode->nothingToDraw() && mComposeLayer)) { + (renderNode->nothingToDraw() && mComposeLayer)) { return; } @@ -234,8 +235,8 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { // we need to restrict the portion of the surface drawn to the size of the renderNode. SkASSERT(renderNode->getLayerSurface()->width() >= bounds.width()); SkASSERT(renderNode->getLayerSurface()->height() >= bounds.height()); - canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(), - bounds, bounds, &paint); + canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(), bounds, + bounds, &paint); if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) { renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true; diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp index 073b4814305e..562a3b225e36 100644 --- a/libs/hwui/pipeline/skia/ShaderCache.cpp +++ b/libs/hwui/pipeline/skia/ShaderCache.cpp @@ -15,11 +15,11 @@ */ #include "ShaderCache.h" -#include <algorithm> #include <log/log.h> -#include <thread> -#include <array> #include <openssl/sha.h> +#include <algorithm> +#include <array> +#include <thread> #include "FileBlobCache.h" #include "Properties.h" #include "utils/TraceUtils.h" @@ -44,8 +44,7 @@ ShaderCache& ShaderCache::get() { } bool ShaderCache::validateCache(const void* identity, ssize_t size) { - if (nullptr == identity && size == 0) - return true; + if (nullptr == identity && size == 0) return true; if (nullptr == identity || size < 0) { if (CC_UNLIKELY(Properties::debugLevel & kDebugCaches)) { @@ -66,8 +65,7 @@ bool ShaderCache::validateCache(const void* identity, ssize_t size) { auto key = sIDKey; auto loaded = mBlobCache->get(&key, sizeof(key), hash.data(), hash.size()); - if (loaded && std::equal(hash.begin(), hash.end(), mIDHash.begin())) - return true; + if (loaded && std::equal(hash.begin(), hash.end(), mIDHash.begin())) return true; if (CC_UNLIKELY(Properties::debugLevel & kDebugCaches)) { ALOGW("ShaderCache::validateCache cache validation fails"); @@ -119,7 +117,7 @@ sk_sp<SkData> ShaderCache::load(const SkData& key) { int maxTries = 3; while (valueSize > mObservedBlobValueSize && maxTries > 0) { mObservedBlobValueSize = std::min(valueSize, maxValueSize); - void *newValueBuffer = realloc(valueBuffer, mObservedBlobValueSize); + void* newValueBuffer = realloc(valueBuffer, mObservedBlobValueSize); if (!newValueBuffer) { free(valueBuffer); return nullptr; @@ -133,7 +131,7 @@ sk_sp<SkData> ShaderCache::load(const SkData& key) { return nullptr; } if (valueSize > mObservedBlobValueSize) { - ALOGE("ShaderCache::load value size is too big %d", (int) valueSize); + ALOGE("ShaderCache::load value size is too big %d", (int)valueSize); free(valueBuffer); return nullptr; } diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h index 82804cf93785..d41aadb269dd 100644 --- a/libs/hwui/pipeline/skia/ShaderCache.h +++ b/libs/hwui/pipeline/skia/ShaderCache.h @@ -16,12 +16,12 @@ #pragma once +#include <GrContextOptions.h> #include <cutils/compiler.h> #include <memory> #include <mutex> #include <string> #include <vector> -#include <GrContextOptions.h> namespace android { @@ -52,7 +52,7 @@ public: * the initialized state the load and store methods will return without * performing any cache operations. */ - virtual void initShaderDiskCache(const void *identity, ssize_t size); + virtual void initShaderDiskCache(const void* identity, ssize_t size); virtual void initShaderDiskCache() { initShaderDiskCache(nullptr, 0); } @@ -153,7 +153,7 @@ private: /** * "mObservedBlobValueSize" is the maximum value size observed by the cache reading function. */ - size_t mObservedBlobValueSize = 20*1024; + size_t mObservedBlobValueSize = 20 * 1024; /** * The time in seconds to wait before saving newly inserted cache entries. @@ -176,7 +176,7 @@ private: */ static constexpr uint8_t sIDKey = 0; - friend class ShaderCacheTestUtils; //used for unit testing + friend class ShaderCacheTestUtils; // used for unit testing }; } /* namespace skiapipeline */ diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp index ac6f6a3f776d..230065c222a9 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp @@ -27,9 +27,9 @@ namespace android { namespace uirenderer { namespace skiapipeline { -void SkiaDisplayList::syncContents() { +void SkiaDisplayList::syncContents(const WebViewSyncData& data) { for (auto& functor : mChildFunctors) { - functor->syncFunctor(); + functor->syncFunctor(data); } for (auto& animatedImage : mAnimatedImages) { animatedImage->syncProperties(); diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h index d7879e722a29..3219ad1deeff 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.h +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h @@ -16,11 +16,11 @@ #pragma once -#include "hwui/AnimatedImageDrawable.h" #include "FunctorDrawable.h" #include "RecordingCanvas.h" #include "RenderNodeDrawable.h" #include "TreeInfo.h" +#include "hwui/AnimatedImageDrawable.h" #include "utils/LinearAllocator.h" #include <deque> @@ -49,7 +49,7 @@ namespace skiapipeline { */ class SkiaDisplayList { public: - size_t getUsedSize() { return allocator.usedSize(); } + size_t getUsedSize() { return allocator.usedSize() + mDisplayList.usedSize(); } ~SkiaDisplayList() { /* Given that we are using a LinearStdAllocator to store some of the @@ -109,7 +109,7 @@ public: * NOTE: This function can be folded into RenderNode when we no longer need * to subclass from DisplayList */ - void syncContents(); + void syncContents(const WebViewSyncData& data); /** * ONLY to be called by RenderNode::prepareTree in order to prepare this diff --git a/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp b/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp index ea578cb3ec05..e48ecf490c56 100644 --- a/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp +++ b/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp @@ -21,16 +21,16 @@ namespace uirenderer { namespace skiapipeline { SkiaMemoryTracer::SkiaMemoryTracer(std::vector<ResourcePair> resourceMap, bool itemizeType) - : mResourceMap(resourceMap) - , mItemizeType(itemizeType) - , mTotalSize("bytes", 0) - , mPurgeableSize("bytes", 0) {} + : mResourceMap(resourceMap) + , mItemizeType(itemizeType) + , mTotalSize("bytes", 0) + , mPurgeableSize("bytes", 0) {} SkiaMemoryTracer::SkiaMemoryTracer(const char* categoryKey, bool itemizeType) - : mCategoryKey(categoryKey) - , mItemizeType(itemizeType) - , mTotalSize("bytes", 0) - , mPurgeableSize("bytes", 0) {} + : mCategoryKey(categoryKey) + , mItemizeType(itemizeType) + , mTotalSize("bytes", 0) + , mPurgeableSize("bytes", 0) {} const char* SkiaMemoryTracer::mapName(const char* resourceName) { for (auto& resource : mResourceMap) { @@ -42,7 +42,7 @@ const char* SkiaMemoryTracer::mapName(const char* resourceName) { } void SkiaMemoryTracer::processElement() { - if(!mCurrentElement.empty()) { + if (!mCurrentElement.empty()) { // Only count elements that contain "size", other values just provide metadata. auto sizeResult = mCurrentValues.find("size"); if (sizeResult != mCurrentValues.end()) { @@ -136,8 +136,8 @@ void SkiaMemoryTracer::logOutput(String8& log) { for (const auto& typedValue : namedItem.second) { TraceValue traceValue = convertUnits(typedValue.second); const char* entry = (traceValue.count > 1) ? "entries" : "entry"; - log.appendFormat(" %s: %.2f %s (%d %s)\n", typedValue.first, - traceValue.value, traceValue.units, traceValue.count, entry); + log.appendFormat(" %s: %.2f %s (%d %s)\n", typedValue.first, traceValue.value, + traceValue.units, traceValue.count, entry); } } else { auto result = namedItem.second.find("size"); diff --git a/libs/hwui/pipeline/skia/SkiaMemoryTracer.h b/libs/hwui/pipeline/skia/SkiaMemoryTracer.h index abf1f4b052ce..e9a7981ef028 100644 --- a/libs/hwui/pipeline/skia/SkiaMemoryTracer.h +++ b/libs/hwui/pipeline/skia/SkiaMemoryTracer.h @@ -50,8 +50,8 @@ public: } bool shouldDumpWrappedObjects() const override { return true; } - void setMemoryBacking(const char*, const char*, const char*) override { } - void setDiscardableMemoryBacking(const char*, const SkDiscardableMemory&) override { } + void setMemoryBacking(const char*, const char*, const char*) override {} + void setDiscardableMemoryBacking(const char*, const SkDiscardableMemory&) override {} private: struct TraceValue { diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 7f62ab5abb7d..2e7850d48e54 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -184,15 +184,15 @@ bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator } else { String8 cachesOutput; mRenderThread.cacheManager().dumpMemoryUsage(cachesOutput, - &mRenderThread.renderState()); + &mRenderThread.renderState()); ALOGE("%s", cachesOutput.string()); if (errorHandler) { std::ostringstream err; err << "Unable to create layer for " << node->getName(); const int maxTextureSize = DeviceInfo::get()->maxTextureSize(); err << ", size " << info.width() << "x" << info.height() << " max size " - << maxTextureSize << " color type " << (int)info.colorType() - << " has context " << (int)(mRenderThread.getGrContext() != nullptr); + << maxTextureSize << " color type " << (int)info.colorType() << " has context " + << (int)(mRenderThread.getGrContext() != nullptr); errorHandler->onError(err.str()); } } @@ -301,8 +301,7 @@ void SkiaPipeline::endCapture(SkSurface* surface) { mSavePictureProcessor->savePicture(data, mCapturedFile); } else { mSavePictureProcessor->savePicture( - data, - mCapturedFile + "_" + std::to_string(mCaptureSequence)); + data, mCapturedFile + "_" + std::to_string(mCaptureSequence)); } mCaptureSequence--; } diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index af58f634ecd0..f2906de4d5a0 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -97,8 +97,7 @@ public: return mLightCenter; } - static void updateLighting(const LightGeometry& lightGeometry, - const LightInfo& lightInfo) { + static void updateLighting(const LightGeometry& lightGeometry, const LightInfo& lightInfo) { mLightRadius = lightGeometry.radius; mAmbientShadowAlpha = lightInfo.ambientShadowAlpha; mSpotShadowAlpha = lightInfo.spotShadowAlpha; diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index b56c3ef94791..6eefed959913 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -24,8 +24,8 @@ #include "RenderNode.h" #include "pipeline/skia/AnimatedDrawables.h" #include "pipeline/skia/GLFunctorDrawable.h" -#include "pipeline/skia/VkInteropFunctorDrawable.h" #include "pipeline/skia/VkFunctorDrawable.h" +#include "pipeline/skia/VkInteropFunctorDrawable.h" namespace android { namespace uirenderer { @@ -95,8 +95,8 @@ void SkiaRecordingCanvas::insertReorderBarrier(bool enableReorder) { drawDrawable(drawable); } if (enableReorder) { - mCurrentBarrier = mDisplayList->allocateDrawable<StartReorderBarrierDrawable>( - mDisplayList.get()); + mCurrentBarrier = + mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(mDisplayList.get()); drawDrawable(mCurrentBarrier); } } @@ -127,11 +127,25 @@ void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor, if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { // TODO(cblume) use VkFunctorDrawable instead of VkInteropFunctorDrawable here when the // interop is disabled/moved. - functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(functor, - listener, asSkCanvas()); + functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>( + functor, listener, asSkCanvas()); + } else { + functorDrawable = + mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, listener, asSkCanvas()); + } + mDisplayList->mChildFunctors.push_back(functorDrawable); + drawDrawable(functorDrawable); +} + +void SkiaRecordingCanvas::drawWebViewFunctor(int functor) { + FunctorDrawable* functorDrawable; + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { + // TODO(cblume) use VkFunctorDrawable instead of VkInteropFunctorDrawable here when the + // interop is disabled. + functorDrawable = + mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(functor, asSkCanvas()); } else { - functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, listener, - asSkCanvas()); + functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, asSkCanvas()); } mDisplayList->mChildFunctors.push_back(functorDrawable); drawDrawable(functorDrawable); @@ -167,7 +181,7 @@ SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint, if (colorSpaceFilter) { if (tmpPaint.getColorFilter()) { tmpPaint.setColorFilter(SkColorFilter::MakeComposeFilter( - tmpPaint.refColorFilter(), std::move(colorSpaceFilter))); + tmpPaint.refColorFilter(), std::move(colorSpaceFilter))); } else { tmpPaint.setColorFilter(std::move(colorSpaceFilter)); } @@ -248,8 +262,7 @@ void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& ch filteredPaint.writeable().setFilterQuality(kLow_SkFilterQuality); } sk_sp<SkImage> image = bitmap.makeImage(); - mRecorder.drawImageLattice(image, lattice, dst, - filterPaint(std::move(filteredPaint)), + mRecorder.drawImageLattice(image, lattice, dst, filterPaint(std::move(filteredPaint)), bitmap.palette()); if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) { mDisplayList->mMutableImages.push_back(image.get()); diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h index d6107a9d9969..afeccea3fb70 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h @@ -74,6 +74,7 @@ public: virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override; virtual void callDrawGLFunction(Functor* functor, uirenderer::GlFunctorLifecycleListener* listener) override; + void drawWebViewFunctor(int functor) override; private: RecordingCanvas mRecorder; diff --git a/libs/hwui/pipeline/skia/SkiaUtils.h b/libs/hwui/pipeline/skia/SkiaUtils.h index 834446905216..fa7f1fe2f746 100644 --- a/libs/hwui/pipeline/skia/SkiaUtils.h +++ b/libs/hwui/pipeline/skia/SkiaUtils.h @@ -22,7 +22,7 @@ namespace android { static inline SkRect SkRectMakeLargest() { const SkScalar v = SK_ScalarMax; - return { -v, -v, v, v }; + return {-v, -v, v, v}; }; } /* namespace android */ diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index 65ae0ddeccf3..1d3a24463057 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -20,9 +20,9 @@ #include "Readback.h" #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" +#include "VkInteropFunctorDrawable.h" #include "renderstate/RenderState.h" #include "renderthread/Frame.h" -#include "VkInteropFunctorDrawable.h" #include <SkSurface.h> #include <SkTypes.h> @@ -158,7 +158,7 @@ sk_sp<Bitmap> SkiaVulkanPipeline::allocateHardwareBitmap(renderthread::RenderThr ALOGW("SkiaVulkanPipeline::allocateHardwareBitmap() failed in GraphicBuffer.create()"); return nullptr; } - return sk_sp<Bitmap>(new Bitmap(buffer.get(), skBitmap.info())); + return Bitmap::createFrom(buffer, skBitmap.refColorSpace()); } } /* namespace skiapipeline */ diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp index 71ad5e17301a..156f74a611a7 100644 --- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp @@ -17,23 +17,21 @@ #include "VkFunctorDrawable.h" #include <private/hwui/DrawVkInfo.h> -#include "thread/ThreadBase.h" -#include "utils/TimeUtils.h" #include <GrBackendDrawableInfo.h> -#include <thread> +#include <SkImage.h> #include <utils/Color.h> #include <utils/Trace.h> #include <utils/TraceUtils.h> -#include <SkImage.h> #include <vk/GrVkTypes.h> +#include <thread> +#include "thread/ThreadBase.h" +#include "utils/TimeUtils.h" namespace android { namespace uirenderer { namespace skiapipeline { -VkFunctorDrawHandler::VkFunctorDrawHandler(Functor *functor) - : INHERITED() - , mFunctor(functor) {} +VkFunctorDrawHandler::VkFunctorDrawHandler(Functor* functor) : INHERITED(), mFunctor(functor) {} VkFunctorDrawHandler::~VkFunctorDrawHandler() { // TODO(cblume) Fill in the DrawVkInfo parameters. @@ -55,14 +53,12 @@ void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) { (*mFunctor)(DrawVkInfo::kModeComposite, &draw_vk_info); } -VkFunctorDrawable::VkFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, - SkCanvas* canvas) - : FunctorDrawable(functor, listener, canvas) {} - -VkFunctorDrawable::~VkFunctorDrawable() = default; - -void VkFunctorDrawable::syncFunctor() const { - (*mFunctor)(DrawVkInfo::kModeSync, nullptr); +VkFunctorDrawable::~VkFunctorDrawable() { + if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) { + if (lp->listener) { + lp->listener->onGlFunctorReleased(lp->functor); + } + } } void VkFunctorDrawable::onDraw(SkCanvas* /*canvas*/) { @@ -71,12 +67,17 @@ void VkFunctorDrawable::onDraw(SkCanvas* /*canvas*/) { } std::unique_ptr<FunctorDrawable::GpuDrawHandler> VkFunctorDrawable::onSnapGpuDrawHandler( - GrBackendApi backendApi, const SkMatrix& matrix) { + GrBackendApi backendApi, const SkMatrix& matrix) { if (backendApi != GrBackendApi::kVulkan) { return nullptr; } - std::unique_ptr<VkFunctorDrawHandler> draw(new VkFunctorDrawHandler(mFunctor)); - return std::move(draw); + std::unique_ptr<VkFunctorDrawHandler> draw; + if (mAnyFunctor.index() == 0) { + LOG_ALWAYS_FATAL("Not implemented"); + return nullptr; + } else { + return std::make_unique<VkFunctorDrawHandler>(std::get<1>(mAnyFunctor).functor); + } } } // namespace skiapipeline diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.h b/libs/hwui/pipeline/skia/VkFunctorDrawable.h index 5cd131405d15..d6fefc1fca06 100644 --- a/libs/hwui/pipeline/skia/VkFunctorDrawable.h +++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.h @@ -18,9 +18,9 @@ #include "FunctorDrawable.h" -#include <utils/RefBase.h> -#include <ui/GraphicBuffer.h> #include <SkImageInfo.h> +#include <ui/GraphicBuffer.h> +#include <utils/RefBase.h> namespace android { namespace uirenderer { @@ -36,6 +36,7 @@ public: ~VkFunctorDrawHandler() override; void draw(const GrBackendDrawableInfo& info) override; + private: typedef GpuDrawHandler INHERITED; @@ -48,17 +49,15 @@ private: */ class VkFunctorDrawable : public FunctorDrawable { public: - VkFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, - SkCanvas* canvas); - ~VkFunctorDrawable() override; + using FunctorDrawable::FunctorDrawable; - void syncFunctor() const override; + ~VkFunctorDrawable() override; protected: // SkDrawable functions: void onDraw(SkCanvas* canvas) override; - std::unique_ptr<FunctorDrawable::GpuDrawHandler> onSnapGpuDrawHandler(GrBackendApi backendApi, - const SkMatrix& matrix) override; + std::unique_ptr<FunctorDrawable::GpuDrawHandler> onSnapGpuDrawHandler( + GrBackendApi backendApi, const SkMatrix& matrix) override; }; } // namespace skiapipeline diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp index 82285501cb63..a5faae7d5068 100644 --- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp @@ -17,13 +17,13 @@ #include "VkInteropFunctorDrawable.h" #include <private/hwui/DrawGlInfo.h> -#include "renderthread/EglManager.h" -#include "thread/ThreadBase.h" -#include "utils/TimeUtils.h" -#include <thread> #include <utils/Color.h> #include <utils/Trace.h> #include <utils/TraceUtils.h> +#include <thread> +#include "renderthread/EglManager.h" +#include "thread/ThreadBase.h" +#include "utils/TimeUtils.h" #include <EGL/eglext.h> #include <GLES2/gl2.h> @@ -44,6 +44,7 @@ static renderthread::EglManager sEglManager; class ScopedDrawRequest { public: ScopedDrawRequest() { beginDraw(); } + private: void beginDraw() { std::lock_guard _lock{sLock}; @@ -57,9 +58,7 @@ private: } if (!sEglManager.hasEglContext()) { - sGLDrawThread->queue().runSync([]() { - sEglManager.initialize(); - }); + sGLDrawThread->queue().runSync([]() { sEglManager.initialize(); }); } } }; @@ -93,14 +92,14 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) { if (!mFrameBuffer.get() || mFBInfo != surfaceInfo) { // Buffer will be used as an OpenGL ES render target. mFrameBuffer = new GraphicBuffer( - //TODO: try to reduce the size of the buffer: possibly by using clip bounds. - static_cast<uint32_t>(surfaceInfo.width()), - static_cast<uint32_t>(surfaceInfo.height()), - ColorTypeToPixelFormat(surfaceInfo.colorType()), - GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER | - GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_HW_RENDER, - std::string("VkInteropFunctorDrawable::onDraw pid [") + std::to_string(getpid()) + - "]"); + // TODO: try to reduce the size of the buffer: possibly by using clip bounds. + static_cast<uint32_t>(surfaceInfo.width()), + static_cast<uint32_t>(surfaceInfo.height()), + ColorTypeToPixelFormat(surfaceInfo.colorType()), + GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER | + GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_HW_RENDER, + std::string("VkInteropFunctorDrawable::onDraw pid [") + std::to_string(getpid()) + + "]"); status_t error = mFrameBuffer->initCheck(); if (error < 0) { ALOGW("VkInteropFunctorDrawable::onDraw() failed in GraphicBuffer.create()"); @@ -110,16 +109,15 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) { mFBInfo = surfaceInfo; } - //TODO: Synchronization is needed on mFrameBuffer to guarantee that the previous Vulkan - //TODO: draw command has completed. - //TODO: A simple but inefficient way is to flush and issue a QueueWaitIdle call. See - //TODO: GrVkGpu::destroyResources() for example. + // TODO: Synchronization is needed on mFrameBuffer to guarantee that the previous Vulkan + // TODO: draw command has completed. + // TODO: A simple but inefficient way is to flush and issue a QueueWaitIdle call. See + // TODO: GrVkGpu::destroyResources() for example. bool success = sGLDrawThread->queue().runSync([&]() -> bool { ATRACE_FORMAT("WebViewDraw_%dx%d", mFBInfo.width(), mFBInfo.height()); EGLDisplay display = sEglManager.eglDisplay(); - LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, - "Failed to get EGL_DEFAULT_DISPLAY! err=%s", - uirenderer::renderthread::EglManager::eglErrorString()); + LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s", + uirenderer::renderthread::EglManager::eglErrorString()); // We use an EGLImage to access the content of the GraphicBuffer // The EGL image is later bound to a 2D texture EGLClientBuffer clientBuffer = (EGLClientBuffer)mFrameBuffer->getNativeBuffer(); @@ -154,10 +152,10 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) { AutoGLFramebuffer glFb; // Bind texture to the frame buffer. glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - glTexture.mTexture, 0); + glTexture.mTexture, 0); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { ALOGE("Failed framebuffer check for created target buffer: %s", - GLUtils::getGLFramebufferError()); + GLUtils::getGLFramebufferError()); return false; } @@ -166,19 +164,22 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) { glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); - (*mFunctor)(DrawGlInfo::kModeDraw, &info); + if (mAnyFunctor.index() == 0) { + std::get<0>(mAnyFunctor).handle->drawGl(info); + } else { + (*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeDraw, &info); + } EGLSyncKHR glDrawFinishedFence = eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL); LOG_ALWAYS_FATAL_IF(glDrawFinishedFence == EGL_NO_SYNC_KHR, - "Could not create sync fence %#x", eglGetError()); + "Could not create sync fence %#x", eglGetError()); glFlush(); // TODO: export EGLSyncKHR in file descr // TODO: import file desc in Vulkan Semaphore // TODO: instead block the GPU: probably by using external Vulkan semaphore. // Block the CPU until the glFlush finish. - EGLint waitStatus = eglClientWaitSyncKHR(display, glDrawFinishedFence, 0, - FENCE_TIMEOUT); + EGLint waitStatus = eglClientWaitSyncKHR(display, glDrawFinishedFence, 0, FENCE_TIMEOUT); LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR, "Failed to wait for the fence %#x", eglGetError()); eglDestroySyncKHR(display, glDrawFinishedFence); @@ -197,26 +198,25 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) { canvas->resetMatrix(); auto functorImage = SkImage::MakeFromAHardwareBuffer( - reinterpret_cast<AHardwareBuffer*>(mFrameBuffer.get()), kPremul_SkAlphaType, - nullptr, kBottomLeft_GrSurfaceOrigin); + reinterpret_cast<AHardwareBuffer*>(mFrameBuffer.get()), kPremul_SkAlphaType, nullptr, + kBottomLeft_GrSurfaceOrigin); canvas->drawImage(functorImage, 0, 0, &paint); canvas->restore(); } VkInteropFunctorDrawable::~VkInteropFunctorDrawable() { - if (mListener.get() != nullptr) { - ScopedDrawRequest _drawRequest{}; - sGLDrawThread->queue().runSync([&]() { - mListener->onGlFunctorReleased(mFunctor); - }); + if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) { + if (lp->listener) { + ScopedDrawRequest _drawRequest{}; + sGLDrawThread->queue().runSync( + [&]() { lp->listener->onGlFunctorReleased(lp->functor); }); + } } } -void VkInteropFunctorDrawable::syncFunctor() const { +void VkInteropFunctorDrawable::syncFunctor(const WebViewSyncData& data) const { ScopedDrawRequest _drawRequest{}; - sGLDrawThread->queue().runSync([&]() { - (*mFunctor)(DrawGlInfo::kModeSync, nullptr); - }); + sGLDrawThread->queue().runSync([&]() { FunctorDrawable::syncFunctor(data); }); } } // namespace skiapipeline diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h index 8fe52c5ef700..c47ee114263f 100644 --- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h +++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h @@ -18,8 +18,8 @@ #include "FunctorDrawable.h" -#include <utils/RefBase.h> #include <ui/GraphicBuffer.h> +#include <utils/RefBase.h> namespace android { namespace uirenderer { @@ -32,20 +32,18 @@ namespace skiapipeline { */ class VkInteropFunctorDrawable : public FunctorDrawable { public: - VkInteropFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, - SkCanvas* canvas) - : FunctorDrawable(functor, listener, canvas) {} - virtual ~VkInteropFunctorDrawable(); + using FunctorDrawable::FunctorDrawable; - void syncFunctor() const override; + virtual ~VkInteropFunctorDrawable(); static void vkInvokeFunctor(Functor* functor); + void syncFunctor(const WebViewSyncData& data) const override; + protected: virtual void onDraw(SkCanvas* canvas) override; private: - // Variables below describe/store temporary offscreen buffer used for Vulkan pipeline. sp<GraphicBuffer> mFrameBuffer; SkImageInfo mFBInfo; diff --git a/libs/hwui/private/hwui/WebViewFunctor.h b/libs/hwui/private/hwui/WebViewFunctor.h new file mode 100644 index 000000000000..e5346aabaee3 --- /dev/null +++ b/libs/hwui/private/hwui/WebViewFunctor.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H +#define FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H + +#include <private/hwui/DrawGlInfo.h> + +namespace android::uirenderer { + +enum class RenderMode { + OpenGL_ES, + Vulkan, +}; + +// Static for the lifetime of the process +RenderMode WebViewFunctor_queryPlatformRenderMode(); + +struct WebViewSyncData { + bool applyForceDark; +}; + +struct WebViewFunctorCallbacks { + // kModeSync, called on RenderThread + void (*onSync)(int functor, const WebViewSyncData& syncData); + + // Called when either the context is destroyed _or_ when the functor's last reference goes + // away. Will always be called with an active context and always on renderthread. + void (*onContextDestroyed)(int functor); + + // Called when the last reference to the handle goes away and the handle is considered + // irrevocably destroyed. Will always be proceeded by a call to onContextDestroyed if + // this functor had ever been drawn. + void (*onDestroyed)(int functor); + + union { + struct { + // Called on RenderThread. initialize is guaranteed to happen before this call + void (*draw)(int functor, const DrawGlInfo& params); + } gles; + // TODO: VK support. The current DrawVkInfo is monolithic and needs to be split up for + // what params are valid on what callbacks + struct { + // Called either the first time the functor is used or the first time it's used after + // a call to onContextDestroyed. + // void (*initialize)(int functor, const InitParams& params); + // void (*frameStart)(int functor, /* todo: what params are actually needed for this to + // be useful? Is this useful? */) + // void (*draw)(int functor, const CompositeParams& params /* todo: rename - composite + // almost always means something else, and we aren't compositing */); + // void (*frameEnd)(int functor, const PostCompositeParams& params /* todo: same as + // CompositeParams - rename */); + } vk; + }; +}; + +// Creates a new WebViewFunctor from the given prototype. The prototype is copied after +// this function returns. Caller retains full ownership of it. +// Returns -1 if the creation fails (such as an unsupported functorMode + platform mode combination) +int WebViewFunctor_create(const WebViewFunctorCallbacks& prototype, RenderMode functorMode); + +// May be called on any thread to signal that the functor should be destroyed. +// The functor will receive an onDestroyed when the last usage of it is released, +// and it should be considered alive & active until that point. +void WebViewFunctor_release(int functor); + +} // namespace android::uirenderer + +#endif // FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 4e4262c5c0a7..8e57a3a119e3 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -576,8 +576,7 @@ void CanvasContext::trimMemory(RenderThread& thread, int level) { ATRACE_CALL(); if (level >= TRIM_MEMORY_COMPLETE) { thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete); - thread.destroyGlContext(); - thread.vulkanManager().destroy(); + thread.destroyRenderingContext(); } else if (level >= TRIM_MEMORY_UI_HIDDEN) { thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden); } diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 085812a00f71..aa6af23d8ed3 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -30,6 +30,7 @@ #include "renderthread/RenderThread.h" #include "utils/Macros.h" #include "utils/TimeUtils.h" +#include "WebViewFunctorManager.h" #include <ui/GraphicBuffer.h> @@ -143,6 +144,14 @@ void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) { } } +void RenderProxy::destroyFunctor(int functor) { + ATRACE_CALL(); + RenderThread& thread = RenderThread::getInstance(); + thread.queue().post([=]() { + WebViewFunctorManager::instance().destroyFunctor(functor); + }); +} + DeferredLayerUpdater* RenderProxy::createTextureLayer() { return mRenderThread.queue().runSync([this]() -> auto { return mContext->createTextureLayer(); diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index d9b789f28f8d..9dc918121be6 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -82,6 +82,7 @@ public: ANDROID_API void destroy(); ANDROID_API static void invokeFunctor(Functor* functor, bool waitForCompletion); + static void destroyFunctor(int functor); ANDROID_API DeferredLayerUpdater* createTextureLayer(); ANDROID_API void buildLayer(RenderNode* node); diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 207673c1c8dd..c06faddf7fa6 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -132,6 +132,7 @@ RenderThread::RenderThread() , mFrameCallbackTaskPending(false) , mRenderState(nullptr) , mEglManager(nullptr) + , mFunctorManager(WebViewFunctorManager::instance()) , mVkManager(nullptr) { Properties::load(); start("RenderThread"); @@ -197,11 +198,13 @@ void RenderThread::requireGlContext() { setGrContext(grContext); } -void RenderThread::destroyGlContext() { +void RenderThread::destroyRenderingContext() { + mFunctorManager.onContextDestroyed(); if (mEglManager->hasEglContext()) { setGrContext(nullptr); mEglManager->destroy(); } + vulkanManager().destroy(); } void RenderThread::dumpGraphicsMemory(int fd) { diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index 2384f9541ec0..12666b323d11 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -23,6 +23,7 @@ #include "CacheManager.h" #include "TimeLord.h" #include "thread/ThreadBase.h" +#include "WebViewFunctorManager.h" #include <GrContext.h> #include <SkBitmap.h> @@ -104,7 +105,7 @@ public: void dumpGraphicsMemory(int fd); void requireGlContext(); - void destroyGlContext(); + void destroyRenderingContext(); /** * isCurrent provides a way to query, if the caller is running on @@ -151,6 +152,7 @@ private: TimeLord mTimeLord; RenderState* mRenderState; EglManager* mEglManager; + WebViewFunctorManager& mFunctorManager; ProfileDataContainer mGlobalProfileData; Readback* mReadback = nullptr; diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp index 15aec9f291a4..4a2f57e344c4 100644 --- a/libs/hwui/surfacetexture/ImageConsumer.cpp +++ b/libs/hwui/surfacetexture/ImageConsumer.cpp @@ -70,7 +70,8 @@ sk_sp<SkImage> ImageConsumer::dequeueImage(bool* queueEmpty, SurfaceTexture& st, int slot = st.mCurrentTexture; if (slot != BufferItem::INVALID_BUFFER_SLOT) { *queueEmpty = true; - mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, item.mDataSpace); + mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, + st.mCurrentDataSpace); return mImageSlots[slot].mImage; } } diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp index f81202292a49..7aa9b82e3583 100644 --- a/libs/hwui/tests/common/TestUtils.cpp +++ b/libs/hwui/tests/common/TestUtils.cpp @@ -32,6 +32,8 @@ namespace android { namespace uirenderer { +std::unordered_map<int, TestUtils::CallCounts> TestUtils::sMockFunctorCounts{}; + SkColor TestUtils::interpolateColor(float fraction, SkColor start, SkColor end) { int startA = (start >> 24) & 0xff; int startR = (start >> 16) & 0xff; @@ -82,12 +84,10 @@ void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint uint32_t length = strlen(text); SkPaint glyphPaint(paint); glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding); - canvas->drawText( - utf16.get(), length, // text buffer - 0, length, // draw range - 0, length, // context range - x, y, minikin::Bidi::LTR, - glyphPaint, nullptr, nullptr /* measured text */); + canvas->drawText(utf16.get(), length, // text buffer + 0, length, // draw range + 0, length, // context range + x, y, minikin::Bidi::LTR, glyphPaint, nullptr, nullptr /* measured text */); } void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint& paint, @@ -96,7 +96,7 @@ void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint SkPaint glyphPaint(paint); glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding); canvas->drawTextOnPath(utf16.get(), strlen(text), minikin::Bidi::LTR, path, 0, 0, glyphPaint, - nullptr); + nullptr); } void TestUtils::TestTask::run() { @@ -110,11 +110,7 @@ void TestUtils::TestTask::run() { rtCallback(renderThread); - if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { - renderThread.vulkanManager().destroy(); - } else { - renderThread.destroyGlContext(); - } + renderThread.destroyRenderingContext(); } std::unique_ptr<uint16_t[]> TestUtils::asciiToUtf16(const char* str) { diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index c5db861d4f48..5ff8993e6779 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -27,6 +27,7 @@ #include <renderstate/RenderState.h> #include <renderthread/RenderThread.h> +#include <gtest/gtest.h> #include <memory> namespace android { @@ -201,8 +202,7 @@ public: static void recordNode(RenderNode& node, std::function<void(Canvas&)> contentCallback) { std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas( - node.stagingProperties().getWidth(), node.stagingProperties().getHeight(), - &node)); + node.stagingProperties().getWidth(), node.stagingProperties().getHeight(), &node)); contentCallback(*canvas.get()); node.setStagingDisplayList(canvas->finishRecording()); } @@ -267,7 +267,14 @@ public: renderthread::RenderThread::getInstance().queue().runSync([&]() { task.run(); }); } + static void runOnRenderThreadUnmanaged(RtCallback rtCallback) { + auto& rt = renderthread::RenderThread::getInstance(); + rt.queue().runSync([&]() { rtCallback(rt); }); + } + + static bool isRenderThreadRunning() { return renderthread::RenderThread::hasInstance(); } + static pid_t getRenderThreadTid() { return renderthread::RenderThread::getInstance().getTid(); } static SkColor interpolateColor(float fraction, SkColor start, SkColor end); @@ -296,7 +303,52 @@ public: static SkRect getClipBounds(const SkCanvas* canvas); static SkRect getLocalClipBounds(const SkCanvas* canvas); + struct CallCounts { + int sync = 0; + int contextDestroyed = 0; + int destroyed = 0; + int glesDraw = 0; + }; + + static void expectOnRenderThread() { EXPECT_EQ(gettid(), TestUtils::getRenderThreadTid()); } + + static WebViewFunctorCallbacks createMockFunctor(RenderMode mode) { + auto callbacks = WebViewFunctorCallbacks{ + .onSync = + [](int functor, const WebViewSyncData& data) { + expectOnRenderThread(); + sMockFunctorCounts[functor].sync++; + }, + .onContextDestroyed = + [](int functor) { + expectOnRenderThread(); + sMockFunctorCounts[functor].contextDestroyed++; + }, + .onDestroyed = + [](int functor) { + expectOnRenderThread(); + sMockFunctorCounts[functor].destroyed++; + }, + }; + switch (mode) { + case RenderMode::OpenGL_ES: + callbacks.gles.draw = [](int functor, const DrawGlInfo& params) { + expectOnRenderThread(); + sMockFunctorCounts[functor].glesDraw++; + }; + break; + default: + ADD_FAILURE(); + return WebViewFunctorCallbacks{}; + } + return callbacks; + } + + static CallCounts& countsForFunctor(int functor) { return sMockFunctorCounts[functor]; } + private: + static std::unordered_map<int, CallCounts> sMockFunctorCounts; + static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) { MarkAndSweepRemoved observer(nullptr); node->syncProperties(); @@ -306,9 +358,9 @@ private: } auto displayList = node->getDisplayList(); if (displayList) { - for (auto&& childDr : static_cast<skiapipeline::SkiaDisplayList*>( - const_cast<DisplayList*>(displayList)) - ->mChildNodes) { + for (auto&& childDr : + static_cast<skiapipeline::SkiaDisplayList*>(const_cast<DisplayList*>(displayList)) + ->mChildNodes) { syncHierarchyPropertiesAndDisplayListImpl(childDr.getRenderNode()); } } diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp index 448408d19eb1..ec81f629ee45 100644 --- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp +++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp @@ -50,7 +50,7 @@ public: pixels[4000 + 4 * i + 3] = 255; } buffer->unlock(); - sk_sp<Bitmap> hardwareBitmap(Bitmap::createFrom(buffer)); + sk_sp<Bitmap> hardwareBitmap(Bitmap::createFrom(buffer, SkColorSpace::MakeSRGB())); sk_sp<SkShader> hardwareShader(createBitmapShader(*hardwareBitmap)); SkPoint center; diff --git a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp index 0d87776e083e..d189a9379c33 100644 --- a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp @@ -76,7 +76,7 @@ public: paint.setStrokeWidth(strokeWidth); // fill column with each op int middleCount = canvas.save(SaveFlags::MatrixClip); - for (auto op : ops) { + for (const auto& op : ops) { int innerCount = canvas.save(SaveFlags::MatrixClip); canvas.clipRect(0, 0, cellSize, cellSize, SkClipOp::kIntersect); canvas.drawColor(Color::White, SkBlendMode::kSrcOver); diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp index a6869791a915..f4c3e13b0ea6 100644 --- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp +++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp @@ -19,9 +19,9 @@ #include "tests/common/TestUtils.h" -#include <gtest/gtest.h> #include <SkBitmap.h> #include <SkImage.h> +#include <gtest/gtest.h> using namespace android; using namespace android::uirenderer; diff --git a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp index 08b967964c59..dac888cd79ca 100644 --- a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp +++ b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp @@ -39,7 +39,7 @@ public: // current thread can spoof being a GPU thread static void destroyEglContext() { if (TestUtils::isRenderThreadRunning()) { - TestUtils::runOnRenderThread([](RenderThread& thread) { thread.destroyGlContext(); }); + TestUtils::runOnRenderThread([](RenderThread& thread) { thread.destroyRenderingContext(); }); } } diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp index 0331581799b7..c813cd945905 100644 --- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp @@ -355,9 +355,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) { class ProjectionTestCanvas : public SkCanvas { public: ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {} - void onDrawRect(const SkRect& rect, const SkPaint& paint) override { - mDrawCounter++; - } + void onDrawRect(const SkRect& rect, const SkPaint& paint) override { mDrawCounter++; } int getDrawCounter() { return mDrawCounter; } @@ -370,7 +368,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) { [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { properties.setProjectionReceiver(true); }, - "B"); // a receiver with an empty display list + "B"); // a receiver with an empty display list auto projectingRipple = TestUtils::createSkiaNode( 0, 0, 100, 100, @@ -389,14 +387,14 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) { canvas.drawRenderNode(projectingRipple.get()); }, "C"); - auto parent = TestUtils::createSkiaNode( - 0, 0, 100, 100, - [&receiverBackground, &child](RenderProperties& properties, - SkiaRecordingCanvas& canvas) { - canvas.drawRenderNode(receiverBackground.get()); - canvas.drawRenderNode(child.get()); - }, - "A"); + auto parent = + TestUtils::createSkiaNode(0, 0, 100, 100, + [&receiverBackground, &child](RenderProperties& properties, + SkiaRecordingCanvas& canvas) { + canvas.drawRenderNode(receiverBackground.get()); + canvas.drawRenderNode(child.get()); + }, + "A"); ContextFactory contextFactory; std::unique_ptr<CanvasContext> canvasContext( CanvasContext::create(renderThread, false, parent.get(), &contextFactory)); @@ -1058,7 +1056,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) { public: FrameTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {} void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, - const SkPaint* paint, SrcRectConstraint constraint) override { + const SkPaint* paint, SrcRectConstraint constraint) override { mDrawCounter++; EXPECT_EQ(kLow_SkFilterQuality, paint->getFilterQuality()); } @@ -1076,7 +1074,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) { FrameTestCanvas canvas; RenderNodeDrawable drawable(layerNode.get(), &canvas, true); canvas.drawDrawable(&drawable); - EXPECT_EQ(1, canvas.mDrawCounter); //make sure the layer was composed + EXPECT_EQ(1, canvas.mDrawCounter); // make sure the layer was composed // clean up layer pointer, so we can safely destruct RenderNode layerNode->setLayerSurface(nullptr); @@ -1129,15 +1127,14 @@ TEST(ReorderBarrierDrawable, testShadowMatrix) { getTotalMatrix()); } else { // Second invocation is preparing the matrix for an elevated RenderNodeDrawable. - EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y), - matrix); - EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y), - getTotalMatrix()); + EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y), matrix); + EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y), getTotalMatrix()); } } protected: int mDrawCounter = 0; + private: bool mFirstDidConcat = true; }; @@ -1174,14 +1171,14 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaRecordingCanvas, drawVectorDrawable) { public: VectorDrawableTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {} void onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, - const SkPaint* paint, SrcRectConstraint constraint) override { + const SkPaint* paint, SrcRectConstraint constraint) override { const int index = mDrawCounter++; switch (index) { case 0: EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT)); break; case 1: - EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH/2, CANVAS_HEIGHT)); + EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH / 2, CANVAS_HEIGHT)); break; default: ADD_FAILURE(); @@ -1191,17 +1188,18 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaRecordingCanvas, drawVectorDrawable) { VectorDrawable::Group* group = new VectorDrawable::Group(); sp<VectorDrawableRoot> vectorDrawable(new VectorDrawableRoot(group)); - vectorDrawable->mutateStagingProperties()->setScaledSize(CANVAS_WIDTH/10, CANVAS_HEIGHT/10); + vectorDrawable->mutateStagingProperties()->setScaledSize(CANVAS_WIDTH / 10, CANVAS_HEIGHT / 10); - auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, - [&](RenderProperties& props, SkiaRecordingCanvas& canvas) { - vectorDrawable->mutateStagingProperties()->setBounds(SkRect::MakeWH(CANVAS_WIDTH, - CANVAS_HEIGHT)); - canvas.drawVectorDrawable(vectorDrawable.get()); - vectorDrawable->mutateStagingProperties()->setBounds(SkRect::MakeWH(CANVAS_WIDTH/2, - CANVAS_HEIGHT)); - canvas.drawVectorDrawable(vectorDrawable.get()); - }); + auto node = + TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, + [&](RenderProperties& props, SkiaRecordingCanvas& canvas) { + vectorDrawable->mutateStagingProperties()->setBounds( + SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT)); + canvas.drawVectorDrawable(vectorDrawable.get()); + vectorDrawable->mutateStagingProperties()->setBounds( + SkRect::MakeWH(CANVAS_WIDTH / 2, CANVAS_HEIGHT)); + canvas.drawVectorDrawable(vectorDrawable.get()); + }); VectorDrawableTestCanvas canvas; RenderNodeDrawable drawable(node.get(), &canvas, true); diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp index 75fb0ef0acc1..1cd9bd8ee9d9 100644 --- a/libs/hwui/tests/unit/RenderNodeTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeTests.cpp @@ -295,7 +295,8 @@ RENDERTHREAD_TEST(RenderNode, prepareTree_nullableDisplayList) { canvasContext->destroy(); } -RENDERTHREAD_TEST(RenderNode, prepareTree_HwLayer_AVD_enqueueDamage) { +// TODO: Is this supposed to work in SkiaGL/SkiaVK? +RENDERTHREAD_TEST(DISABLED_RenderNode, prepareTree_HwLayer_AVD_enqueueDamage) { VectorDrawable::Group* group = new VectorDrawable::Group(); sp<VectorDrawableRoot> vectorDrawable(new VectorDrawableRoot(group)); diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp index 1433aa0349f4..87981f115763 100644 --- a/libs/hwui/tests/unit/ShaderCacheTests.cpp +++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp @@ -14,17 +14,17 @@ * limitations under the License. */ -#include <gtest/gtest.h> -#include <dirent.h> #include <cutils/properties.h> -#include <cstdint> +#include <dirent.h> #include <errno.h> +#include <gtest/gtest.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <utils/Log.h> -#include "pipeline/skia/ShaderCache.h" +#include <cstdint> #include "FileBlobCache.h" +#include "pipeline/skia/ShaderCache.h" using namespace android::uirenderer::skiapipeline; @@ -66,7 +66,6 @@ public: } /* namespace uirenderer */ } /* namespace android */ - namespace { std::string getExternalStorageFolder() { @@ -82,14 +81,12 @@ bool folderExist(const std::string& folderName) { return false; } -inline bool -checkShader(const sk_sp<SkData>& shader1, const sk_sp<SkData>& shader2) { - return nullptr != shader1 && nullptr != shader2 && shader1->size() == shader2->size() - && 0 == memcmp(shader1->data(), shader2->data(), shader1->size()); +inline bool checkShader(const sk_sp<SkData>& shader1, const sk_sp<SkData>& shader2) { + return nullptr != shader1 && nullptr != shader2 && shader1->size() == shader2->size() && + 0 == memcmp(shader1->data(), shader2->data(), shader1->size()); } -inline bool -checkShader(const sk_sp<SkData>& shader, const char* program) { +inline bool checkShader(const sk_sp<SkData>& shader, const char* program) { sk_sp<SkData> shader2 = SkData::MakeWithCString(program); return checkShader(shader, shader2); } @@ -116,32 +113,31 @@ void genRandomData(std::vector<T>& buffer) { } } - #define GrProgramDescTest(a) (*SkData::MakeWithCString(#a).get()) TEST(ShaderCacheTest, testWriteAndRead) { if (!folderExist(getExternalStorageFolder())) { - //don't run the test if external storage folder is not available + // don't run the test if external storage folder is not available return; } - std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1"; - std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2"; + std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1"; + std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2"; - //remove any test files from previous test run + // remove any test files from previous test run int deleteFile = remove(cacheFile1.c_str()); ASSERT_TRUE(0 == deleteFile || ENOENT == errno); std::srand(0); - //read the cache from a file that does not exist + // read the cache from a file that does not exist ShaderCache::get().setFilename(cacheFile1.c_str()); - ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); //disable deferred save + ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); // disable deferred save ShaderCache::get().initShaderDiskCache(); - //read a key - should not be found since the cache is empty + // read a key - should not be found since the cache is empty sk_sp<SkData> outVS; ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>()); - //write to the in-memory cache without storing on disk and verify we read the same values + // write to the in-memory cache without storing on disk and verify we read the same values sk_sp<SkData> inVS; setShader(inVS, "sassas"); ShaderCache::get().store(GrProgramDescTest(100), *inVS.get()); @@ -152,23 +148,23 @@ TEST(ShaderCacheTest, testWriteAndRead) { ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>()); ASSERT_TRUE(checkShader(outVS, "someVS")); - //store content to disk and release in-memory cache + // store content to disk and release in-memory cache ShaderCacheTestUtils::terminate(ShaderCache::get(), true); - //change to a file that does not exist and verify load fails + // change to a file that does not exist and verify load fails ShaderCache::get().setFilename(cacheFile2.c_str()); ShaderCache::get().initShaderDiskCache(); ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>()); ShaderCacheTestUtils::terminate(ShaderCache::get(), false); - //load again content from disk from an existing file and check the data is read correctly + // load again content from disk from an existing file and check the data is read correctly ShaderCache::get().setFilename(cacheFile1.c_str()); ShaderCache::get().initShaderDiskCache(); sk_sp<SkData> outVS2; ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>()); ASSERT_TRUE(checkShader(outVS2, "someVS")); - //change data, store to disk, read back again and verify data has been changed + // change data, store to disk, read back again and verify data has been changed setShader(inVS, "ewData1"); ShaderCache::get().store(GrProgramDescTest(432), *inVS.get()); ShaderCacheTestUtils::terminate(ShaderCache::get(), true); @@ -176,9 +172,8 @@ TEST(ShaderCacheTest, testWriteAndRead) { ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>()); ASSERT_TRUE(checkShader(outVS2, "ewData1")); - - //write and read big data chunk (50K) - size_t dataSize = 50*1024; + // write and read big data chunk (50K) + size_t dataSize = 50 * 1024; std::vector<uint8_t> dataBuffer(dataSize); genRandomData(dataBuffer); setShader(inVS, dataBuffer); @@ -194,31 +189,31 @@ TEST(ShaderCacheTest, testWriteAndRead) { TEST(ShaderCacheTest, testCacheValidation) { if (!folderExist(getExternalStorageFolder())) { - //don't run the test if external storage folder is not available + // don't run the test if external storage folder is not available return; } - std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1"; - std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2"; + std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1"; + std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2"; - //remove any test files from previous test run + // remove any test files from previous test run int deleteFile = remove(cacheFile1.c_str()); ASSERT_TRUE(0 == deleteFile || ENOENT == errno); std::srand(0); - //generate identity and read the cache from a file that does not exist + // generate identity and read the cache from a file that does not exist ShaderCache::get().setFilename(cacheFile1.c_str()); - ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); //disable deferred save + ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); // disable deferred save std::vector<uint8_t> identity(1024); genRandomData(identity); - ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() * - sizeof(decltype(identity)::value_type)); + ShaderCache::get().initShaderDiskCache( + identity.data(), identity.size() * sizeof(decltype(identity)::value_type)); // generate random content in cache and store to disk constexpr size_t numBlob(10); constexpr size_t keySize(1024); constexpr size_t dataSize(50 * 1024); - std::vector< std::pair<sk_sp<SkData>, sk_sp<SkData>> > blobVec(numBlob); + std::vector<std::pair<sk_sp<SkData>, sk_sp<SkData>>> blobVec(numBlob); for (auto& blob : blobVec) { std::vector<uint8_t> keyBuffer(keySize); std::vector<uint8_t> dataBuffer(dataSize); @@ -237,47 +232,47 @@ TEST(ShaderCacheTest, testCacheValidation) { // change to a file that does not exist and verify validation fails ShaderCache::get().setFilename(cacheFile2.c_str()); ShaderCache::get().initShaderDiskCache(); - ASSERT_FALSE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) ); + ASSERT_FALSE(ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity)); ShaderCacheTestUtils::terminate(ShaderCache::get(), false); // restore the original file and verify validation succeeds ShaderCache::get().setFilename(cacheFile1.c_str()); - ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() * - sizeof(decltype(identity)::value_type)); - ASSERT_TRUE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) ); + ShaderCache::get().initShaderDiskCache( + identity.data(), identity.size() * sizeof(decltype(identity)::value_type)); + ASSERT_TRUE(ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity)); for (const auto& blob : blobVec) { auto outVS = ShaderCache::get().load(*blob.first.get()); - ASSERT_TRUE( checkShader(outVS, blob.second) ); + ASSERT_TRUE(checkShader(outVS, blob.second)); } // generate error identity and verify load fails ShaderCache::get().initShaderDiskCache(identity.data(), -1); for (const auto& blob : blobVec) { - ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() ); + ASSERT_EQ(ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>()); } - ShaderCache::get().initShaderDiskCache(nullptr, identity.size() * - sizeof(decltype(identity)::value_type)); + ShaderCache::get().initShaderDiskCache( + nullptr, identity.size() * sizeof(decltype(identity)::value_type)); for (const auto& blob : blobVec) { - ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() ); + ASSERT_EQ(ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>()); } // verify the cache validation again after load fails - ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() * - sizeof(decltype(identity)::value_type)); - ASSERT_TRUE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) ); + ShaderCache::get().initShaderDiskCache( + identity.data(), identity.size() * sizeof(decltype(identity)::value_type)); + ASSERT_TRUE(ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity)); for (const auto& blob : blobVec) { auto outVS = ShaderCache::get().load(*blob.first.get()); - ASSERT_TRUE( checkShader(outVS, blob.second) ); + ASSERT_TRUE(checkShader(outVS, blob.second)); } // generate another identity and verify load fails for (auto& data : identity) { data += std::rand(); } - ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() * - sizeof(decltype(identity)::value_type)); + ShaderCache::get().initShaderDiskCache( + identity.data(), identity.size() * sizeof(decltype(identity)::value_type)); for (const auto& blob : blobVec) { - ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() ); + ASSERT_EQ(ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>()); } ShaderCacheTestUtils::terminate(ShaderCache::get(), false); diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp index 415f9e8517ff..53bf84f13fd6 100644 --- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp +++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp @@ -100,16 +100,35 @@ TEST(SkiaDisplayList, syncContexts) { GLFunctorDrawable functorDrawable(&functor, nullptr, &dummyCanvas); skiaDL.mChildFunctors.push_back(&functorDrawable); + int functor2 = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES), + RenderMode::OpenGL_ES); + auto& counts = TestUtils::countsForFunctor(functor2); + skiaDL.mChildFunctors.push_back( + skiaDL.allocateDrawable<GLFunctorDrawable>(functor2, &dummyCanvas)); + WebViewFunctor_release(functor2); + SkRect bounds = SkRect::MakeWH(200, 200); VectorDrawableRoot vectorDrawable(new VectorDrawable::Group()); vectorDrawable.mutateStagingProperties()->setBounds(bounds); skiaDL.mVectorDrawables.push_back(&vectorDrawable); // ensure that the functor and vectorDrawable are properly synced - skiaDL.syncContents(); - - ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeSync); - ASSERT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds); + TestUtils::runOnRenderThread([&](auto&) { + skiaDL.syncContents(WebViewSyncData{ + .applyForceDark = false, + }); + }); + + EXPECT_EQ(functor.getLastMode(), DrawGlInfo::kModeSync); + EXPECT_EQ(counts.sync, 1); + EXPECT_EQ(counts.destroyed, 0); + EXPECT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds); + + skiaDL.reset(); + TestUtils::runOnRenderThread([](auto&) { + // Fence + }); + EXPECT_EQ(counts.destroyed, 1); } class ContextFactory : public IContextFactory { diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index d16b8be89e20..3c06dab36fe4 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -24,10 +24,10 @@ #include "DamageAccumulator.h" #include "IContextFactory.h" #include "SkiaCanvas.h" -#include "pipeline/skia/SkiaUtils.h" #include "pipeline/skia/SkiaDisplayList.h" #include "pipeline/skia/SkiaOpenGLPipeline.h" #include "pipeline/skia/SkiaRecordingCanvas.h" +#include "pipeline/skia/SkiaUtils.h" #include "renderthread/CanvasContext.h" #include "tests/common/TestUtils.h" @@ -51,8 +51,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) { auto surface = SkSurface::MakeRasterN32Premul(1, 1); surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, - surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED); } @@ -84,8 +83,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, testOnPrepareTree) { ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); // drawFrame will crash if "SkiaPipeline::onPrepareTree" did not clean invalid VD pointer - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, - surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED); } @@ -106,12 +104,10 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) { auto surface = SkSurface::MakeRasterN32Premul(2, 2); surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, - surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, contentDrawBounds, - surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned int)SK_ColorTRANSPARENT); ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN); } @@ -130,8 +126,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) { auto surface = SkSurface::MakeRasterN32Premul(2, 2); surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, - surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); ASSERT_EQ(TestUtils::getColor(surface, 1, 0), SK_ColorBLUE); ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorRED); @@ -203,38 +198,32 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderOverdraw) { ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); // Single draw, should be white. - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, - surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE); // 1 Overdraw, should be blue blended onto white. renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, - surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0d0ff); // 2 Overdraw, should be green blended onto white renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, - surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0ffd0); // 3 Overdraw, should be pink blended onto white. renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, - surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffffc0c0); // 4 Overdraw, should be red blended onto white. renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, - surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080); // 5 Overdraw, should be red blended onto white. renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, - surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080); } @@ -389,7 +378,6 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) { EXPECT_FALSE(pipeline->isSurfaceReady()); EXPECT_TRUE(pipeline->setSurface((Surface*)0x01, SwapBehavior::kSwap_default, ColorMode::SRGB)); EXPECT_TRUE(pipeline->isSurfaceReady()); - renderThread.destroyGlContext(); + renderThread.destroyRenderingContext(); EXPECT_FALSE(pipeline->isSurfaceReady()); } - diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp index b645aeb55074..1a09b1c52d8a 100644 --- a/libs/hwui/tests/unit/TypefaceTests.cpp +++ b/libs/hwui/tests/unit/TypefaceTests.cpp @@ -54,9 +54,9 @@ std::shared_ptr<minikin::FontFamily> buildFamily(const char* fileName) { sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); sk_sp<SkTypeface> typeface(fm->makeFromStream(std::move(fontData))); LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName); - std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>( - std::move(typeface), data, st.st_size, fileName, 0, - std::vector<minikin::FontVariation>()); + std::shared_ptr<minikin::MinikinFont> font = + std::make_shared<MinikinFontSkia>(std::move(typeface), data, st.st_size, fileName, 0, + std::vector<minikin::FontVariation>()); std::vector<minikin::Font> fonts; fonts.push_back(minikin::Font::Builder(font).build()); return std::make_shared<minikin::FontFamily>(std::move(fonts)); diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp index ee6beba847a0..5db002862fcd 100644 --- a/libs/hwui/tests/unit/VectorDrawableTests.cpp +++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp @@ -85,8 +85,10 @@ const static TestData sTestDataSet[] = { outPath->rCubicTo(8.0, 8.0, 8.0, 8.0, 8.0, 8.0); outPath->cubicTo(16.0, 16.0, 9.0, 9.0, 9.0, 9.0); outPath->rCubicTo(0.0, 0.0, 9.0, 9.0, 9.0, 9.0); - outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 10.0, 10.0); - outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 20.0, 20.0); + outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 10.0, + 10.0); + outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 20.0, + 20.0); }}, // Check box VectorDrawable path data @@ -157,7 +159,8 @@ const static TestData sTestDataSet[] = { }, [](SkPath* outPath) { outPath->moveTo(300.0, 70.0); - outPath->arcTo(230.0, 230.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCCW_Direction, 301.0, 70.0); + outPath->arcTo(230.0, 230.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCCW_Direction, + 301.0, 70.0); outPath->close(); outPath->moveTo(300.0, 70.0); }}, @@ -236,14 +239,14 @@ struct StringPath { }; const StringPath sStringPaths[] = { - {"3e...3", false}, // Not starting with a verb and ill-formatted float - {"L.M.F.A.O", false}, // No floats following verbs - {"m 1 1", true}, // Valid path data - {"\n \t z", true}, // Valid path data with leading spaces - {"1-2e34567", false}, // Not starting with a verb and ill-formatted float - {"f 4 5", false}, // Invalid verb - {"\r ", false}, // Empty string - {"L1,0 L1,1 L0,1 z M1000", false} // Not enough floats following verb M. + {"3e...3", false}, // Not starting with a verb and ill-formatted float + {"L.M.F.A.O", false}, // No floats following verbs + {"m 1 1", true}, // Valid path data + {"\n \t z", true}, // Valid path data with leading spaces + {"1-2e34567", false}, // Not starting with a verb and ill-formatted float + {"f 4 5", false}, // Invalid verb + {"\r ", false}, // Empty string + {"L1,0 L1,1 L0,1 z M1000", false} // Not enough floats following verb M. }; static bool hasSameVerbs(const PathData& from, const PathData& to) { @@ -251,7 +254,7 @@ static bool hasSameVerbs(const PathData& from, const PathData& to) { } TEST(PathParser, parseStringForData) { - for (TestData testData : sTestDataSet) { + for (const TestData& testData : sTestDataSet) { PathParser::ParseResult result; // Test generated path data against the given data. PathData pathData; @@ -271,7 +274,7 @@ TEST(PathParser, parseStringForData) { } TEST(VectorDrawableUtils, createSkPathFromPathData) { - for (TestData testData : sTestDataSet) { + for (const TestData& testData : sTestDataSet) { SkPath expectedPath; testData.skPathLamda(&expectedPath); SkPath actualPath; @@ -281,7 +284,7 @@ TEST(VectorDrawableUtils, createSkPathFromPathData) { } TEST(PathParser, parseAsciiStringForSkPath) { - for (TestData testData : sTestDataSet) { + for (const TestData& testData : sTestDataSet) { PathParser::ParseResult result; size_t length = strlen(testData.pathString); // Check the return value as well as the SkPath generated. @@ -304,8 +307,8 @@ TEST(PathParser, parseAsciiStringForSkPath) { } TEST(VectorDrawableUtils, morphPathData) { - for (TestData fromData : sTestDataSet) { - for (TestData toData : sTestDataSet) { + for (const TestData& fromData : sTestDataSet) { + for (const TestData& toData : sTestDataSet) { bool canMorph = VectorDrawableUtils::canMorph(fromData.pathData, toData.pathData); if (fromData.pathData == toData.pathData) { EXPECT_TRUE(canMorph); @@ -319,8 +322,8 @@ TEST(VectorDrawableUtils, morphPathData) { TEST(VectorDrawableUtils, interpolatePathData) { // Interpolate path data with itself and every other path data - for (TestData fromData : sTestDataSet) { - for (TestData toData : sTestDataSet) { + for (const TestData& fromData : sTestDataSet) { + for (const TestData& toData : sTestDataSet) { PathData outData; bool success = VectorDrawableUtils::interpolatePathData(&outData, fromData.pathData, toData.pathData, 0.5); @@ -331,7 +334,7 @@ TEST(VectorDrawableUtils, interpolatePathData) { float fractions[] = {0, 0.00001, 0.28, 0.5, 0.7777, 0.9999999, 1}; // Now try to interpolate with a slightly modified version of self and expect success - for (TestData fromData : sTestDataSet) { + for (const TestData& fromData : sTestDataSet) { PathData toPathData = fromData.pathData; for (size_t i = 0; i < toPathData.points.size(); i++) { toPathData.points[i]++; diff --git a/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp new file mode 100644 index 000000000000..c8169aff1c5e --- /dev/null +++ b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include "WebViewFunctorManager.h" +#include "private/hwui/WebViewFunctor.h" +#include "renderthread/RenderProxy.h" +#include "tests/common/TestUtils.h" + +#include <unordered_map> + +using namespace android; +using namespace android::uirenderer; + +TEST(WebViewFunctor, createDestroyGLES) { + int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES), + RenderMode::OpenGL_ES); + ASSERT_NE(-1, functor); + WebViewFunctor_release(functor); + TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) { + // Empty, don't care + }); + auto& counts = TestUtils::countsForFunctor(functor); + // We never initialized, so contextDestroyed == 0 + EXPECT_EQ(0, counts.contextDestroyed); + EXPECT_EQ(1, counts.destroyed); +} + +TEST(WebViewFunctor, createSyncHandleGLES) { + int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES), + RenderMode::OpenGL_ES); + ASSERT_NE(-1, functor); + auto handle = WebViewFunctorManager::instance().handleFor(functor); + ASSERT_TRUE(handle); + WebViewFunctor_release(functor); + EXPECT_FALSE(WebViewFunctorManager::instance().handleFor(functor)); + TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) { + // fence + }); + auto& counts = TestUtils::countsForFunctor(functor); + EXPECT_EQ(0, counts.sync); + EXPECT_EQ(0, counts.contextDestroyed); + EXPECT_EQ(0, counts.destroyed); + + TestUtils::runOnRenderThreadUnmanaged([&](auto&) { + WebViewSyncData syncData; + handle->sync(syncData); + }); + + EXPECT_EQ(1, counts.sync); + + TestUtils::runOnRenderThreadUnmanaged([&](auto&) { + WebViewSyncData syncData; + handle->sync(syncData); + }); + + EXPECT_EQ(2, counts.sync); + + handle.clear(); + + TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) { + // fence + }); + + EXPECT_EQ(2, counts.sync); + EXPECT_EQ(0, counts.contextDestroyed); + EXPECT_EQ(1, counts.destroyed); +} + +TEST(WebViewFunctor, createSyncDrawGLES) { + int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES), + RenderMode::OpenGL_ES); + ASSERT_NE(-1, functor); + auto handle = WebViewFunctorManager::instance().handleFor(functor); + ASSERT_TRUE(handle); + WebViewFunctor_release(functor); + auto& counts = TestUtils::countsForFunctor(functor); + for (int i = 0; i < 5; i++) { + TestUtils::runOnRenderThreadUnmanaged([&](auto&) { + WebViewSyncData syncData; + handle->sync(syncData); + DrawGlInfo drawInfo; + handle->drawGl(drawInfo); + handle->drawGl(drawInfo); + }); + } + handle.clear(); + TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) { + // fence + }); + EXPECT_EQ(5, counts.sync); + EXPECT_EQ(10, counts.glesDraw); + EXPECT_EQ(1, counts.contextDestroyed); + EXPECT_EQ(1, counts.destroyed); +} + +TEST(WebViewFunctor, contextDestroyed) { + int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES), + RenderMode::OpenGL_ES); + ASSERT_NE(-1, functor); + auto handle = WebViewFunctorManager::instance().handleFor(functor); + ASSERT_TRUE(handle); + WebViewFunctor_release(functor); + auto& counts = TestUtils::countsForFunctor(functor); + TestUtils::runOnRenderThreadUnmanaged([&](auto&) { + WebViewSyncData syncData; + handle->sync(syncData); + DrawGlInfo drawInfo; + handle->drawGl(drawInfo); + }); + EXPECT_EQ(1, counts.sync); + EXPECT_EQ(1, counts.glesDraw); + EXPECT_EQ(0, counts.contextDestroyed); + EXPECT_EQ(0, counts.destroyed); + TestUtils::runOnRenderThreadUnmanaged([](auto& rt) { + rt.destroyRenderingContext(); + }); + EXPECT_EQ(1, counts.sync); + EXPECT_EQ(1, counts.glesDraw); + EXPECT_EQ(1, counts.contextDestroyed); + EXPECT_EQ(0, counts.destroyed); + TestUtils::runOnRenderThreadUnmanaged([&](auto&) { + WebViewSyncData syncData; + handle->sync(syncData); + DrawGlInfo drawInfo; + handle->drawGl(drawInfo); + }); + EXPECT_EQ(2, counts.sync); + EXPECT_EQ(2, counts.glesDraw); + EXPECT_EQ(1, counts.contextDestroyed); + EXPECT_EQ(0, counts.destroyed); + handle.clear(); + TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) { + // fence + }); + EXPECT_EQ(2, counts.sync); + EXPECT_EQ(2, counts.glesDraw); + EXPECT_EQ(2, counts.contextDestroyed); + EXPECT_EQ(1, counts.destroyed); +}
\ No newline at end of file diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp index aecceb3609f5..63d15403b607 100644 --- a/libs/hwui/tests/unit/main.cpp +++ b/libs/hwui/tests/unit/main.cpp @@ -17,14 +17,14 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "Properties.h" #include "debug/GlesDriver.h" #include "debug/NullGlesDriver.h" #include "hwui/Typeface.h" -#include "Properties.h" #include "tests/common/LeakChecker.h" -#include "thread/TaskProcessor.h" #include "thread/Task.h" #include "thread/TaskManager.h" +#include "thread/TaskProcessor.h" #include <signal.h> diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h index 4daccda78e23..4473ce632b1b 100644 --- a/libs/hwui/utils/Color.h +++ b/libs/hwui/utils/Color.h @@ -17,6 +17,7 @@ #define COLOR_H #include <math.h> +#include <cutils/compiler.h> #include <system/graphics.h> #include <ui/PixelFormat.h> @@ -117,7 +118,7 @@ bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace); android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType); -sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace); +ANDROID_API sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace); struct Lab { float L; diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index 80d8e72a87e2..0a90f85cda0e 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -89,6 +89,10 @@ PointerController::PointerController(const sp<PointerControllerPolicyInterface>& mLocked.animationPending = false; + mLocked.displayWidth = -1; + mLocked.displayHeight = -1; + mLocked.displayOrientation = DISPLAY_ORIENTATION_0; + mLocked.presentation = PRESENTATION_POINTER; mLocked.presentationChanged = false; @@ -106,6 +110,15 @@ PointerController::PointerController(const sp<PointerControllerPolicyInterface>& mLocked.lastFrameUpdatedTime = 0; mLocked.buttonState = 0; + + mPolicy->loadPointerIcon(&mLocked.pointerIcon); + + loadResources(); + + if (mLocked.pointerIcon.isValid()) { + mLocked.pointerIconChanged = true; + updatePointerLocked(); + } } PointerController::~PointerController() { @@ -131,15 +144,23 @@ bool PointerController::getBounds(float* outMinX, float* outMinY, bool PointerController::getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const { - - if (!mLocked.viewport.isValid()) { + if (mLocked.displayWidth <= 0 || mLocked.displayHeight <= 0) { return false; } - *outMinX = mLocked.viewport.logicalLeft; - *outMinY = mLocked.viewport.logicalTop; - *outMaxX = mLocked.viewport.logicalRight - 1; - *outMaxY = mLocked.viewport.logicalBottom - 1; + *outMinX = 0; + *outMinY = 0; + switch (mLocked.displayOrientation) { + case DISPLAY_ORIENTATION_90: + case DISPLAY_ORIENTATION_270: + *outMaxX = mLocked.displayHeight - 1; + *outMaxY = mLocked.displayWidth - 1; + break; + default: + *outMaxX = mLocked.displayWidth - 1; + *outMaxY = mLocked.displayHeight - 1; + break; + } return true; } @@ -210,12 +231,6 @@ void PointerController::getPosition(float* outX, float* outY) const { *outY = mLocked.pointerY; } -int32_t PointerController::getDisplayId() const { - AutoMutex _l(mLock); - - return mLocked.viewport.displayId; -} - void PointerController::fade(Transition transition) { AutoMutex _l(mLock); @@ -340,57 +355,48 @@ void PointerController::setInactivityTimeout(InactivityTimeout inactivityTimeout void PointerController::reloadPointerResources() { AutoMutex _l(mLock); - loadResourcesLocked(); - updatePointerLocked(); -} + loadResources(); -/** - * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation, - * so here we are getting the dimensions in the original, unrotated orientation (orientation 0). - */ -static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) { - if (viewport.orientation == DISPLAY_ORIENTATION_90 - || viewport.orientation == DISPLAY_ORIENTATION_270) { - width = viewport.deviceHeight; - height = viewport.deviceWidth; - } else { - width = viewport.deviceWidth; - height = viewport.deviceHeight; + if (mLocked.presentation == PRESENTATION_POINTER) { + mLocked.additionalMouseResources.clear(); + mLocked.animationResources.clear(); + mPolicy->loadPointerIcon(&mLocked.pointerIcon); + mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, + &mLocked.animationResources); } + + mLocked.presentationChanged = true; + updatePointerLocked(); } -void PointerController::setDisplayViewport(const DisplayViewport& viewport) { +void PointerController::setDisplayViewport(int32_t width, int32_t height, int32_t orientation) { AutoMutex _l(mLock); - if (viewport == mLocked.viewport) { - return; - } - const DisplayViewport oldViewport = mLocked.viewport; - mLocked.viewport = viewport; - - int32_t oldDisplayWidth, oldDisplayHeight; - getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight); - int32_t newDisplayWidth, newDisplayHeight; - getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight); + // Adjust to use the display's unrotated coordinate frame. + if (orientation == DISPLAY_ORIENTATION_90 + || orientation == DISPLAY_ORIENTATION_270) { + int32_t temp = height; + height = width; + width = temp; + } - // Reset cursor position to center if size or display changed. - if (oldViewport.displayId != viewport.displayId - || oldDisplayWidth != newDisplayWidth - || oldDisplayHeight != newDisplayHeight) { + if (mLocked.displayWidth != width || mLocked.displayHeight != height) { + mLocked.displayWidth = width; + mLocked.displayHeight = height; float minX, minY, maxX, maxY; if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) { mLocked.pointerX = (minX + maxX) * 0.5f; mLocked.pointerY = (minY + maxY) * 0.5f; - // Reload icon resources for density may be changed. - loadResourcesLocked(); } else { mLocked.pointerX = 0; mLocked.pointerY = 0; } fadeOutAndReleaseAllSpotsLocked(); - } else if (oldViewport.orientation != viewport.orientation) { + } + + if (mLocked.displayOrientation != orientation) { // Apply offsets to convert from the pixel top-left corner position to the pixel center. // This creates an invariant frame of reference that we can easily rotate when // taking into account that the pointer may be located at fractional pixel offsets. @@ -399,37 +405,37 @@ void PointerController::setDisplayViewport(const DisplayViewport& viewport) { float temp; // Undo the previous rotation. - switch (oldViewport.orientation) { + switch (mLocked.displayOrientation) { case DISPLAY_ORIENTATION_90: temp = x; - x = oldViewport.deviceHeight - y; + x = mLocked.displayWidth - y; y = temp; break; case DISPLAY_ORIENTATION_180: - x = oldViewport.deviceWidth - x; - y = oldViewport.deviceHeight - y; + x = mLocked.displayWidth - x; + y = mLocked.displayHeight - y; break; case DISPLAY_ORIENTATION_270: temp = x; x = y; - y = oldViewport.deviceWidth - temp; + y = mLocked.displayHeight - temp; break; } // Perform the new rotation. - switch (viewport.orientation) { + switch (orientation) { case DISPLAY_ORIENTATION_90: temp = x; x = y; - y = viewport.deviceHeight - temp; + y = mLocked.displayWidth - temp; break; case DISPLAY_ORIENTATION_180: - x = viewport.deviceWidth - x; - y = viewport.deviceHeight - y; + x = mLocked.displayWidth - x; + y = mLocked.displayHeight - y; break; case DISPLAY_ORIENTATION_270: temp = x; - x = viewport.deviceWidth - y; + x = mLocked.displayHeight - y; y = temp; break; } @@ -438,6 +444,7 @@ void PointerController::setDisplayViewport(const DisplayViewport& viewport) { // and save the results. mLocked.pointerX = x - 0.5f; mLocked.pointerY = y - 0.5f; + mLocked.displayOrientation = orientation; } updatePointerLocked(); @@ -607,16 +614,11 @@ void PointerController::removeInactivityTimeoutLocked() { mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT); } -void PointerController::updatePointerLocked() REQUIRES(mLock) { - if (!mLocked.viewport.isValid()) { - return; - } - +void PointerController::updatePointerLocked() { mSpriteController->openTransaction(); mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER); mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY); - mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId); if (mLocked.pointerAlpha > 0) { mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha); @@ -727,18 +729,8 @@ void PointerController::fadeOutAndReleaseAllSpotsLocked() { } } -void PointerController::loadResourcesLocked() REQUIRES(mLock) { +void PointerController::loadResources() { mPolicy->loadPointerResources(&mResources); - - if (mLocked.presentation == PRESENTATION_POINTER) { - mLocked.additionalMouseResources.clear(); - mLocked.animationResources.clear(); - mPolicy->loadPointerIcon(&mLocked.pointerIcon); - mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, - &mLocked.animationResources); - } - - mLocked.pointerIconChanged = true; } diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index a32cc42a3342..7f4e5a59c9b6 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -23,7 +23,6 @@ #include <vector> #include <ui/DisplayInfo.h> -#include <input/DisplayViewport.h> #include <input/Input.h> #include <PointerControllerInterface.h> #include <utils/BitSet.h> @@ -97,7 +96,6 @@ public: virtual int32_t getButtonState() const; virtual void setPosition(float x, float y); virtual void getPosition(float* outX, float* outY) const; - virtual int32_t getDisplayId() const; virtual void fade(Transition transition); virtual void unfade(Transition transition); @@ -108,7 +106,7 @@ public: void updatePointerIcon(int32_t iconId); void setCustomPointerIcon(const SpriteIcon& icon); - void setDisplayViewport(const DisplayViewport& viewport); + void setDisplayViewport(int32_t width, int32_t height, int32_t orientation); void setInactivityTimeout(InactivityTimeout inactivityTimeout); void reloadPointerResources(); @@ -158,7 +156,9 @@ private: size_t animationFrameIndex; nsecs_t lastFrameUpdatedTime; - DisplayViewport viewport; + int32_t displayWidth; + int32_t displayHeight; + int32_t displayOrientation; InactivityTimeout inactivityTimeout; @@ -182,7 +182,7 @@ private: Vector<Spot*> spots; Vector<sp<Sprite> > recycledSprites; - } mLocked GUARDED_BY(mLock); + } mLocked; bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; void setPositionLocked(float x, float y); @@ -207,7 +207,7 @@ private: void fadeOutAndReleaseSpotLocked(Spot* spot); void fadeOutAndReleaseAllSpotsLocked(); - void loadResourcesLocked(); + void loadResources(); }; } // namespace android diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp index c1868d3a94d6..eb2bc98ec9e9 100644 --- a/libs/input/SpriteController.cpp +++ b/libs/input/SpriteController.cpp @@ -144,16 +144,13 @@ void SpriteController::doUpdateSprites() { } } - // Resize and/or reparent sprites if needed. + // Resize sprites if needed. SurfaceComposerClient::Transaction t; bool needApplyTransaction = false; for (size_t i = 0; i < numSprites; i++) { SpriteUpdate& update = updates.editItemAt(i); - if (update.state.surfaceControl == nullptr) { - continue; - } - if (update.state.wantSurfaceVisible()) { + if (update.state.surfaceControl != NULL && update.state.wantSurfaceVisible()) { int32_t desiredWidth = update.state.icon.bitmap.width(); int32_t desiredHeight = update.state.icon.bitmap.height(); if (update.state.surfaceWidth < desiredWidth @@ -173,12 +170,6 @@ void SpriteController::doUpdateSprites() { } } } - - // If surface is a new one, we have to set right layer stack. - if (update.surfaceChanged || update.state.dirty & DIRTY_DISPLAY_ID) { - t.setLayerStack(update.state.surfaceControl, update.state.displayId); - needApplyTransaction = true; - } } if (needApplyTransaction) { t.apply(); @@ -245,7 +236,7 @@ void SpriteController::doUpdateSprites() { if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden || (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA | DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER - | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID))))) { + | DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) { needApplyTransaction = true; if (wantSurfaceVisibleAndDrawn @@ -454,15 +445,6 @@ void SpriteController::SpriteImpl::setTransformationMatrix( } } -void SpriteController::SpriteImpl::setDisplayId(int32_t displayId) { - AutoMutex _l(mController->mLock); - - if (mLocked.state.displayId != displayId) { - mLocked.state.displayId = displayId; - invalidateLocked(DIRTY_DISPLAY_ID); - } -} - void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) { bool wasDirty = mLocked.state.dirty; mLocked.state.dirty |= dirty; diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h index 5b216f50d113..31e43e9b99e5 100644 --- a/libs/input/SpriteController.h +++ b/libs/input/SpriteController.h @@ -125,9 +125,6 @@ public: /* Sets the sprite transformation matrix. */ virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix) = 0; - - /* Sets the id of the display where the sprite should be shown. */ - virtual void setDisplayId(int32_t displayId) = 0; }; /* @@ -173,7 +170,6 @@ private: DIRTY_LAYER = 1 << 4, DIRTY_VISIBILITY = 1 << 5, DIRTY_HOTSPOT = 1 << 6, - DIRTY_DISPLAY_ID = 1 << 7, }; /* Describes the state of a sprite. @@ -184,7 +180,7 @@ private: struct SpriteState { inline SpriteState() : dirty(0), visible(false), - positionX(0), positionY(0), layer(0), alpha(1.0f), displayId(ADISPLAY_ID_DEFAULT), + positionX(0), positionY(0), layer(0), alpha(1.0f), surfaceWidth(0), surfaceHeight(0), surfaceDrawn(false), surfaceVisible(false) { } @@ -197,7 +193,6 @@ private: int32_t layer; float alpha; SpriteTransformationMatrix transformationMatrix; - int32_t displayId; sp<SurfaceControl> surfaceControl; int32_t surfaceWidth; @@ -230,7 +225,6 @@ private: virtual void setLayer(int32_t layer); virtual void setAlpha(float alpha); virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix); - virtual void setDisplayId(int32_t displayId); inline const SpriteState& getStateLocked() const { return mLocked.state; diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java index 00a393a902b4..d656fa359826 100644 --- a/media/java/android/media/MediaMetadataRetriever.java +++ b/media/java/android/media/MediaMetadataRetriever.java @@ -40,8 +40,7 @@ import java.util.Map; * MediaMetadataRetriever class provides a unified interface for retrieving * frame and meta data from an input media file. */ -public class MediaMetadataRetriever -{ +public class MediaMetadataRetriever implements AutoCloseable { static { System.loadLibrary("media_jni"); native_init(); @@ -672,6 +671,11 @@ public class MediaMetadataRetriever @UnsupportedAppUsage private native byte[] getEmbeddedPicture(int pictureType); + @Override + public void close() { + release(); + } + /** * Call it when one is done with the object. This method releases the memory * allocated internally. diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 18d36eb1f753..0057875ec3f4 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -19,7 +19,6 @@ package android.media; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.app.ActivityThread; import android.content.ContentProvider; @@ -1680,37 +1679,6 @@ public class MediaPlayer extends PlayerBase public native boolean isPlaying(); /** - * Gets the current buffering management params used by the source component. - * Calling it only after {@code setDataSource} has been called. - * Each type of data source might have different set of default params. - * - * @return the current buffering management params used by the source component. - * @throws IllegalStateException if the internal player engine has not been - * initialized, or {@code setDataSource} has not been called. - * @hide - */ - @NonNull - @TestApi - public native BufferingParams getBufferingParams(); - - /** - * Sets buffering management params. - * The object sets its internal BufferingParams to the input, except that the input is - * invalid or not supported. - * Call it only after {@code setDataSource} has been called. - * The input is a hint to MediaPlayer. - * - * @param params the buffering management params. - * - * @throws IllegalStateException if the internal player engine has not been - * initialized or has been released, or {@code setDataSource} has not been called. - * @throws IllegalArgumentException if params is invalid or not supported. - * @hide - */ - @TestApi - public native void setBufferingParams(@NonNull BufferingParams params); - - /** * Change playback speed of audio by resampling the audio. * <p> * Specifies resampling as audio mode for variable rate playback, i.e., diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java index d4b1c7f868cb..b137ce2cda0f 100644 --- a/media/java/android/media/MediaPlayer2.java +++ b/media/java/android/media/MediaPlayer2.java @@ -544,32 +544,55 @@ public class MediaPlayer2 implements AutoCloseable public native long getCurrentPosition(); /** - * Gets the duration of the file. + * Gets the duration of the dsd. * + * @param dsd the descriptor of data source of which you want to get duration * @return the duration in milliseconds, if no duration is available * (for example, if streaming live content), -1 is returned. + * @throws NullPointerException if dsd is null */ - public native long getDuration(); + public long getDuration(@NonNull DataSourceDesc dsd) { + if (dsd == null) { + throw new NullPointerException("non-null dsd is expected"); + } + SourceInfo sourceInfo = getSourceInfo(dsd); + if (sourceInfo == null) { + return -1; + } + + return native_getDuration(sourceInfo.mId); + } + + private native long native_getDuration(long srcId); /** - * Gets the current buffered media source position received through progressive downloading. + * Gets the buffered media source position of given dsd. * For example a buffering update of 8000 milliseconds when 5000 milliseconds of the content * has already been played indicates that the next 3000 milliseconds of the * content to play has been buffered. * + * @param dsd the descriptor of data source of which you want to get buffered position * @return the current buffered media source position in milliseconds + * @throws NullPointerException if dsd is null */ - public long getBufferedPosition() { + public long getBufferedPosition(@NonNull DataSourceDesc dsd) { + if (dsd == null) { + throw new NullPointerException("non-null dsd is expected"); + } + SourceInfo sourceInfo = getSourceInfo(dsd); + if (sourceInfo == null) { + return 0; + } + // Use cached buffered percent for now. - int bufferedPercentage; - synchronized (mSrcLock) { - if (mCurrentSourceInfo == null) { - bufferedPercentage = 0; - } else { - bufferedPercentage = mCurrentSourceInfo.mBufferedPercentage.get(); - } + int bufferedPercentage = sourceInfo.mBufferedPercentage.get(); + + long duration = getDuration(dsd); + if (duration < 0) { + duration = 0; } - return getDuration() * bufferedPercentage / 100; + + return duration * bufferedPercentage / 100; } /** @@ -1467,7 +1490,6 @@ public class MediaPlayer2 implements AutoCloseable private native PersistableBundle native_getMetrics(); - /** * Gets the current buffering management params used by the source component. * Calling it only after {@code setDataSource} has been called. @@ -1505,7 +1527,6 @@ public class MediaPlayer2 implements AutoCloseable private native void native_setBufferingParams(@NonNull BufferingParams params); - /** * Sets playback rate using {@link PlaybackParams}. The object sets its internal * PlaybackParams to the input. This allows the object to resume at previous speed @@ -1969,19 +1990,31 @@ public class MediaPlayer2 implements AutoCloseable /** * Returns a List of track information. * + * @param dsd the descriptor of data source of which you want to get track info * @return List of track info. The total number of tracks is the array length. * Must be called again if an external timed text source has been added after * addTimedTextSource method is called. * @throws IllegalStateException if it is called in an invalid state. + * @throws NullPointerException if dsd is null */ - public @NonNull List<TrackInfo> getTrackInfo() { - TrackInfo[] trackInfo = getInbandTrackInfo(); + + public @NonNull List<TrackInfo> getTrackInfo(@NonNull DataSourceDesc dsd) { + if (dsd == null) { + throw new NullPointerException("non-null dsd is expected"); + } + SourceInfo sourceInfo = getSourceInfo(dsd); + if (sourceInfo == null) { + return new ArrayList<TrackInfo>(0); + } + + TrackInfo[] trackInfo = getInbandTrackInfo(sourceInfo); return (trackInfo != null ? Arrays.asList(trackInfo) : new ArrayList<TrackInfo>(0)); } - private TrackInfo[] getInbandTrackInfo() throws IllegalStateException { + private TrackInfo[] getInbandTrackInfo(SourceInfo sourceInfo) throws IllegalStateException { PlayerMessage request = PlayerMessage.newBuilder() .addValues(Value.newBuilder().setInt32Value(INVOKE_ID_GET_TRACK_INFO)) + .addValues(Value.newBuilder().setInt64Value(sourceInfo.mId)) .build(); PlayerMessage response = invoke(request); if (response == null) { @@ -2001,9 +2034,10 @@ public class MediaPlayer2 implements AutoCloseable /** * Returns the index of the audio, video, or subtitle track currently selected for playback, - * The return value is an index into the array returned by {@link #getTrackInfo()}, and can - * be used in calls to {@link #selectTrack(int)} or {@link #deselectTrack(int)}. + * The return value is an index into the array returned by {@link #getTrackInfo}, and can + * be used in calls to {@link #selectTrack} or {@link #deselectTrack}. * + * @param dsd the descriptor of data source of which you want to get selected track * @param trackType should be one of {@link TrackInfo#MEDIA_TRACK_TYPE_VIDEO}, * {@link TrackInfo#MEDIA_TRACK_TYPE_AUDIO}, or * {@link TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE} @@ -2011,14 +2045,24 @@ public class MediaPlayer2 implements AutoCloseable * a negative integer is returned when there is no selected track for {@code trackType} or * when {@code trackType} is not one of audio, video, or subtitle. * @throws IllegalStateException if called after {@link #close()} + * @throws NullPointerException if dsd is null * - * @see #getTrackInfo() - * @see #selectTrack(int) - * @see #deselectTrack(int) + * @see #getTrackInfo + * @see #selectTrack + * @see #deselectTrack */ - public int getSelectedTrack(int trackType) { + public int getSelectedTrack(@NonNull DataSourceDesc dsd, int trackType) { + if (dsd == null) { + throw new NullPointerException("non-null dsd is expected"); + } + SourceInfo sourceInfo = getSourceInfo(dsd); + if (sourceInfo == null) { + return -1; + } + PlayerMessage request = PlayerMessage.newBuilder() .addValues(Value.newBuilder().setInt32Value(INVOKE_ID_GET_SELECTED_TRACK)) + .addValues(Value.newBuilder().setInt64Value(sourceInfo.mId)) .addValues(Value.newBuilder().setInt32Value(trackType)) .build(); PlayerMessage response = invoke(request); @@ -2049,19 +2093,20 @@ public class MediaPlayer2 implements AutoCloseable * In addition, the support for selecting an audio track at runtime is pretty limited * in that an audio track can only be selected in the <em>Prepared</em> state. * </p> + * @param dsd the descriptor of data source of which you want to select track * @param index the index of the track to be selected. The valid range of the index * is 0..total number of track - 1. The total number of tracks as well as the type of - * each individual track can be found by calling {@link #getTrackInfo()} method. + * each individual track can be found by calling {@link #getTrackInfo} method. * @return a token which can be used to cancel the operation later with {@link #cancelCommand}. * * @see MediaPlayer2#getTrackInfo */ // This is an asynchronous call. - public Object selectTrack(int index) { + public Object selectTrack(@NonNull DataSourceDesc dsd, int index) { return addTask(new Task(CALL_COMPLETED_SELECT_TRACK, false) { @Override void process() { - selectOrDeselectTrack(index, true /* select */); + selectOrDeselectTrack(dsd, index, true /* select */); } }); } @@ -2073,28 +2118,37 @@ public class MediaPlayer2 implements AutoCloseable * deselected. If the timed text track identified by index has not been * selected before, it throws an exception. * </p> + * @param dsd the descriptor of data source of which you want to deselect track * @param index the index of the track to be deselected. The valid range of the index * is 0..total number of tracks - 1. The total number of tracks as well as the type of - * each individual track can be found by calling {@link #getTrackInfo()} method. + * each individual track can be found by calling {@link #getTrackInfo} method. * @return a token which can be used to cancel the operation later with {@link #cancelCommand}. * * @see MediaPlayer2#getTrackInfo */ // This is an asynchronous call. - public Object deselectTrack(int index) { + public Object deselectTrack(@NonNull DataSourceDesc dsd, int index) { return addTask(new Task(CALL_COMPLETED_DESELECT_TRACK, false) { @Override void process() { - selectOrDeselectTrack(index, false /* select */); + selectOrDeselectTrack(dsd, index, false /* select */); } }); } - private void selectOrDeselectTrack(int index, boolean select) - throws IllegalStateException { + private void selectOrDeselectTrack(@NonNull DataSourceDesc dsd, int index, boolean select) { + if (dsd == null) { + throw new IllegalArgumentException("non-null dsd is expected"); + } + SourceInfo sourceInfo = getSourceInfo(dsd); + if (sourceInfo == null) { + return; + } + PlayerMessage request = PlayerMessage.newBuilder() .addValues(Value.newBuilder().setInt32Value( select ? INVOKE_ID_SELECT_TRACK : INVOKE_ID_DESELECT_TRACK)) + .addValues(Value.newBuilder().setInt64Value(sourceInfo.mId)) .addValues(Value.newBuilder().setInt32Value(index)) .build(); invoke(request); @@ -2568,7 +2622,7 @@ public class MediaPlayer2 implements AutoCloseable * Currently only HTTP live streaming data URI's embedded with timed ID3 tags generates * {@link TimedMetaData}. * - * @see MediaPlayer2#selectTrack(int) + * @see MediaPlayer2#selectTrack * @see MediaPlayer2.OnTimedMetaDataAvailableListener * @see TimedMetaData * diff --git a/media/java/android/media/MediaPlayerBase.java b/media/java/android/media/MediaPlayerBase.java deleted file mode 100644 index a4265525fb6b..000000000000 --- a/media/java/android/media/MediaPlayerBase.java +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.media; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.List; -import java.util.concurrent.Executor; - -/** - * @hide - * Base class for all media players that want media session. - */ -public abstract class MediaPlayerBase implements AutoCloseable { - /** - * @hide - */ - @IntDef({ - PLAYER_STATE_IDLE, - PLAYER_STATE_PAUSED, - PLAYER_STATE_PLAYING, - PLAYER_STATE_ERROR }) - @Retention(RetentionPolicy.SOURCE) - public @interface PlayerState {} - - /** - * @hide - */ - @IntDef({ - BUFFERING_STATE_UNKNOWN, - BUFFERING_STATE_BUFFERING_AND_PLAYABLE, - BUFFERING_STATE_BUFFERING_AND_STARVED, - BUFFERING_STATE_BUFFERING_COMPLETE }) - @Retention(RetentionPolicy.SOURCE) - public @interface BuffState {} - - /** - * State when the player is idle, and needs configuration to start playback. - */ - public static final int PLAYER_STATE_IDLE = 0; - - /** - * State when the player's playback is paused - */ - public static final int PLAYER_STATE_PAUSED = 1; - - /** - * State when the player's playback is ongoing - */ - public static final int PLAYER_STATE_PLAYING = 2; - - /** - * State when the player is in error state and cannot be recovered self. - */ - public static final int PLAYER_STATE_ERROR = 3; - - /** - * Buffering state is unknown. - */ - public static final int BUFFERING_STATE_UNKNOWN = 0; - - /** - * Buffering state indicating the player is buffering but enough has been buffered - * for this player to be able to play the content. - * See {@link #getBufferedPosition()} for how far is buffered already. - */ - public static final int BUFFERING_STATE_BUFFERING_AND_PLAYABLE = 1; - - /** - * Buffering state indicating the player is buffering, but the player is currently starved - * for data, and cannot play. - */ - public static final int BUFFERING_STATE_BUFFERING_AND_STARVED = 2; - - /** - * Buffering state indicating the player is done buffering, and the remainder of the content is - * available for playback. - */ - public static final int BUFFERING_STATE_BUFFERING_COMPLETE = 3; - - /** - * Starts or resumes playback. - */ - public abstract void play(); - - /** - * Prepares the player for playback. - * See {@link PlayerEventCallback#onMediaPrepared(MediaPlayerBase, DataSourceDesc)} for being - * notified when the preparation phase completed. During this time, the player may allocate - * resources required to play, such as audio and video decoders. - */ - public abstract void prepare(); - - /** - * Pauses playback. - */ - public abstract void pause(); - - /** - * Resets the MediaPlayerBase to its uninitialized state. - */ - public abstract void reset(); - - /** - * - */ - public abstract void skipToNext(); - - /** - * Moves the playback head to the specified position - * @param pos the new playback position expressed in ms. - */ - public abstract void seekTo(long pos); - - public static final long UNKNOWN_TIME = -1; - - /** - * Gets the current playback head position. - * @return the current playback position in ms, or {@link #UNKNOWN_TIME} if unknown. - */ - public long getCurrentPosition() { return UNKNOWN_TIME; } - - /** - * Returns the duration of the current data source, or {@link #UNKNOWN_TIME} if unknown. - * @return the duration in ms, or {@link #UNKNOWN_TIME}. - */ - public long getDuration() { return UNKNOWN_TIME; } - - /** - * Gets the buffered position of current playback, or {@link #UNKNOWN_TIME} if unknown. - * @return the buffered position in ms, or {@link #UNKNOWN_TIME}. - */ - public long getBufferedPosition() { return UNKNOWN_TIME; } - - /** - * Returns the current player state. - * See also {@link PlayerEventCallback#onPlayerStateChanged(MediaPlayerBase, int)} for - * notification of changes. - * @return the current player state - */ - public abstract @PlayerState int getPlayerState(); - - /** - * Returns the current buffering state of the player. - * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already - * buffered. - * @return the buffering state. - */ - public abstract @BuffState int getBufferingState(); - - /** - * Sets the {@link AudioAttributes} to be used during the playback of the media. - * - * @param attributes non-null <code>AudioAttributes</code>. - */ - public abstract void setAudioAttributes(@NonNull AudioAttributes attributes); - - /** - * Returns AudioAttributes that media player has. - */ - public abstract @Nullable AudioAttributes getAudioAttributes(); - - /** - * Sets the data source to be played. - * @param dsd - */ - public abstract void setDataSource(@NonNull DataSourceDesc dsd); - - /** - * Sets the data source that will be played immediately after the current one is done playing. - * @param dsd - */ - public abstract void setNextDataSource(@NonNull DataSourceDesc dsd); - - /** - * Sets the list of data sources that will be sequentially played after the current one. Each - * data source is played immediately after the previous one is done playing. - * @param dsds - */ - public abstract void setNextDataSources(@NonNull List<DataSourceDesc> dsds); - - /** - * Returns the current data source. - * @return the current data source, or null if none is set, or none available to play. - */ - public abstract @Nullable DataSourceDesc getCurrentDataSource(); - - /** - * Configures the player to loop on the current data source. - * @param loop true if the current data source is meant to loop. - */ - public abstract void loopCurrent(boolean loop); - - /** - * Sets the playback speed. - * A value of 1.0f is the default playback value. - * A negative value indicates reverse playback, check {@link #isReversePlaybackSupported()} - * before using negative values.<br> - * After changing the playback speed, it is recommended to query the actual speed supported - * by the player, see {@link #getPlaybackSpeed()}. - * @param speed - */ - public abstract void setPlaybackSpeed(float speed); - - /** - * Returns the actual playback speed to be used by the player when playing. - * Note that it may differ from the speed set in {@link #setPlaybackSpeed(float)}. - * @return the actual playback speed - */ - public float getPlaybackSpeed() { return 1.0f; } - - /** - * Indicates whether reverse playback is supported. - * Reverse playback is indicated by negative playback speeds, see - * {@link #setPlaybackSpeed(float)}. - * @return true if reverse playback is supported. - */ - public boolean isReversePlaybackSupported() { return false; } - - /** - * Sets the volume of the audio of the media to play, expressed as a linear multiplier - * on the audio samples. - * Note that this volume is specific to the player, and is separate from stream volume - * used across the platform.<br> - * A value of 0.0f indicates muting, a value of 1.0f is the nominal unattenuated and unamplified - * gain. See {@link #getMaxPlayerVolume()} for the volume range supported by this player. - * @param volume a value between 0.0f and {@link #getMaxPlayerVolume()}. - */ - public abstract void setPlayerVolume(float volume); - - /** - * Returns the current volume of this player to this player. - * Note that it does not take into account the associated stream volume. - * @return the player volume. - */ - public abstract float getPlayerVolume(); - - /** - * @return the maximum volume that can be used in {@link #setPlayerVolume(float)}. - */ - public float getMaxPlayerVolume() { return 1.0f; } - - /** - * Adds a callback to be notified of events for this player. - * @param e the {@link Executor} to be used for the events. - * @param cb the callback to receive the events. - */ - public abstract void registerPlayerEventCallback(@NonNull Executor e, - @NonNull PlayerEventCallback cb); - - /** - * Removes a previously registered callback for player events - * @param cb the callback to remove - */ - public abstract void unregisterPlayerEventCallback(@NonNull PlayerEventCallback cb); - - /** - * A callback class to receive notifications for events on the media player. - * See {@link MediaPlayerBase#registerPlayerEventCallback(Executor, PlayerEventCallback)} to - * register this callback. - */ - public static abstract class PlayerEventCallback { - /** - * Called when the player's current data source has changed. - * - * @param mpb the player whose data source changed. - * @param dsd the new current data source. null, if no more data sources available. - */ - public void onCurrentDataSourceChanged(@NonNull MediaPlayerBase mpb, - @Nullable DataSourceDesc dsd) { } - /** - * Called when the player is <i>prepared</i>, i.e. it is ready to play the content - * referenced by the given data source. - * @param mpb the player that is prepared. - * @param dsd the data source that the player is prepared to play. - */ - public void onMediaPrepared(@NonNull MediaPlayerBase mpb, @NonNull DataSourceDesc dsd) { } - - /** - * Called to indicate that the state of the player has changed. - * See {@link MediaPlayerBase#getPlayerState()} for polling the player state. - * @param mpb the player whose state has changed. - * @param state the new state of the player. - */ - public void onPlayerStateChanged(@NonNull MediaPlayerBase mpb, @PlayerState int state) { } - - /** - * Called to report buffering events for a data source. - * @param mpb the player that is buffering - * @param dsd the data source for which buffering is happening. - * @param state the new buffering state. - */ - public void onBufferingStateChanged(@NonNull MediaPlayerBase mpb, - @NonNull DataSourceDesc dsd, @BuffState int state) { } - - /** - * Called to indicate that the playback speed has changed. - * @param mpb the player that has changed the playback speed. - * @param speed the new playback speed. - */ - public void onPlaybackSpeedChanged(@NonNull MediaPlayerBase mpb, float speed) { } - - /** - * Called to indicate that {@link #seekTo(long)} is completed. - * - * @param mpb the player that has completed seeking. - * @param position the previous seeking request. - * @see #seekTo(long) - */ - public void onSeekCompleted(@NonNull MediaPlayerBase mpb, long position) { } - } - -} diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java index fd1406078e7a..f07076ad14aa 100644 --- a/media/java/android/media/ThumbnailUtils.java +++ b/media/java/android/media/ThumbnailUtils.java @@ -16,33 +16,53 @@ package android.media; +import static android.media.MediaMetadataRetriever.METADATA_KEY_DURATION; +import static android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT; +import static android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH; +import static android.media.MediaMetadataRetriever.OPTION_CLOSEST_SYNC; +import static android.os.Environment.MEDIA_UNKNOWN; + +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.content.ContentResolver; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; +import android.graphics.ImageDecoder; +import android.graphics.ImageDecoder.ImageInfo; +import android.graphics.ImageDecoder.Source; import android.graphics.Matrix; +import android.graphics.Point; import android.graphics.Rect; import android.net.Uri; +import android.os.CancellationSignal; +import android.os.Environment; import android.os.ParcelFileDescriptor; -import android.provider.MediaStore.Images; +import android.provider.MediaStore.ThumbnailConstants; import android.util.Log; +import android.util.Size; -import java.io.FileDescriptor; -import java.io.FileInputStream; +import com.android.internal.util.ArrayUtils; + +import libcore.io.IoUtils; + +import java.io.File; import java.io.IOException; +import java.util.Arrays; +import java.util.Comparator; +import java.util.function.ToIntFunction; /** - * Thumbnail generation routines for media provider. + * Utilities for generating visual thumbnails from files. */ - public class ThumbnailUtils { private static final String TAG = "ThumbnailUtils"; - /* Maximum pixels size for created bitmap. */ - private static final int MAX_NUM_PIXELS_THUMBNAIL = 512 * 384; - private static final int MAX_NUM_PIXELS_MICRO_THUMBNAIL = 160 * 120; - private static final int UNCONSTRAINED = -1; + /** @hide */ + @Deprecated + @UnsupportedAppUsage + public static final int TARGET_SIZE_MICRO_THUMBNAIL = 96; /* Options used internally. */ private static final int OPTIONS_NONE = 0x0; @@ -54,153 +74,252 @@ public class ThumbnailUtils { */ public static final int OPTIONS_RECYCLE_INPUT = 0x2; + private static Size convertKind(int kind) { + if (kind == ThumbnailConstants.MICRO_KIND) { + return Point.convert(ThumbnailConstants.MICRO_SIZE); + } else if (kind == ThumbnailConstants.FULL_SCREEN_KIND) { + return Point.convert(ThumbnailConstants.FULL_SCREEN_SIZE); + } else if (kind == ThumbnailConstants.MINI_KIND) { + return Point.convert(ThumbnailConstants.MINI_SIZE); + } else { + throw new IllegalArgumentException("Unsupported kind: " + kind); + } + } + + private static class Resizer implements ImageDecoder.OnHeaderDecodedListener { + private final Size size; + private final CancellationSignal signal; + + public Resizer(Size size, CancellationSignal signal) { + this.size = size; + this.signal = signal; + } + + @Override + public void onHeaderDecoded(ImageDecoder decoder, ImageInfo info, Source source) { + // One last-ditch check to see if we've been canceled. + if (signal != null) signal.throwIfCanceled(); + + // We don't know how clients will use the decoded data, so we have + // to default to the more flexible "software" option. + decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); + + // We requested a rough thumbnail size, but the remote size may have + // returned something giant, so defensively scale down as needed. + final int widthSample = info.getSize().getWidth() / size.getWidth(); + final int heightSample = info.getSize().getHeight() / size.getHeight(); + final int sample = Math.max(widthSample, heightSample); + if (sample > 1) { + decoder.setTargetSampleSize(sample); + } + } + } + /** - * Constant used to indicate the dimension of mini thumbnail. - * @hide Only used by media framework and media provider internally. + * Create a thumbnail for given audio file. + * + * @param filePath The audio file. + * @param kind The desired thumbnail kind, such as + * {@link android.provider.MediaStore.Images.Thumbnails#MINI_KIND}. */ - public static final int TARGET_SIZE_MINI_THUMBNAIL = 320; + @Deprecated + public static @Nullable Bitmap createAudioThumbnail(@NonNull String filePath, int kind) { + try { + return createAudioThumbnail(new File(filePath), convertKind(kind), null); + } catch (IOException e) { + Log.w(TAG, e); + return null; + } + } /** - * Constant used to indicate the dimension of micro thumbnail. - * @hide Only used by media framework and media provider internally. + * Create a thumbnail for given audio file. + * + * @param file The audio file. + * @param size The desired thumbnail size. + * @throws IOException If any trouble was encountered while generating or + * loading the thumbnail, or if + * {@link CancellationSignal#cancel()} was invoked. */ - @UnsupportedAppUsage - public static final int TARGET_SIZE_MICRO_THUMBNAIL = 96; + public static @NonNull Bitmap createAudioThumbnail(@NonNull File file, @NonNull Size size, + @Nullable CancellationSignal signal) throws IOException { + // Checkpoint before going deeper + if (signal != null) signal.throwIfCanceled(); + + final Resizer resizer = new Resizer(size, signal); + try (MediaMetadataRetriever retriever = new MediaMetadataRetriever()) { + retriever.setDataSource(file.getAbsolutePath()); + final byte[] raw = retriever.getEmbeddedPicture(); + if (raw != null) { + return ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer); + } + } catch (RuntimeException e) { + throw new IOException("Failed to create thumbnail", e); + } + + // Only poke around for files on external storage + if (MEDIA_UNKNOWN.equals(Environment.getExternalStorageState(file))) { + throw new IOException("No embedded album art found"); + } + + // Ignore "Downloads" or top-level directories + final File parent = file.getParentFile(); + final File grandParent = parent != null ? parent.getParentFile() : null; + if (parent != null + && parent.getName().equals(Environment.DIRECTORY_DOWNLOADS)) { + throw new IOException("No thumbnails in Downloads directories"); + } + if (grandParent != null + && MEDIA_UNKNOWN.equals(Environment.getExternalStorageState(grandParent))) { + throw new IOException("No thumbnails in top-level directories"); + } + + // If no embedded image found, look around for best standalone file + final File[] found = ArrayUtils + .defeatNullable(file.getParentFile().listFiles((dir, name) -> { + final String lower = name.toLowerCase(); + return (lower.endsWith(".jpg") || lower.endsWith(".png")); + })); + + final ToIntFunction<File> score = (f) -> { + final String lower = f.getName().toLowerCase(); + if (lower.equals("albumart.jpg")) return 4; + if (lower.startsWith("albumart") && lower.endsWith(".jpg")) return 3; + if (lower.contains("albumart") && lower.endsWith(".jpg")) return 2; + if (lower.endsWith(".jpg")) return 1; + return 0; + }; + final Comparator<File> bestScore = (a, b) -> { + return score.applyAsInt(a) - score.applyAsInt(b); + }; + + final File bestFile = Arrays.asList(found).stream().max(bestScore).orElse(null); + if (bestFile == null) { + throw new IOException("No album art found"); + } + + // Checkpoint before going deeper + if (signal != null) signal.throwIfCanceled(); + + return ImageDecoder.decodeBitmap(ImageDecoder.createSource(bestFile), resizer); + } /** - * This method first examines if the thumbnail embedded in EXIF is bigger than our target - * size. If not, then it'll create a thumbnail from original image. Due to efficiency - * consideration, we want to let MediaThumbRequest avoid calling this method twice for - * both kinds, so it only requests for MICRO_KIND and set saveImage to true. - * - * This method always returns a "square thumbnail" for MICRO_KIND thumbnail. + * Create a thumbnail for given image file. * - * @param filePath the path of image file - * @param kind could be MINI_KIND or MICRO_KIND - * @return Bitmap, or null on failures + * @param filePath The image file. + * @param kind The desired thumbnail kind, such as + * {@link android.provider.MediaStore.Images.Thumbnails#MINI_KIND}. + */ + @Deprecated + public static @Nullable Bitmap createImageThumbnail(@NonNull String filePath, int kind) { + try { + return createImageThumbnail(new File(filePath), convertKind(kind), null); + } catch (IOException e) { + Log.w(TAG, e); + return null; + } + } + + /** + * Create a thumbnail for given image file. * - * @hide This method is only used by media framework and media provider internally. + * @param file The audio file. + * @param size The desired thumbnail size. + * @throws IOException If any trouble was encountered while generating or + * loading the thumbnail, or if + * {@link CancellationSignal#cancel()} was invoked. */ - @UnsupportedAppUsage - public static Bitmap createImageThumbnail(String filePath, int kind) { - boolean wantMini = (kind == Images.Thumbnails.MINI_KIND); - int targetSize = wantMini - ? TARGET_SIZE_MINI_THUMBNAIL - : TARGET_SIZE_MICRO_THUMBNAIL; - int maxPixels = wantMini - ? MAX_NUM_PIXELS_THUMBNAIL - : MAX_NUM_PIXELS_MICRO_THUMBNAIL; - SizedThumbnailBitmap sizedThumbnailBitmap = new SizedThumbnailBitmap(); - Bitmap bitmap = null; - String mimeType = MediaFile.getMimeTypeForFile(filePath); + public static @NonNull Bitmap createImageThumbnail(@NonNull File file, @NonNull Size size, + @Nullable CancellationSignal signal) throws IOException { + // Checkpoint before going deeper + if (signal != null) signal.throwIfCanceled(); + + final Resizer resizer = new Resizer(size, signal); + final String mimeType = MediaFile.getMimeTypeForFile(file.getName()); if (mimeType.equals("image/heif") || mimeType.equals("image/heif-sequence") || mimeType.equals("image/heic") || mimeType.equals("image/heic-sequence")) { - bitmap = createThumbnailFromMetadataRetriever(filePath, targetSize, maxPixels); + try (MediaMetadataRetriever retriever = new MediaMetadataRetriever()) { + retriever.setDataSource(file.getAbsolutePath()); + return retriever.getThumbnailImageAtIndex(-1, + new MediaMetadataRetriever.BitmapParams(), size.getWidth(), + size.getWidth() * size.getHeight()); + } catch (RuntimeException e) { + throw new IOException("Failed to create thumbnail", e); + } } else if (MediaFile.isExifMimeType(mimeType)) { - createThumbnailFromEXIF(filePath, targetSize, maxPixels, sizedThumbnailBitmap); - bitmap = sizedThumbnailBitmap.mBitmap; + final ExifInterface exif = new ExifInterface(file); + final byte[] raw = exif.getThumbnailBytes(); + if (raw != null) { + return ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer); + } } - if (bitmap == null) { - FileInputStream stream = null; - try { - stream = new FileInputStream(filePath); - FileDescriptor fd = stream.getFD(); - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inSampleSize = 1; - options.inJustDecodeBounds = true; - BitmapFactory.decodeFileDescriptor(fd, null, options); - if (options.mCancel || options.outWidth == -1 - || options.outHeight == -1) { - return null; - } - options.inSampleSize = computeSampleSize( - options, targetSize, maxPixels); - options.inJustDecodeBounds = false; - - options.inDither = false; - options.inPreferredConfig = Bitmap.Config.ARGB_8888; - bitmap = BitmapFactory.decodeFileDescriptor(fd, null, options); - } catch (IOException ex) { - Log.e(TAG, "", ex); - } catch (OutOfMemoryError oom) { - Log.e(TAG, "Unable to decode file " + filePath + ". OutOfMemoryError.", oom); - } finally { - try { - if (stream != null) { - stream.close(); - } - } catch (IOException ex) { - Log.e(TAG, "", ex); - } - } + // Checkpoint before going deeper + if (signal != null) signal.throwIfCanceled(); - } + return ImageDecoder.decodeBitmap(ImageDecoder.createSource(file), resizer); + } - if (kind == Images.Thumbnails.MICRO_KIND) { - // now we make it a "square thumbnail" for MICRO_KIND thumbnail - bitmap = extractThumbnail(bitmap, - TARGET_SIZE_MICRO_THUMBNAIL, - TARGET_SIZE_MICRO_THUMBNAIL, OPTIONS_RECYCLE_INPUT); + /** + * Create a thumbnail for given video file. + * + * @param filePath The video file. + * @param kind The desired thumbnail kind, such as + * {@link android.provider.MediaStore.Images.Thumbnails#MINI_KIND}. + */ + @Deprecated + public static @Nullable Bitmap createVideoThumbnail(@NonNull String filePath, int kind) { + try { + return createVideoThumbnail(new File(filePath), convertKind(kind), null); + } catch (IOException e) { + Log.w(TAG, e); + return null; } - return bitmap; } /** - * Create a video thumbnail for a video. May return null if the video is - * corrupt or the format is not supported. + * Create a thumbnail for given video file. * - * @param filePath the path of video file - * @param kind could be MINI_KIND or MICRO_KIND + * @param file The video file. + * @param size The desired thumbnail size. + * @throws IOException If any trouble was encountered while generating or + * loading the thumbnail, or if + * {@link CancellationSignal#cancel()} was invoked. */ - public static Bitmap createVideoThumbnail(String filePath, int kind) { - Bitmap bitmap = null; - MediaMetadataRetriever retriever = new MediaMetadataRetriever(); - try { - retriever.setDataSource(filePath); - // First retrieve album art in metadata if set. - byte[] embeddedPicture = retriever.getEmbeddedPicture(); - if (embeddedPicture != null && embeddedPicture.length > 0) { - bitmap = BitmapFactory.decodeByteArray(embeddedPicture, 0, embeddedPicture.length); + public static @NonNull Bitmap createVideoThumbnail(@NonNull File file, @NonNull Size size, + @Nullable CancellationSignal signal) throws IOException { + // Checkpoint before going deeper + if (signal != null) signal.throwIfCanceled(); + + final Resizer resizer = new Resizer(size, signal); + try (MediaMetadataRetriever mmr = new MediaMetadataRetriever()) { + mmr.setDataSource(file.getAbsolutePath()); + + // Try to retrieve thumbnail from metadata + final byte[] raw = mmr.getEmbeddedPicture(); + if (raw != null) { + return ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer); } - // Fall back to first frame of the video. - if (bitmap == null) { - bitmap = retriever.getFrameAtTime(-1); - } - } catch (IllegalArgumentException ex) { - // Assume this is a corrupt video file - } catch (RuntimeException ex) { - // Assume this is a corrupt video file. - } finally { - try { - retriever.release(); - } catch (RuntimeException ex) { - // Ignore failures while cleaning up. - } - } - if (bitmap == null) return null; - - if (kind == Images.Thumbnails.MINI_KIND) { - // Scale down the bitmap if it's too large. - int width = bitmap.getWidth(); - int height = bitmap.getHeight(); - int max = Math.max(width, height); - if (max > 512) { - float scale = 512f / max; - int w = Math.round(scale * width); - int h = Math.round(scale * height); - bitmap = Bitmap.createScaledBitmap(bitmap, w, h, true); + // Fall back to middle of video + final int width = Integer.parseInt(mmr.extractMetadata(METADATA_KEY_VIDEO_WIDTH)); + final int height = Integer.parseInt(mmr.extractMetadata(METADATA_KEY_VIDEO_HEIGHT)); + final long duration = Long.parseLong(mmr.extractMetadata(METADATA_KEY_DURATION)); + + // If we're okay with something larger than native format, just + // return a frame without up-scaling it + if (size.getWidth() > width && size.getHeight() > height) { + return mmr.getFrameAtTime(duration / 2, OPTION_CLOSEST_SYNC); + } else { + return mmr.getScaledFrameAtTime(duration / 2, OPTION_CLOSEST_SYNC, + size.getWidth(), size.getHeight()); } - } else if (kind == Images.Thumbnails.MICRO_KIND) { - bitmap = extractThumbnail(bitmap, - TARGET_SIZE_MICRO_THUMBNAIL, - TARGET_SIZE_MICRO_THUMBNAIL, - OPTIONS_RECYCLE_INPUT); + } catch (RuntimeException e) { + throw new IOException("Failed to create thumbnail", e); } - return bitmap; } /** @@ -242,122 +361,27 @@ public class ThumbnailUtils { return thumbnail; } - /* - * Compute the sample size as a function of minSideLength - * and maxNumOfPixels. - * minSideLength is used to specify that minimal width or height of a - * bitmap. - * maxNumOfPixels is used to specify the maximal size in pixels that is - * tolerable in terms of memory usage. - * - * The function returns a sample size based on the constraints. - * Both size and minSideLength can be passed in as IImage.UNCONSTRAINED, - * which indicates no care of the corresponding constraint. - * The functions prefers returning a sample size that - * generates a smaller bitmap, unless minSideLength = IImage.UNCONSTRAINED. - * - * Also, the function rounds up the sample size to a power of 2 or multiple - * of 8 because BitmapFactory only honors sample size this way. - * For example, BitmapFactory downsamples an image by 2 even though the - * request is 3. So we round up the sample size to avoid OOM. - */ + @Deprecated @UnsupportedAppUsage private static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) { - int initialSize = computeInitialSampleSize(options, minSideLength, - maxNumOfPixels); - - int roundedSize; - if (initialSize <= 8 ) { - roundedSize = 1; - while (roundedSize < initialSize) { - roundedSize <<= 1; - } - } else { - roundedSize = (initialSize + 7) / 8 * 8; - } - - return roundedSize; + return 1; } + @Deprecated @UnsupportedAppUsage private static int computeInitialSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) { - double w = options.outWidth; - double h = options.outHeight; - - int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 : - (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels)); - int upperBound = (minSideLength == UNCONSTRAINED) ? 128 : - (int) Math.min(Math.floor(w / minSideLength), - Math.floor(h / minSideLength)); - - if (upperBound < lowerBound) { - // return the larger one when there is no overlapping zone. - return lowerBound; - } - - if ((maxNumOfPixels == UNCONSTRAINED) && - (minSideLength == UNCONSTRAINED)) { - return 1; - } else if (minSideLength == UNCONSTRAINED) { - return lowerBound; - } else { - return upperBound; - } - } - - /** - * Make a bitmap from a given Uri, minimal side length, and maximum number of pixels. - * The image data will be read from specified pfd if it's not null, otherwise - * a new input stream will be created using specified ContentResolver. - * - * Clients are allowed to pass their own BitmapFactory.Options used for bitmap decoding. A - * new BitmapFactory.Options will be created if options is null. - */ - private static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels, - Uri uri, ContentResolver cr, ParcelFileDescriptor pfd, - BitmapFactory.Options options) { - Bitmap b = null; - try { - if (pfd == null) pfd = makeInputStream(uri, cr); - if (pfd == null) return null; - if (options == null) options = new BitmapFactory.Options(); - - FileDescriptor fd = pfd.getFileDescriptor(); - options.inSampleSize = 1; - options.inJustDecodeBounds = true; - BitmapFactory.decodeFileDescriptor(fd, null, options); - if (options.mCancel || options.outWidth == -1 - || options.outHeight == -1) { - return null; - } - options.inSampleSize = computeSampleSize( - options, minSideLength, maxNumOfPixels); - options.inJustDecodeBounds = false; - - options.inDither = false; - options.inPreferredConfig = Bitmap.Config.ARGB_8888; - b = BitmapFactory.decodeFileDescriptor(fd, null, options); - } catch (OutOfMemoryError ex) { - Log.e(TAG, "Got oom exception ", ex); - return null; - } finally { - closeSilently(pfd); - } - return b; + return 1; } + @Deprecated @UnsupportedAppUsage private static void closeSilently(ParcelFileDescriptor c) { - if (c == null) return; - try { - c.close(); - } catch (Throwable t) { - // do nothing - } + IoUtils.closeQuietly(c); } + @Deprecated @UnsupportedAppUsage private static ParcelFileDescriptor makeInputStream( Uri uri, ContentResolver cr) { @@ -371,6 +395,7 @@ public class ThumbnailUtils { /** * Transform source Bitmap to targeted width and height. */ + @Deprecated @UnsupportedAppUsage private static Bitmap transform(Matrix scaler, Bitmap source, @@ -468,14 +493,7 @@ public class ThumbnailUtils { return b2; } - /** - * SizedThumbnailBitmap contains the bitmap, which is downsampled either from - * the thumbnail in exif or the full image. - * mThumbnailData, mThumbnailWidth and mThumbnailHeight are set together only if mThumbnail - * is not null. - * - * The width/height of the sized bitmap may be different from mThumbnailWidth/mThumbnailHeight. - */ + @Deprecated private static class SizedThumbnailBitmap { public byte[] mThumbnailData; public Bitmap mBitmap; @@ -483,81 +501,9 @@ public class ThumbnailUtils { public int mThumbnailHeight; } - /** - * Creates a bitmap by either downsampling from the thumbnail in EXIF or the full image. - * The functions returns a SizedThumbnailBitmap, - * which contains a downsampled bitmap and the thumbnail data in EXIF if exists. - */ + @Deprecated @UnsupportedAppUsage private static void createThumbnailFromEXIF(String filePath, int targetSize, int maxPixels, SizedThumbnailBitmap sizedThumbBitmap) { - if (filePath == null) return; - - ExifInterface exif = null; - byte [] thumbData = null; - try { - exif = new ExifInterface(filePath); - thumbData = exif.getThumbnail(); - } catch (IOException ex) { - Log.w(TAG, ex); - } - - BitmapFactory.Options fullOptions = new BitmapFactory.Options(); - BitmapFactory.Options exifOptions = new BitmapFactory.Options(); - int exifThumbWidth = 0; - int fullThumbWidth = 0; - - // Compute exifThumbWidth. - if (thumbData != null) { - exifOptions.inJustDecodeBounds = true; - BitmapFactory.decodeByteArray(thumbData, 0, thumbData.length, exifOptions); - exifOptions.inSampleSize = computeSampleSize(exifOptions, targetSize, maxPixels); - exifThumbWidth = exifOptions.outWidth / exifOptions.inSampleSize; - } - - // Compute fullThumbWidth. - fullOptions.inJustDecodeBounds = true; - BitmapFactory.decodeFile(filePath, fullOptions); - fullOptions.inSampleSize = computeSampleSize(fullOptions, targetSize, maxPixels); - fullThumbWidth = fullOptions.outWidth / fullOptions.inSampleSize; - - // Choose the larger thumbnail as the returning sizedThumbBitmap. - if (thumbData != null && exifThumbWidth >= fullThumbWidth) { - int width = exifOptions.outWidth; - int height = exifOptions.outHeight; - exifOptions.inJustDecodeBounds = false; - sizedThumbBitmap.mBitmap = BitmapFactory.decodeByteArray(thumbData, 0, - thumbData.length, exifOptions); - if (sizedThumbBitmap.mBitmap != null) { - sizedThumbBitmap.mThumbnailData = thumbData; - sizedThumbBitmap.mThumbnailWidth = width; - sizedThumbBitmap.mThumbnailHeight = height; - } - } else { - fullOptions.inJustDecodeBounds = false; - sizedThumbBitmap.mBitmap = BitmapFactory.decodeFile(filePath, fullOptions); - } - } - - private static Bitmap createThumbnailFromMetadataRetriever( - String filePath, int targetSize, int maxPixels) { - if (filePath == null) { - return null; - } - Bitmap thumbnail = null; - MediaMetadataRetriever retriever = new MediaMetadataRetriever(); - try { - retriever.setDataSource(filePath); - MediaMetadataRetriever.BitmapParams params = new MediaMetadataRetriever.BitmapParams(); - params.setPreferredConfig(Bitmap.Config.ARGB_8888); - thumbnail = retriever.getThumbnailImageAtIndex(-1, params, targetSize, maxPixels); - } catch (RuntimeException ex) { - // Assume this is a corrupt video file. - } finally { - if (retriever != null) { - retriever.release(); - } - } - return thumbnail; } } diff --git a/media/jni/Android.bp b/media/jni/Android.bp index e25e6a5e735f..7481fff74765 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -97,9 +97,10 @@ cc_library_shared { shared_libs: [ "android.hardware.cas@1.0", // for CasManager. VNDK??? "android.hardware.cas.native@1.0", // CasManager. VNDK??? + "android.hidl.allocator@1.0", + "libhidlmemory", "libbinder", "libgui", // for VideoFrameScheduler - "libhidlallocatorutils", "libhidlbase", // VNDK??? "libpowermanager", // for JWakeLock. to be removed diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index 5dd01b03274a..76bbce7f0d87 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -39,7 +39,6 @@ #include "utils/Errors.h" // for status_t #include "utils/KeyedVector.h" #include "utils/String8.h" -#include "android_media_BufferingParams.h" #include "android_media_MediaDataSource.h" #include "android_media_MediaMetricsJNI.h" #include "android_media_PlaybackParams.h" @@ -94,7 +93,6 @@ struct fields_t { }; static fields_t fields; -static BufferingParams::fields_t gBufferingParamsFields; static PlaybackParams::fields_t gPlaybackParamsFields; static SyncParams::fields_t gSyncParamsFields; static VolumeShaperHelper::fields_t gVolumeShaperFields; @@ -370,50 +368,6 @@ android_media_MediaPlayer_setVideoSurface(JNIEnv *env, jobject thiz, jobject jsu setVideoSurface(env, thiz, jsurface, true /* mediaPlayerMustBeAlive */); } -static jobject -android_media_MediaPlayer_getBufferingParams(JNIEnv *env, jobject thiz) -{ - sp<MediaPlayer> mp = getMediaPlayer(env, thiz); - if (mp == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); - return NULL; - } - - BufferingParams bp; - BufferingSettings &settings = bp.settings; - process_media_player_call( - env, thiz, mp->getBufferingSettings(&settings), - "java/lang/IllegalStateException", "unexpected error"); - if (env->ExceptionCheck()) { - return nullptr; - } - ALOGV("getBufferingSettings:{%s}", settings.toString().string()); - - return bp.asJobject(env, gBufferingParamsFields); -} - -static void -android_media_MediaPlayer_setBufferingParams(JNIEnv *env, jobject thiz, jobject params) -{ - if (params == NULL) { - return; - } - - sp<MediaPlayer> mp = getMediaPlayer(env, thiz); - if (mp == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); - return; - } - - BufferingParams bp; - bp.fillFromJobject(env, gBufferingParamsFields, params); - ALOGV("setBufferingParams:{%s}", bp.settings.toString().string()); - - process_media_player_call( - env, thiz, mp->setBufferingSettings(bp.settings), - "java/lang/IllegalStateException", "unexpected error"); -} - static void android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz) { @@ -976,8 +930,6 @@ android_media_MediaPlayer_native_init(JNIEnv *env) env->DeleteLocalRef(clazz); - gBufferingParamsFields.init(env); - // Modular DRM FIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException"); if (clazz) { @@ -1426,8 +1378,6 @@ static const JNINativeMethod gMethods[] = { {"_setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD}, {"_setDataSource", "(Landroid/media/MediaDataSource;)V",(void *)android_media_MediaPlayer_setDataSourceCallback }, {"_setVideoSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaPlayer_setVideoSurface}, - {"getBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer_getBufferingParams}, - {"setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer_setBufferingParams}, {"_prepare", "()V", (void *)android_media_MediaPlayer_prepare}, {"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync}, {"_start", "()V", (void *)android_media_MediaPlayer_start}, diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp index 8b6009e749ce..7e6a8abbce90 100644 --- a/media/jni/android_media_MediaPlayer2.cpp +++ b/media/jni/android_media_MediaPlayer2.cpp @@ -820,7 +820,7 @@ android_media_MediaPlayer2_getCurrentPosition(JNIEnv *env, jobject thiz) } static jlong -android_media_MediaPlayer2_getDuration(JNIEnv *env, jobject thiz) +android_media_MediaPlayer2_getDuration(JNIEnv *env, jobject thiz, jlong srcId) { sp<MediaPlayer2> mp = getMediaPlayer(env, thiz); if (mp == NULL ) { @@ -828,7 +828,7 @@ android_media_MediaPlayer2_getDuration(JNIEnv *env, jobject thiz) return 0; } int64_t msec; - process_media_player_call( env, thiz, mp->getDuration(&msec), NULL, NULL ); + process_media_player_call( env, thiz, mp->getDuration(srcId, &msec), NULL, NULL ); ALOGV("getDuration: %lld (msec)", (long long)msec); return (jlong) msec; } @@ -1408,7 +1408,7 @@ static const JNINativeMethod gMethods[] = { {"native_seekTo", "(JI)V", (void *)android_media_MediaPlayer2_seekTo}, {"native_pause", "()V", (void *)android_media_MediaPlayer2_pause}, {"getCurrentPosition", "()J", (void *)android_media_MediaPlayer2_getCurrentPosition}, - {"getDuration", "()J", (void *)android_media_MediaPlayer2_getDuration}, + {"native_getDuration", "(J)J", (void *)android_media_MediaPlayer2_getDuration}, {"native_release", "()V", (void *)android_media_MediaPlayer2_release}, {"native_reset", "()V", (void *)android_media_MediaPlayer2_reset}, {"native_setAudioAttributes", "(Landroid/media/AudioAttributes;)Z", (void *)android_media_MediaPlayer2_setAudioAttributes}, diff --git a/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml index 1d6728689933..a2a628d7319e 100644 --- a/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml +++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml @@ -25,13 +25,15 @@ android:orientation="vertical" android:gravity="center"> - <ImageView android:id="@+id/user_avatar" + <ImageView + android:id="@+id/user_avatar" android:layout_width="@dimen/car_user_switcher_image_avatar_size" android:layout_height="@dimen/car_user_switcher_image_avatar_size" - android:background="@drawable/car_button_ripple_background_light" + android:background="?android:attr/selectableItemBackground" android:gravity="center"/> - <TextView android:id="@+id/user_name" + <TextView + android:id="@+id/user_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/car_user_switcher_vertical_spacing_between_name_and_avatar" diff --git a/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml index 6cd70d62b4f7..e8c5134cd180 100644 --- a/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml +++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml @@ -19,12 +19,10 @@ android:fitsSystemWindows="true" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/car_user_switcher_background_color" android:visibility="gone"> <LinearLayout android:id="@+id/container" - android:background="@color/car_user_switcher_background_color" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> @@ -38,7 +36,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="@dimen/car_user_switcher_margin_top" - android:theme="@style/Theme.Car.Light.List" + android:theme="@style/PagedListTheme" app:verticallyCenterListContent="true" app:showPagedListViewDivider="false" app:gutter="both" diff --git a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml index 141b28a9ae28..72ec8d86368d 100644 --- a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml +++ b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml @@ -68,7 +68,7 @@ android:orientation="vertical"> <com.android.keyguard.AlphaOptimizedImageButton - android:id="@+id/notifications" + android:id="@+id/note" android:layout_height="wrap_content" android:layout_width="match_parent" android:src="@drawable/car_ic_notification" diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml index b67ce15f80b0..052566d67c1b 100644 --- a/packages/CarSystemUI/res/layout/car_navigation_bar.xml +++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml @@ -20,7 +20,7 @@ xmlns:systemui="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@android:color/black" + android:background="@drawable/system_bar_background" android:orientation="vertical"> <LinearLayout android:id="@id/nav_buttons" diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml index 46e60db0ba4b..4fa877ff37dc 100644 --- a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml +++ b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml @@ -20,7 +20,7 @@ xmlns:systemui="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@android:color/black" + android:background="@drawable/system_bar_background" android:orientation="vertical"> <LinearLayout diff --git a/packages/CarSystemUI/res/layout/car_qs_footer.xml b/packages/CarSystemUI/res/layout/car_qs_footer.xml index 6f19cfcfa345..bf96c00e3f0d 100644 --- a/packages/CarSystemUI/res/layout/car_qs_footer.xml +++ b/packages/CarSystemUI/res/layout/car_qs_footer.xml @@ -35,7 +35,7 @@ android:layout_centerVertical="true" android:layout_width="@dimen/car_qs_footer_icon_width" android:layout_height="@dimen/car_qs_footer_icon_height" - android:background="@drawable/ripple_drawable" + android:background="?android:attr/selectableItemBackground" android:focusable="true"> <ImageView diff --git a/packages/CarSystemUI/res/layout/car_qs_panel.xml b/packages/CarSystemUI/res/layout/car_qs_panel.xml index dfa48c30b0c8..d923e0fbb20b 100644 --- a/packages/CarSystemUI/res/layout/car_qs_panel.xml +++ b/packages/CarSystemUI/res/layout/car_qs_panel.xml @@ -21,8 +21,7 @@ android:layout_height="wrap_content" android:background="@color/car_qs_background_primary" android:orientation="vertical" - android:elevation="4dp" - android:theme="@android:style/Theme"> + android:elevation="4dp"> <include layout="@layout/car_status_bar_header"/> <include layout="@layout/car_qs_footer"/> @@ -39,7 +38,7 @@ android:id="@+id/user_grid" android:layout_width="match_parent" android:layout_height="match_parent" - android:theme="@style/Theme.Car.Light.List" + android:theme="@style/PagedListTheme" app:showPagedListViewDivider="false" app:gutter="both" app:itemSpacing="@dimen/car_user_switcher_vertical_spacing_between_users"/> diff --git a/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml index 141b28a9ae28..72ec8d86368d 100644 --- a/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml +++ b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml @@ -68,7 +68,7 @@ android:orientation="vertical"> <com.android.keyguard.AlphaOptimizedImageButton - android:id="@+id/notifications" + android:id="@+id/note" android:layout_height="wrap_content" android:layout_width="match_parent" android:src="@drawable/car_ic_notification" diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml index 7b3333e63b7a..1dca10a04c43 100644 --- a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml +++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml @@ -21,7 +21,7 @@ android:id="@+id/car_top_bar" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@android:color/black" + android:background="@drawable/system_bar_background" android:orientation="vertical"> <RelativeLayout diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml index 572737f92370..c527711f563f 100644 --- a/packages/CarSystemUI/res/values/config.xml +++ b/packages/CarSystemUI/res/values/config.xml @@ -51,7 +51,6 @@ <item>com.android.systemui.LatencyTester</item> <item>com.android.systemui.globalactions.GlobalActionsComponent</item> <item>com.android.systemui.ScreenDecorations</item> - <item>com.android.systemui.fingerprint.FingerprintDialogImpl</item> <item>com.android.systemui.SliceBroadcastRelayHandler</item> <item>com.android.systemui.notifications.NotificationsUI</item> </string-array> diff --git a/packages/CarSystemUI/res/values/themes.xml b/packages/CarSystemUI/res/values/themes.xml new file mode 100644 index 000000000000..8a5961ed051b --- /dev/null +++ b/packages/CarSystemUI/res/values/themes.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> + +<resources> + <!--This Theme contains attributes required for components from the car support lib --> + <style name="PagedListTheme" parent="Theme.CarSupportWrapper.NoActionBar"> + </style> +</resources> diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java index 730c3e3440ea..a4424260fef5 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java @@ -52,6 +52,9 @@ public class DrivingStateHelper { * is idling or moving, {@code false} otherwise. */ public boolean isCurrentlyDriving() { + if (mDrivingStateManager == null) { + return false; + } try { CarDrivingStateEvent currentState = mDrivingStateManager.getCurrentCarDrivingState(); if (currentState != null) { diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml index 34bc4ebcd0aa..0be71e6d17b9 100644 --- a/packages/CompanionDeviceManager/AndroidManifest.xml +++ b/packages/CompanionDeviceManager/AndroidManifest.xml @@ -26,6 +26,7 @@ <uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> + <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/> diff --git a/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java b/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java index ab71802102ae..81a63dd80a63 100644 --- a/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java +++ b/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java @@ -17,14 +17,10 @@ package android.ext.services.sms; import android.annotation.NonNull; import android.annotation.Nullable; -import android.database.Cursor; import android.database.CursorWindow; -import android.net.Uri; import android.os.Bundle; import android.service.sms.FinancialSmsService; -import android.util.Log; -import java.util.ArrayList; /** * Service to provide financial apps access to sms messages. */ @@ -36,45 +32,6 @@ public class FinancialSmsServiceImpl extends FinancialSmsService { @Nullable @Override public CursorWindow onGetSmsMessages(@NonNull Bundle params) { - ArrayList<String> columnNames = params.getStringArrayList(KEY_COLUMN_NAMES); - if (columnNames == null || columnNames.size() <= 0) { - return null; - } - - Uri inbox = Uri.parse("content://sms/inbox"); - - try (Cursor cursor = getContentResolver().query(inbox, null, null, null, null); - CursorWindow window = new CursorWindow("FinancialSmsMessages")) { - int messageCount = cursor.getCount(); - if (messageCount > 0 && cursor.moveToFirst()) { - window.setNumColumns(columnNames.size()); - for (int row = 0; row < messageCount; row++) { - if (!window.allocRow()) { - Log.e(TAG, "CursorWindow ran out of memory."); - return null; - } - for (int col = 0; col < columnNames.size(); col++) { - String columnName = columnNames.get(col); - int inboxColumnIndex = cursor.getColumnIndexOrThrow(columnName); - String inboxColumnValue = cursor.getString(inboxColumnIndex); - boolean addedToCursorWindow = window.putString(inboxColumnValue, row, col); - if (!addedToCursorWindow) { - Log.e(TAG, "Failed to add:" - + inboxColumnValue - + ";column:" - + columnName); - return null; - } - } - cursor.moveToNext(); - } - } else { - Log.w(TAG, "No sms messages."); - } - return window; - } catch (Exception e) { - Log.e(TAG, "Failed to get sms messages."); - return null; - } + return null; } } diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java index 9699294df587..71778215e079 100644 --- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java @@ -19,9 +19,7 @@ package com.android.settingslib.deviceinfo; import android.annotation.SuppressLint; import android.content.Context; import android.net.ConnectivityManager; -import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; -import android.provider.Settings; import android.text.TextUtils; import androidx.annotation.VisibleForTesting; @@ -83,15 +81,13 @@ public abstract class AbstractWifiMacAddressPreferenceController @SuppressLint("HardwareIds") @Override protected void updateConnectivity() { - WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); - final int macRandomizationMode = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, OFF); - final String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress(); + final String[] macAddresses = mWifiManager.getFactoryMacAddresses(); + String macAddress = null; + if (macAddresses != null && macAddresses.length > 0) { + macAddress = macAddresses[0]; + } - if (macRandomizationMode == ON && WifiInfo.DEFAULT_MAC_ADDRESS.equals(macAddress)) { - mWifiMacAddress.setSummary(R.string.wifi_status_mac_randomized); - } else if (TextUtils.isEmpty(macAddress) - || WifiInfo.DEFAULT_MAC_ADDRESS.equals(macAddress)) { + if (TextUtils.isEmpty(macAddress)) { mWifiMacAddress.setSummary(R.string.status_unavailable); } else { mWifiMacAddress.setSummary(macAddress); diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java index f7b16f8b18db..c8c05a0f0bb6 100644 --- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java @@ -22,6 +22,7 @@ import android.content.Intent; import android.os.PowerManager; import android.provider.Settings.Global; import android.provider.Settings.Secure; +import android.text.TextUtils; import android.util.KeyValueListParser; import android.util.Log; import android.util.Slog; @@ -176,4 +177,22 @@ public class BatterySaverUtils { setAutoBatterySaverTriggerLevel(context, level); } } + + /** + * Reverts battery saver schedule mode to none if we are in a bad state where routine mode + * is selected but no app is configured to actually provide the signal. + * @param context a valid context + */ + public static void revertScheduleToNoneIfNeeded(Context context) { + ContentResolver resolver = context.getContentResolver(); + final int currentMode = Global.getInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE, + PowerManager.POWER_SAVER_MODE_PERCENTAGE); + boolean providerConfigured = !TextUtils.isEmpty(context.getString( + com.android.internal.R.string.config_batterySaverScheduleProvider)); + if (currentMode == PowerManager.POWER_SAVER_MODE_DYNAMIC && !providerConfigured) { + Global.putInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0); + Global.putInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE, + PowerManager.POWER_SAVER_MODE_PERCENTAGE); + } + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java new file mode 100644 index 000000000000..9af06702a696 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.location; + +import android.app.AppOpsManager; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.graphics.drawable.Drawable; +import android.os.Process; +import android.os.UserHandle; +import android.os.UserManager; +import android.text.format.DateUtils; +import android.util.IconDrawableFactory; +import android.util.Log; + +import androidx.annotation.VisibleForTesting; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * Retrieves the information of applications which accessed location recently. + */ +public class RecentLocationAccesses { + private static final String TAG = RecentLocationAccesses.class.getSimpleName(); + @VisibleForTesting + static final String ANDROID_SYSTEM_PACKAGE_NAME = "android"; + + // Keep last 24 hours of location app information. + private static final long RECENT_TIME_INTERVAL_MILLIS = DateUtils.DAY_IN_MILLIS; + + @VisibleForTesting + static final int[] LOCATION_OPS = new int[]{ + AppOpsManager.OP_FINE_LOCATION, + AppOpsManager.OP_COARSE_LOCATION, + }; + + private final PackageManager mPackageManager; + private final Context mContext; + private final IconDrawableFactory mDrawableFactory; + + public RecentLocationAccesses(Context context) { + mContext = context; + mPackageManager = context.getPackageManager(); + mDrawableFactory = IconDrawableFactory.newInstance(context); + } + + /** + * Fills a list of applications which queried location recently within specified time. + * Apps are sorted by recency. Apps with more recent location accesses are in the front. + */ + public List<Access> getAppList() { + // Retrieve a location usage list from AppOps + AppOpsManager aoManager = + (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); + List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(LOCATION_OPS); + + final int appOpsCount = appOps != null ? appOps.size() : 0; + + // Process the AppOps list and generate a preference list. + ArrayList<Access> accesses = new ArrayList<>(appOpsCount); + final long now = System.currentTimeMillis(); + final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + final List<UserHandle> profiles = um.getUserProfiles(); + + for (int i = 0; i < appOpsCount; ++i) { + AppOpsManager.PackageOps ops = appOps.get(i); + // Don't show the Android System in the list - it's not actionable for the user. + // Also don't show apps belonging to background users except managed users. + String packageName = ops.getPackageName(); + int uid = ops.getUid(); + int userId = UserHandle.getUserId(uid); + boolean isAndroidOs = + (uid == Process.SYSTEM_UID) && ANDROID_SYSTEM_PACKAGE_NAME.equals(packageName); + if (isAndroidOs || !profiles.contains(new UserHandle(userId))) { + continue; + } + Access access = getAccessFromOps(now, ops); + if (access != null) { + accesses.add(access); + } + } + return accesses; + } + + public List<Access> getAppListSorted() { + List<Access> accesses = getAppList(); + // Sort the list of Access by recency. Most recent accesses first. + Collections.sort(accesses, Collections.reverseOrder(new Comparator<Access>() { + @Override + public int compare(Access access1, Access access2) { + return Long.compare(access1.accessFinishTime, access2.accessFinishTime); + } + })); + return accesses; + } + + /** + * Creates a Access entry for the given PackageOps. + * + * This method examines the time interval of the PackageOps first. If the PackageOps is older + * than the designated interval, this method ignores the PackageOps object and returns null. + * When the PackageOps is fresh enough, this method returns a Access object for the package + */ + private Access getAccessFromOps(long now, + AppOpsManager.PackageOps ops) { + String packageName = ops.getPackageName(); + List<AppOpsManager.OpEntry> entries = ops.getOps(); + long locationAccessFinishTime = 0L; + // Earliest time for a location access to end and still be shown in list. + long recentLocationCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS; + for (AppOpsManager.OpEntry entry : entries) { + locationAccessFinishTime = Math.max(entry.getLastAccessBackgroundTime(), + entry.getLastAccessForegroundTime()); + } + // Bail out if the entry is out of date. + if (locationAccessFinishTime < recentLocationCutoffTime) { + return null; + } + + // The package is fresh enough, continue. + int uid = ops.getUid(); + int userId = UserHandle.getUserId(uid); + + Access access = null; + try { + ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser( + packageName, PackageManager.GET_META_DATA, userId); + if (appInfo == null) { + Log.w(TAG, "Null application info retrieved for package " + packageName + + ", userId " + userId); + return null; + } + + final UserHandle userHandle = new UserHandle(userId); + Drawable icon = mDrawableFactory.getBadgedIcon(appInfo, userId); + CharSequence appLabel = mPackageManager.getApplicationLabel(appInfo); + CharSequence badgedAppLabel = mPackageManager.getUserBadgedLabel(appLabel, userHandle); + if (appLabel.toString().contentEquals(badgedAppLabel)) { + // If badged label is not different from original then no need for it as + // a separate content description. + badgedAppLabel = null; + } + access = new Access(packageName, userHandle, icon, appLabel, badgedAppLabel, + locationAccessFinishTime); + } catch (NameNotFoundException e) { + Log.w(TAG, "package name not found for " + packageName + ", userId " + userId); + } + return access; + } + + public static class Access { + public final String packageName; + public final UserHandle userHandle; + public final Drawable icon; + public final CharSequence label; + public final CharSequence contentDescription; + public final long accessFinishTime; + + private Access(String packageName, UserHandle userHandle, Drawable icon, + CharSequence label, CharSequence contentDescription, + long accessFinishTime) { + this.packageName = packageName; + this.userHandle = userHandle; + this.icon = icon; + this.label = label; + this.contentDescription = contentDescription; + this.accessFinishTime = accessFinishTime; + } + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java index 74e5bf5a8034..1f7f4bc1f6b6 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java @@ -27,7 +27,6 @@ import android.content.Context; import android.net.ConnectivityManager; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; -import android.provider.Settings; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; @@ -93,105 +92,23 @@ public class WifiMacAddressPreferenceControllerTest { } @Test - public void updateConnectivity_nullWifiInfoWithMacRandomizationOff_setMacUnavailable() { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, - AbstractWifiMacAddressPreferenceController.OFF); - doReturn(null).when(mWifiManager).getConnectionInfo(); - - mController.displayPreference(mScreen); - - assertThat(mPreference.getSummary()) - .isEqualTo(mContext.getString(R.string.status_unavailable)); - } - - @Test - public void updateConnectivity_nullMacWithMacRandomizationOff_setMacUnavailable() { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, - AbstractWifiMacAddressPreferenceController.OFF); - doReturn(null).when(mWifiInfo).getMacAddress(); - + public void updateConnectivity_null_setMacUnavailable() { + doReturn(null).when(mWifiManager).getFactoryMacAddresses(); mController.displayPreference(mScreen); - assertThat(mPreference.getSummary()) .isEqualTo(mContext.getString(R.string.status_unavailable)); } @Test - public void updateConnectivity_defaultMacWithMacRandomizationOff_setMacUnavailable() { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, - AbstractWifiMacAddressPreferenceController.OFF); - doReturn(WifiInfo.DEFAULT_MAC_ADDRESS).when(mWifiInfo).getMacAddress(); - + public void updateConnectivity_validMac_setValidMac() { + final String[] macAddresses = new String[]{TEST_MAC_ADDRESS}; + doReturn(macAddresses).when(mWifiManager).getFactoryMacAddresses(); mController.displayPreference(mScreen); - - assertThat(mPreference.getSummary()) - .isEqualTo(mContext.getString(R.string.status_unavailable)); - } - - @Test - public void updateConnectivity_validMacWithMacRandomizationOff_setValidMac() { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, - AbstractWifiMacAddressPreferenceController.OFF); - doReturn(TEST_MAC_ADDRESS).when(mWifiInfo).getMacAddress(); - - mController.displayPreference(mScreen); - assertThat(mPreference.getSummary()).isEqualTo(TEST_MAC_ADDRESS); - } - - @Test - public void updateConnectivity_nullWifiInfoWithMacRandomizationOn_setMacUnavailable() { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, - AbstractWifiMacAddressPreferenceController.ON); - doReturn(null).when(mWifiManager).getConnectionInfo(); - mController.displayPreference(mScreen); - - assertThat(mPreference.getSummary()) - .isEqualTo(mContext.getString(R.string.status_unavailable)); - } - - @Test - public void updateConnectivity_nullMacWithMacRandomizationOn_setMacUnavailable() { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, - AbstractWifiMacAddressPreferenceController.ON); - doReturn(null).when(mWifiInfo).getMacAddress(); - mController.displayPreference(mScreen); - assertThat(mPreference.getSummary()) - .isEqualTo(mContext.getString(R.string.status_unavailable)); - } - @Test - public void updateConnectivity_defaultMacWithMacRandomizationOn_setMacRandomized() { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, - AbstractWifiMacAddressPreferenceController.ON); - doReturn(WifiInfo.DEFAULT_MAC_ADDRESS).when(mWifiInfo).getMacAddress(); - - mController.displayPreference(mScreen); - - assertThat(mPreference.getSummary()) - .isEqualTo(mContext.getString(R.string.wifi_status_mac_randomized)); - } - - @Test - public void updateConnectivity_validMacWithMacRandomizationOn_setValidMac() { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, - AbstractWifiMacAddressPreferenceController.ON); - doReturn(TEST_MAC_ADDRESS).when(mWifiInfo).getMacAddress(); - - mController.displayPreference(mScreen); - - assertThat(mPreference.getSummary()).isEqualTo(TEST_MAC_ADDRESS); } private static class ConcreteWifiMacAddressPreferenceController diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 5fe08aab93cc..8cfc2a6b158d 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -27,6 +27,7 @@ <uses-permission android:name="android.permission.READ_SMS" /> <uses-permission android:name="android.permission.CALL_PHONE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> + <uses-permission android:name="android.permission.READ_PRECISE_PHONE_STATE" /> <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" /> diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index c2e107a06692..ad44b9a1ee9f 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -134,6 +134,7 @@ android_app { ], platform_apis: true, + product_specific: true, certificate: "platform", privileged: true, diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java index 1a18f6096e25..6135aebb0587 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java @@ -62,4 +62,12 @@ public interface ClockPlugin extends Plugin { * Notifies that the time zone has changed. */ default void onTimeZoneChanged(TimeZone timeZone) {} + + /** + * Indicates whether the keyguard status area (date) should be shown below + * the clock. + */ + default boolean shouldShowStatusArea() { + return true; + } } diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index 367a9ae28f97..d52866fbd444 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -20,19 +20,31 @@ <!-- This is a view that shows clock information in Keyguard. --> <com.android.keyguard.KeyguardClockSwitch xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/keyguard_clock_container" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:layout_alignParentTop="true"> - <TextClock - android:id="@+id/default_clock_view" - android:layout_width="wrap_content" + android:layout_gravity="center_horizontal|top"> + <FrameLayout + android:id="@+id/clock_view" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:layout_alignParentTop="true"> + <TextClock + android:id="@+id/default_clock_view" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:letterSpacing="0.03" + android:textColor="?attr/wallpaperTextColor" + android:singleLine="true" + style="@style/widget_big" + android:format12Hour="@string/keyguard_widget_12_hours_format" + android:format24Hour="@string/keyguard_widget_24_hours_format" /> + </FrameLayout> + <include layout="@layout/keyguard_status_area" + android:id="@+id/keyguard_status_area" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:letterSpacing="0.03" - android:textColor="?attr/wallpaperTextColor" - android:singleLine="true" - style="@style/widget_big" - android:format12Hour="@string/keyguard_widget_12_hours_format" - android:format24Hour="@string/keyguard_widget_24_hours_format" /> + android:layout_below="@id/clock_view" /> </com.android.keyguard.KeyguardClockSwitch> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml index 7d8a1f5bbbc7..a9ba19d2d393 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml @@ -33,21 +33,11 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> - <RelativeLayout + <include + layout="@layout/keyguard_clock_switch" android:id="@+id/keyguard_clock_container" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal|top"> - <include layout="@layout/keyguard_clock_switch" - android:id="@+id/clock_view" - android:layout_width="match_parent" - android:layout_height="wrap_content" /> - <include layout="@layout/keyguard_status_area" - android:id="@+id/keyguard_status_area" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@id/clock_view" /> - </RelativeLayout> + android:layout_height="wrap_content" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml index 67ecf6f7b21a..10fea9d50112 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml @@ -50,21 +50,11 @@ android:textSize="13sp" android:text="@*android:string/global_action_logout" /> - <RelativeLayout + <include + layout="@layout/keyguard_clock_switch" android:id="@+id/keyguard_clock_container" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal|top"> - <include layout="@layout/keyguard_clock_switch" - android:id="@+id/clock_view" - android:layout_width="match_parent" - android:layout_height="wrap_content" /> - <include layout="@layout/keyguard_status_area" - android:id="@+id/keyguard_status_area" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@id/clock_view" /> - </RelativeLayout> + android:layout_height="wrap_content" /> <TextView android:id="@+id/owner_info" diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java index 74fd13f9564e..01b012d1fc84 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java @@ -14,12 +14,40 @@ package com.android.systemui.shared.plugins; +import android.annotation.IntDef; import android.content.ComponentName; /** * Enables and disables plugins. */ public interface PluginEnabler { - void setEnabled(ComponentName component, boolean enabled); + + int ENABLED = 0; + int DISABLED_MANUALLY = 1; + int DISABLED_INVALID_VERSION = 1; + int DISABLED_FROM_EXPLICIT_CRASH = 2; + int DISABLED_FROM_SYSTEM_CRASH = 3; + + @IntDef({ENABLED, DISABLED_MANUALLY, DISABLED_INVALID_VERSION, DISABLED_FROM_EXPLICIT_CRASH, + DISABLED_FROM_SYSTEM_CRASH}) + @interface DisableReason { + } + + /** Enables plugin via the PackageManager. */ + void setEnabled(ComponentName component); + + /** Disables a plugin via the PackageManager and records the reason for disabling. */ + void setDisabled(ComponentName component, @DisableReason int reason); + + /** Returns true if the plugin is enabled in the PackageManager. */ boolean isEnabled(ComponentName component); + + /** + * Returns the reason that a plugin is disabled, (if it is). + * + * It should return {@link #ENABLED} if the plugin is turned on. + * It should return {@link #DISABLED_MANUALLY} if the plugin is off but the reason is unknown. + */ + @DisableReason + int getDisableReason(ComponentName componentName); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java index 8e7fadb5c7cb..523720d54eec 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java @@ -136,7 +136,7 @@ public class PluginInstanceManager<T extends Plugin> { ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins); for (PluginInfo info : plugins) { if (className.startsWith(info.mPackage)) { - disable(info); + disable(info, PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH); disableAny = true; } } @@ -146,12 +146,13 @@ public class PluginInstanceManager<T extends Plugin> { public boolean disableAll() { ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins); for (int i = 0; i < plugins.size(); i++) { - disable(plugins.get(i)); + disable(plugins.get(i), PluginEnabler.DISABLED_FROM_SYSTEM_CRASH); } return plugins.size() != 0; } - private void disable(PluginInfo info) { + private void disable(PluginInfo info, + @PluginEnabler.DisableReason int reason) { // Live by the sword, die by the sword. // Misbehaving plugins get disabled and won't come back until uninstall/reinstall. @@ -162,9 +163,9 @@ public class PluginInstanceManager<T extends Plugin> { // Don't disable whitelisted plugins as they are a part of the OS. return; } - Log.w(TAG, "Disabling plugin " + info.mPackage + "/" + info.mClass); - mManager.getPluginEnabler().setEnabled(new ComponentName(info.mPackage, info.mClass), - false); + ComponentName pluginComponent = new ComponentName(info.mPackage, info.mClass); + Log.w(TAG, "Disabling plugin " + pluginComponent.flattenToShortString()); + mManager.getPluginEnabler().setDisabled(pluginComponent, reason); } public <T> boolean dependsOn(Plugin p, Class<T> cls) { diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java index dc2a9bd5105b..10b5f1c64d85 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java @@ -184,6 +184,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage mListening = true; IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_CHANGED); + filter.addAction(Intent.ACTION_PACKAGE_REPLACED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addAction(PLUGIN_CHANGED); filter.addAction(DISABLE_PLUGIN); @@ -214,12 +215,13 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage // Don't disable whitelisted plugins as they are a part of the OS. return; } - getPluginEnabler().setEnabled(component, false); + getPluginEnabler().setDisabled(component, PluginEnabler.DISABLED_INVALID_VERSION); mContext.getSystemService(NotificationManager.class).cancel(component.getClassName(), SystemMessage.NOTE_PLUGIN); } else { Uri data = intent.getData(); String pkg = data.getEncodedSchemeSpecificPart(); + ComponentName componentName = ComponentName.unflattenFromString(pkg); if (mOneShotPackages.contains(pkg)) { int icon = mContext.getResources().getIdentifier("tuner", "drawable", mContext.getPackageName()); @@ -256,6 +258,17 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage Log.v(TAG, "Reloading " + pkg); } } + if (Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction())) { + @PluginEnabler.DisableReason int disableReason = + getPluginEnabler().getDisableReason(componentName); + if (disableReason == PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH + || disableReason == PluginEnabler.DISABLED_FROM_SYSTEM_CRASH + || disableReason == PluginEnabler.DISABLED_INVALID_VERSION) { + Log.i(TAG, "Re-enabling previously disabled plugin that has been " + + "updated: " + componentName.flattenToShortString()); + getPluginEnabler().setEnabled(componentName); + } + } if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) { for (PluginInstanceManager manager : mPluginMap.values()) { manager.onPackageChange(pkg); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 0ec90148c350..22a23a8f8a21 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -7,6 +7,7 @@ import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; +import android.widget.RelativeLayout; import android.widget.TextClock; import androidx.annotation.VisibleForTesting; @@ -22,7 +23,7 @@ import java.util.TimeZone; /** * Switch to show plugin clock when plugin is connected, otherwise it will show default clock. */ -public class KeyguardClockSwitch extends FrameLayout { +public class KeyguardClockSwitch extends RelativeLayout { /** * Optional/alternative clock injected via plugin. */ @@ -31,6 +32,15 @@ public class KeyguardClockSwitch extends FrameLayout { * Default clock. */ private TextClock mClockView; + /** + * Frame for default and custom clock. + */ + private FrameLayout mClockFrame; + /** + * Status area (date and other stuff) shown below the clock. Plugin can decide whether + * or not to show it below the alternate clock. + */ + private View mKeyguardStatusArea; private final PluginListener<ClockPlugin> mClockPluginListener = new PluginListener<ClockPlugin>() { @@ -43,11 +53,14 @@ public class KeyguardClockSwitch extends FrameLayout { // selected clock face. In the future, the user should be able to // pick a clock face from the available plugins. mClockPlugin = plugin; - addView(view, -1, + mClockFrame.addView(view, -1, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); initPluginParams(); mClockView.setVisibility(View.GONE); + if (!plugin.shouldShowStatusArea()) { + mKeyguardStatusArea.setVisibility(View.GONE); + } } } @@ -56,6 +69,7 @@ public class KeyguardClockSwitch extends FrameLayout { if (Objects.equals(plugin, mClockPlugin)) { disconnectPlugin(); mClockView.setVisibility(View.VISIBLE); + mKeyguardStatusArea.setVisibility(View.VISIBLE); } } }; @@ -72,6 +86,8 @@ public class KeyguardClockSwitch extends FrameLayout { protected void onFinishInflate() { super.onFinishInflate(); mClockView = findViewById(R.id.default_clock_view); + mClockFrame = findViewById(R.id.clock_view); + mKeyguardStatusArea = findViewById(R.id.keyguard_status_area); } @Override @@ -185,7 +201,7 @@ public class KeyguardClockSwitch extends FrameLayout { if (mClockPlugin != null) { View view = mClockPlugin.getView(); if (view != null) { - removeView(view); + mClockFrame.removeView(view); } mClockPlugin = null; } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index 1e9d288bc605..c6f172684686 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -37,7 +37,7 @@ import android.util.Slog; import android.util.TypedValue; import android.view.View; import android.widget.GridLayout; -import android.widget.RelativeLayout; +import android.widget.LinearLayout; import android.widget.TextView; import androidx.core.graphics.ColorUtils; @@ -173,7 +173,7 @@ public class KeyguardStatusView extends GridLayout implements mLogoutView.setOnClickListener(this::onLogoutClicked); } - mClockView = findViewById(R.id.clock_view); + mClockView = findViewById(R.id.keyguard_clock_container); mClockView.setShowCurrentUserTime(true); if (KeyguardClockAccessibilityDelegate.isNeeded(mContext)) { mClockView.setAccessibilityDelegate(new KeyguardClockAccessibilityDelegate(mContext)); @@ -205,8 +205,8 @@ public class KeyguardStatusView extends GridLayout implements * Moves clock, adjusting margins when slice content changes. */ private void onSliceContentChanged() { - RelativeLayout.LayoutParams layoutParams = - (RelativeLayout.LayoutParams) mClockView.getLayoutParams(); + LinearLayout.LayoutParams layoutParams = + (LinearLayout.LayoutParams) mClockView.getLayoutParams(); layoutParams.bottomMargin = mPulsing ? mSmallClockPadding : 0; mClockView.setLayoutParams(layoutParams); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java index 242890e79f44..974cd8804841 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java @@ -182,7 +182,7 @@ public class DozeLog { */ public static void traceLockScreenWakeUp(boolean wake) { if (!ENABLED) return; - log("wakeLockScreenWakeUp " + wake); + log("wakeLockScreen " + wake); } /** @@ -191,7 +191,7 @@ public class DozeLog { */ public static void traceWakeDisplay(boolean wake) { if (!ENABLED) return; - log("wakeLockScreenWakeUp " + wake); + log("wakeDisplay " + wake); } public static void traceProximityResult(Context context, boolean near, long millis, diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java index e2417f7b6397..63374150adaa 100644 --- a/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java @@ -16,28 +16,44 @@ package com.android.systemui.plugins; import android.content.ComponentName; import android.content.Context; +import android.content.SharedPreferences; import android.content.pm.PackageManager; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.shared.plugins.PluginEnabler; public class PluginEnablerImpl implements PluginEnabler { + private static final String CRASH_DISABLED_PLUGINS_PREF_FILE = "auto_disabled_plugins_prefs"; - final private PackageManager mPm; + private PackageManager mPm; + private final SharedPreferences mAutoDisabledPrefs; public PluginEnablerImpl(Context context) { - this(context.getPackageManager()); + this(context, context.getPackageManager()); } - @VisibleForTesting public PluginEnablerImpl(PackageManager pm) { + @VisibleForTesting public PluginEnablerImpl(Context context, PackageManager pm) { + mAutoDisabledPrefs = context.getSharedPreferences( + CRASH_DISABLED_PLUGINS_PREF_FILE, Context.MODE_PRIVATE); mPm = pm; } @Override - public void setEnabled(ComponentName component, boolean enabled) { + public void setEnabled(ComponentName component) { + setDisabled(component, ENABLED); + } + + @Override + public void setDisabled(ComponentName component, @DisableReason int reason) { + boolean enabled = reason == ENABLED; final int desiredState = enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED; mPm.setComponentEnabledSetting(component, desiredState, PackageManager.DONT_KILL_APP); + if (enabled) { + mAutoDisabledPrefs.edit().remove(component.flattenToString()).apply(); + } else { + mAutoDisabledPrefs.edit().putInt(component.flattenToString(), reason).apply(); + } } @Override @@ -45,4 +61,12 @@ public class PluginEnablerImpl implements PluginEnabler { return mPm.getComponentEnabledSetting(component) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED; } + + @Override + public @DisableReason int getDisableReason(ComponentName componentName) { + if (isEnabled(componentName)) { + return ENABLED; + } + return mAutoDisabledPrefs.getInt(componentName.flattenToString(), DISABLED_MANUALLY); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java index f5d6904a1543..e31f90d5a92e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java @@ -88,10 +88,14 @@ public class SmartReplyController { return mSendingKeys.contains(key); } - public void smartRepliesAdded(final NotificationData.Entry entry, int replyCount) { + /** + * Smart Replies and Actions have been added to the UI. + */ + public void smartSuggestionsAdded(final NotificationData.Entry entry, int replyCount, + int actionCount, boolean generatedByAssistant) { try { - mBarService.onNotificationSmartRepliesAdded(entry.notification.getKey(), - replyCount); + mBarService.onNotificationSmartSuggestionsAdded( + entry.notification.getKey(), replyCount, actionCount, generatedByAssistant); } catch (RemoteException e) { // Nothing to do, system going down } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java new file mode 100644 index 000000000000..49f1a8d6f248 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.notification; + +import android.app.Notification; +import android.os.SystemClock; +import android.service.notification.StatusBarNotification; +import android.util.Log; +import android.view.View; + +import com.android.systemui.DejankUtils; +import com.android.systemui.bubbles.BubbleController; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.phone.ShadeController; + +/** + * Click handler for generic clicks on notifications. Clicks on specific areas (expansion caret, + * app ops icon, etc) are handled elsewhere. + */ +public final class NotificationClicker implements View.OnClickListener { + private static final String TAG = "NotificationClicker"; + + private final ShadeController mShadeController; + private final BubbleController mBubbleController; + private final NotificationActivityStarter mNotificationActivityStarter; + + public NotificationClicker(ShadeController shadeController, + BubbleController bubbleController, + NotificationActivityStarter notificationActivityStarter) { + mShadeController = shadeController; + mBubbleController = bubbleController; + mNotificationActivityStarter = notificationActivityStarter; + } + + @Override + public void onClick(final View v) { + if (!(v instanceof ExpandableNotificationRow)) { + Log.e(TAG, "NotificationClicker called on a view that is not a notification row."); + return; + } + + mShadeController.wakeUpIfDozing(SystemClock.uptimeMillis(), v); + + final ExpandableNotificationRow row = (ExpandableNotificationRow) v; + final StatusBarNotification sbn = row.getStatusBarNotification(); + if (sbn == null) { + Log.e(TAG, "NotificationClicker called on an unclickable notification,"); + return; + } + + // Check if the notification is displaying the menu, if so slide notification back + if (isMenuVisible(row)) { + row.animateTranslateNotification(0); + return; + } else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) { + row.getNotificationParent().animateTranslateNotification(0); + return; + } else if (row.isSummaryWithChildren() && row.areChildrenExpanded()) { + // We never want to open the app directly if the user clicks in between + // the notifications. + return; + } + + // Mark notification for one frame. + row.setJustClicked(true); + DejankUtils.postAfterTraversal(() -> row.setJustClicked(false)); + + // If it was a bubble we should close it + if (row.getEntry().isBubble()) { + mBubbleController.collapseStack(); + } + + mNotificationActivityStarter.onNotificationClicked(sbn, row); + } + + private boolean isMenuVisible(ExpandableNotificationRow row) { + return row.getProvider() != null && row.getProvider().isMenuVisible(); + } + + /** + * Attaches the click listener to the row if appropriate. + */ + public void register(ExpandableNotificationRow row, StatusBarNotification sbn) { + Notification notification = sbn.getNotification(); + if (notification.contentIntent != null || notification.fullScreenIntent != null) { + row.setOnClickListener(this); + } else { + row.setOnClickListener(null); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index aab3fc4c7a0e..d2a5864f9f33 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -15,6 +15,7 @@ */ package com.android.systemui.statusbar.notification; +import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.systemui.bubbles.BubbleController.DEBUG_DEMOTE_TO_NOTIF; import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT; import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY; @@ -36,7 +37,6 @@ import android.os.Handler; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.service.dreams.DreamService; @@ -49,7 +49,6 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.EventLog; import android.util.Log; -import android.view.View; import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; @@ -57,7 +56,6 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.util.NotificationMessagingUtil; -import com.android.systemui.DejankUtils; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.EventLogTags; @@ -115,7 +113,6 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. private final NotificationMessagingUtil mMessagingUtil; protected final Context mContext; protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>(); - private final NotificationClicker mNotificationClicker = new NotificationClicker(); private final NotificationGroupManager mGroupManager = Dependency.get(NotificationGroupManager.class); @@ -146,7 +143,6 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. protected IStatusBarService mBarService; private NotificationPresenter mPresenter; private Callback mCallback; - private NotificationActivityStarter mNotificationActivityStarter; protected PowerManager mPowerManager; private NotificationListenerService.RankingMap mLatestRankingMap; protected HeadsUpManager mHeadsUpManager; @@ -161,63 +157,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener; private NotificationViewHierarchyManager.StatusBarStateListener mStatusBarStateListener; @Nullable private AlertTransferListener mAlertTransferListener; - - private final class NotificationClicker implements View.OnClickListener { - - @Override - public void onClick(final View v) { - if (!(v instanceof ExpandableNotificationRow)) { - Log.e(TAG, "NotificationClicker called on a view that is not a notification row."); - return; - } - - getShadeController().wakeUpIfDozing(SystemClock.uptimeMillis(), v); - - final ExpandableNotificationRow row = (ExpandableNotificationRow) v; - final StatusBarNotification sbn = row.getStatusBarNotification(); - if (sbn == null) { - Log.e(TAG, "NotificationClicker called on an unclickable notification,"); - return; - } - - // Check if the notification is displaying the menu, if so slide notification back - if (isMenuVisible(row)) { - row.animateTranslateNotification(0); - return; - } else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) { - row.getNotificationParent().animateTranslateNotification(0); - return; - } else if (row.isSummaryWithChildren() && row.areChildrenExpanded()) { - // We never want to open the app directly if the user clicks in between - // the notifications. - return; - } - - // Mark notification for one frame. - row.setJustClicked(true); - DejankUtils.postAfterTraversal(() -> row.setJustClicked(false)); - - // If it was a bubble we should close it - if (row.getEntry().isBubble()) { - mBubbleController.collapseStack(); - } - - mNotificationActivityStarter.onNotificationClicked(sbn, row); - } - - private boolean isMenuVisible(ExpandableNotificationRow row) { - return row.getProvider() != null && row.getProvider().isMenuVisible(); - } - - public void register(ExpandableNotificationRow row, StatusBarNotification sbn) { - Notification notification = sbn.getNotification(); - if (notification.contentIntent != null || notification.fullScreenIntent != null) { - row.setOnClickListener(this); - } else { - row.setOnClickListener(null); - } - } - } + @Nullable private NotificationClicker mNotificationClicker; private final DeviceProvisionedController.DeviceProvisionedListener mDeviceProvisionedListener = @@ -269,6 +209,10 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. mAlertTransferListener = listener; } + public void setNotificationClicker(NotificationClicker clicker) { + mNotificationClicker = clicker; + } + /** * Our dependencies can have cyclic references, so some need to be lazy */ @@ -355,11 +299,6 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. mOnAppOpsClickListener = mGutsManager::openGuts; } - public void setNotificationActivityStarter( - NotificationActivityStarter notificationActivityStarter) { - mNotificationActivityStarter = notificationActivityStarter; - } - public NotificationData getNotificationData() { return mNotificationData; } @@ -757,7 +696,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. row.setIsLowPriority(isLowPriority); row.setLowPriorityStateUpdated(isUpdate && (wasLowPriority != isLowPriority)); // bind the click event to the content area - mNotificationClicker.register(row, sbn); + checkNotNull(mNotificationClicker).register(row, sbn); // Extract target SDK version. try { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index 6bc39c82f8bf..02a310c29379 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -1474,9 +1474,19 @@ public class NotificationContentView extends FrameLayout { if (mExpandedChild != null) { mExpandedSmartReplyView = applySmartReplyView(mExpandedChild, smartRepliesAndActions, entry); - if (mExpandedSmartReplyView != null && smartRepliesAndActions.smartReplies != null) { - mSmartReplyController.smartRepliesAdded( - entry, smartRepliesAndActions.smartReplies.choices.length); + if (mExpandedSmartReplyView != null) { + if (smartRepliesAndActions.smartReplies != null + || smartRepliesAndActions.smartActions != null) { + int numSmartReplies = smartRepliesAndActions.smartReplies == null + ? 0 : smartRepliesAndActions.smartReplies.choices.length; + int numSmartActions = smartRepliesAndActions.smartActions == null + ? 0 : smartRepliesAndActions.smartActions.actions.size(); + boolean fromAssistant = smartRepliesAndActions.smartReplies == null + ? smartRepliesAndActions.smartActions.fromAssistant + : smartRepliesAndActions.smartReplies.fromAssistant; + mSmartReplyController.smartSuggestionsAdded(entry, numSmartReplies, + numSmartActions, fromAssistant); + } } } if (mHeadsUpChild != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java index 67b077e729df..7c30e48f171a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java @@ -76,11 +76,14 @@ public class LightBarController implements BatteryController.BatteryStateChangeC private final Rect mLastDockedBounds = new Rect(); private boolean mQsCustomizing; + private final Context mContext; + public LightBarController(Context ctx) { mDarkModeColor = Color.valueOf(ctx.getColor(R.color.dark_mode_icon_color_single_tone)); mStatusBarIconController = Dependency.get(DarkIconDispatcher.class); mBatteryController = Dependency.get(BatteryController.class); mBatteryController.addCallback(this); + mContext = ctx; } public void setNavigationBar(LightBarTransitionsController navigationBar) { @@ -217,8 +220,9 @@ public class LightBarController implements BatteryController.BatteryStateChangeC private void updateNavigation() { if (mNavigationBarController != null) { - mNavigationBarController.setIconsDark( - mNavigationLight, animateChange()); + if (!NavBarTintController.isEnabled(mContext)) { + mNavigationBarController.setIconsDark(mNavigationLight, animateChange()); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java index 57cc7d6c1ecb..7876aa5d89d0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java @@ -16,12 +16,16 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.statusbar.phone.NavBarTintController.MIN_COLOR_ADAPT_TRANSITION_TIME; +import static com.android.systemui.statusbar.phone.NavBarTintController.NAV_COLOR_TRANSITION_TIME_SETTING; + import android.animation.ValueAnimator; import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.os.SystemClock; import android.util.MathUtils; +import android.provider.Settings; import android.util.TimeUtils; import com.android.systemui.Dependency; @@ -42,13 +46,14 @@ import java.io.PrintWriter; public class LightBarTransitionsController implements Dumpable, Callbacks, StatusBarStateController.StateListener { - public static final long DEFAULT_TINT_ANIMATION_DURATION = 120; + public static final int DEFAULT_TINT_ANIMATION_DURATION = 120; private static final String EXTRA_DARK_INTENSITY = "dark_intensity"; private final Handler mHandler; private final DarkIntensityApplier mApplier; private final KeyguardMonitor mKeyguardMonitor; private final StatusBarStateController mStatusBarStateController; + private NavBarTintController mColorAdaptionController; private boolean mTransitionDeferring; private long mTransitionDeferringStartTime; @@ -67,6 +72,8 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, } }; + private final Context mContext; + public LightBarTransitionsController(Context context, DarkIntensityApplier applier) { mApplier = applier; mHandler = new Handler(); @@ -76,6 +83,7 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, .addCallback(this); mStatusBarStateController.addCallback(this); mDozeAmount = mStatusBarStateController.getDozeAmount(); + mContext = context; } public void destroy(Context context) { @@ -106,7 +114,7 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, public void appTransitionCancelled() { if (mTransitionPending && mTintChangePending) { mTintChangePending = false; - animateIconTint(mPendingDarkIntensity, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION); + animateIconTint(mPendingDarkIntensity, 0 /* delay */, getTintAnimationDuration()); } mTransitionPending = false; } @@ -146,8 +154,17 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, Math.max(0, mTransitionDeferringStartTime - SystemClock.uptimeMillis()), mTransitionDeferringDuration); } else { - animateIconTint(dark ? 1.0f : 0.0f, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION); + animateIconTint(dark ? 1.0f : 0.0f, 0 /* delay */, getTintAnimationDuration()); + } + } + + public long getTintAnimationDuration() { + if (NavBarTintController.isEnabled(mContext)) { + return Math.max(Settings.Global.getInt(mContext.getContentResolver(), + NAV_COLOR_TRANSITION_TIME_SETTING, DEFAULT_TINT_ANIMATION_DURATION), + MIN_COLOR_ADAPT_TRANSITION_TIME); } + return DEFAULT_TINT_ANIMATION_DURATION; } public float getCurrentDarkIntensity() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java new file mode 100644 index 000000000000..9ecee1825f07 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Color; +import android.graphics.Rect; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.provider.Settings; +import android.util.DisplayMetrics; +import android.view.SurfaceControl; + +public class NavBarTintController { + public static final String NAV_COLOR_TRANSITION_TIME_SETTING = "navbar_color_adapt_transition"; + public static final int MIN_COLOR_ADAPT_TRANSITION_TIME = 400; + + private final HandlerThread mColorAdaptHandlerThread = new HandlerThread("ColorExtractThread"); + private Handler mColorAdaptionHandler; + + // Poll time for each iteration to color sample + private static final int COLOR_ADAPTION_TIMEOUT = 300; + + // Passing the threshold of this luminance value will make the button black otherwise white + private static final float LUMINANCE_THRESHOLD = 0.3f; + + // The home button's icon is actually smaller than the button's size, the percentage will + // cut into the button's size to determine the icon size + private static final float PERCENTAGE_BUTTON_PADDING = 0.3f; + + // The distance from the home button to color sample around + private static final int COLOR_SAMPLE_MARGIN = 20; + + private boolean mRunning; + + private final NavigationBarView mNavigationBarView; + private final LightBarTransitionsController mLightBarController; + private final Handler mMainHandler = new Handler(Looper.getMainLooper()); + + public NavBarTintController(NavigationBarView navigationBarView, + LightBarTransitionsController lightBarController) { + mNavigationBarView = navigationBarView; + mLightBarController = lightBarController; + } + + public void start() { + if (!isEnabled(mNavigationBarView.getContext())) { + return; + } + if (mColorAdaptionHandler == null) { + mColorAdaptHandlerThread.start(); + mColorAdaptionHandler = new Handler(mColorAdaptHandlerThread.getLooper()); + } + mColorAdaptionHandler.removeCallbacksAndMessages(null); + mColorAdaptionHandler.post(this::updateTint); + mRunning = true; + } + + public void end() { + if (mColorAdaptionHandler != null) { + mColorAdaptionHandler.removeCallbacksAndMessages(null); + } + mRunning = false; + } + + public void stop() { + end(); + if (mColorAdaptionHandler != null) { + mColorAdaptHandlerThread.quitSafely(); + } + } + + private void updateTint() { + int[] navPos = new int[2]; + int[] butPos = new int[2]; + if (mNavigationBarView.getHomeButton().getCurrentView() == null) { + return; + } + + // Determine the area of the home icon in the larger home button + mNavigationBarView.getHomeButton().getCurrentView().getLocationInSurface(butPos); + final int navWidth = mNavigationBarView.getHomeButton().getCurrentView().getWidth(); + final int navHeight = mNavigationBarView.getHomeButton().getCurrentView().getHeight(); + final int xPadding = (int) (PERCENTAGE_BUTTON_PADDING * navWidth); + final int yPadding = (int) (PERCENTAGE_BUTTON_PADDING * navHeight); + final Rect homeButtonRect = new Rect(butPos[0] + xPadding, butPos[1] + yPadding, + navWidth + butPos[0] - xPadding, navHeight + butPos[1] - yPadding); + if (mNavigationBarView.getCurrentView() == null || homeButtonRect.isEmpty()) { + scheduleColorAdaption(); + return; + } + mNavigationBarView.getCurrentView().getLocationOnScreen(navPos); + homeButtonRect.offset(navPos[0], navPos[1]); + + // Apply a margin area around the button region to sample the colors, crop from screenshot + final Rect cropRect = new Rect(homeButtonRect); + cropRect.inset(-COLOR_SAMPLE_MARGIN, -COLOR_SAMPLE_MARGIN); + if (cropRect.isEmpty()) { + scheduleColorAdaption(); + return; + } + + // Determine the size of the home area + Rect homeArea = new Rect(COLOR_SAMPLE_MARGIN, COLOR_SAMPLE_MARGIN, + homeButtonRect.width() + COLOR_SAMPLE_MARGIN, + homeButtonRect.height() + COLOR_SAMPLE_MARGIN); + + // Get the screenshot around the home button icon to determine the color + DisplayMetrics mDisplayMetrics = new DisplayMetrics(); + mNavigationBarView.getContext().getDisplay().getRealMetrics(mDisplayMetrics); + final Bitmap hardBitmap = SurfaceControl + .screenshot(new Rect(), mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels, + mNavigationBarView.getContext().getDisplay().getRotation()); + if (hardBitmap != null && cropRect.bottom <= hardBitmap.getHeight()) { + final Bitmap cropBitmap = Bitmap.createBitmap(hardBitmap, cropRect.left, cropRect.top, + cropRect.width(), cropRect.height()); + final Bitmap softBitmap = cropBitmap.copy(Config.ARGB_8888, false); + + // Get the luminance value to determine if the home button should be black or white + final int[] pixels = new int[softBitmap.getByteCount() / 4]; + softBitmap.getPixels(pixels, 0, softBitmap.getWidth(), 0, 0, softBitmap.getWidth(), + softBitmap.getHeight()); + float r = 0, g = 0, blue = 0; + + int width = cropRect.width(); + int total = 0; + for (int i = 0; i < pixels.length; i += 4) { + int x = i % width; + int y = i / width; + if (!homeArea.contains(x, y)) { + r += Color.red(pixels[i]); + g += Color.green(pixels[i]); + blue += Color.blue(pixels[i]); + total++; + } + } + + r /= total; + g /= total; + blue /= total; + + r = Math.max(Math.min(r / 255f, 1), 0); + g = Math.max(Math.min(g / 255f, 1), 0); + blue = Math.max(Math.min(blue / 255f, 1), 0); + + if (r <= 0.03928) { + r /= 12.92; + } else { + r = (float) Math.pow((r + 0.055) / 1.055, 2.4); + } + if (g <= 0.03928) { + g /= 12.92; + } else { + g = (float) Math.pow((g + 0.055) / 1.055, 2.4); + } + if (blue <= 0.03928) { + blue /= 12.92; + } else { + blue = (float) Math.pow((blue + 0.055) / 1.055, 2.4); + } + + if (r * 0.2126 + g * 0.7152 + blue * 0.0722 > LUMINANCE_THRESHOLD) { + // Black + mMainHandler.post( + () -> mLightBarController + .setIconsDark(true /* dark */, true /* animate */)); + } else { + // White + mMainHandler.post( + () -> mLightBarController + .setIconsDark(false /* dark */, true /* animate */)); + } + cropBitmap.recycle(); + hardBitmap.recycle(); + } + scheduleColorAdaption(); + } + + private void scheduleColorAdaption() { + mColorAdaptionHandler.removeCallbacksAndMessages(null); + if (!mRunning || !isEnabled(mNavigationBarView.getContext())) { + return; + } + mColorAdaptionHandler.postDelayed(this::updateTint, COLOR_ADAPTION_TIMEOUT); + } + + public static boolean isEnabled(Context context) { + return Settings.Global.getInt(context.getContentResolver(), + NavigationPrototypeController.NAV_COLOR_ADAPT_ENABLE_SETTING, 0) == 1; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index ae0a1452905d..55655d5b6240 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -851,6 +851,16 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback if (Intent.ACTION_SCREEN_OFF.equals(action) || Intent.ACTION_SCREEN_ON.equals(action)) { notifyNavigationBarScreenOn(); + + if (Intent.ACTION_SCREEN_ON.equals(action)) { + // Enabled and screen is on, start it again if enabled + if (NavBarTintController.isEnabled(getContext())) { + mNavigationBarView.getColorAdaptionController().start(); + } + } else { + // Screen off disable it + mNavigationBarView.getColorAdaptionController().end(); + } } if (Intent.ACTION_USER_SWITCHED.equals(action)) { // The accessibility settings may be different for the new user diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 30e840926698..6a7983af862d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -149,6 +149,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav private RecentsOnboarding mRecentsOnboarding; private NotificationPanelView mPanelView; + private NavBarTintController mColorAdaptionController; private NavigationPrototypeController mPrototypeController; private NavigationGestureAction[] mDefaultGestureMap; private QuickScrubAction mQuickScrubAction; @@ -277,6 +278,15 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav public void onBackButtonVisibilityChanged(boolean visible) { getBackButton().setVisibility(visible ? VISIBLE : GONE); } + + @Override + public void onColorAdaptChanged(boolean enabled) { + if (enabled) { + mColorAdaptionController.start(); + } else { + mColorAdaptionController.end(); + } + } }; public NavigationBarView(Context context, AttributeSet attrs) { @@ -334,6 +344,11 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mPrototypeController = new NavigationPrototypeController(mHandler, mContext); mPrototypeController.register(); mPrototypeController.setOnPrototypeChangedListener(mPrototypeListener); + mColorAdaptionController = new NavBarTintController(this, getLightTransitionsController()); + } + + public NavBarTintController getColorAdaptionController() { + return mColorAdaptionController; } public BarTransitions getBarTransitions() { @@ -1097,6 +1112,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav Dependency.get(PluginManager.class).addPluginListener(this, NavGesture.class, false /* Only one */); setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled()); + mColorAdaptionController.start(); } @Override @@ -1107,6 +1123,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mGestureHelper.destroy(); } mPrototypeController.unregister(); + mColorAdaptionController.stop(); setUpSwipeUpOnboarding(false); for (int i = 0; i < mButtonDispatchers.size(); ++i) { mButtonDispatchers.valueAt(i).onDestroy(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java index b11b6d472713..40ac79376b06 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java @@ -37,6 +37,7 @@ public class NavigationPrototypeController extends ContentObserver { static final String NAVBAR_EXPERIMENTS_DISABLED = "navbarexperiments_disabled"; private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map"; + public static final String NAV_COLOR_ADAPT_ENABLE_SETTING = "navbar_color_adapt_enable"; @Retention(RetentionPolicy.SOURCE) @IntDef({ACTION_DEFAULT, ACTION_QUICKSTEP, ACTION_QUICKSCRUB, ACTION_BACK}) @@ -73,6 +74,7 @@ public class NavigationPrototypeController extends ContentObserver { public void register() { registerObserver(HIDE_BACK_BUTTON_SETTING); registerObserver(GESTURE_MATCH_SETTING); + registerObserver(NAV_COLOR_ADAPT_ENABLE_SETTING); } /** @@ -96,6 +98,9 @@ public class NavigationPrototypeController extends ContentObserver { } else if (path.endsWith(HIDE_BACK_BUTTON_SETTING)) { mListener.onBackButtonVisibilityChanged( !getGlobalBool(HIDE_BACK_BUTTON_SETTING)); + } else if (path.endsWith(NAV_COLOR_ADAPT_ENABLE_SETTING)) { + mListener.onColorAdaptChanged( + NavBarTintController.isEnabled(mContext)); } } catch (SettingNotFoundException e) { e.printStackTrace(); @@ -138,5 +143,6 @@ public class NavigationPrototypeController extends ContentObserver { public interface OnPrototypeChangedListener { void onGestureRemap(@GestureAction int[] actions); void onBackButtonVisibilityChanged(boolean visible); + void onColorAdaptChanged(boolean enabled); } } 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 6caea1d7a8c7..1e709125c7ba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -187,6 +187,7 @@ import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.NotificationActivityStarter; +import com.android.systemui.statusbar.notification.NotificationClicker; import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationData.Entry; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -630,8 +631,6 @@ public class StatusBar extends SystemUI implements DemoMode, mBubbleController = Dependency.get(BubbleController.class); mBubbleController.setExpandListener(mBubbleExpandListener); - mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager); - mColorExtractor.addOnColorsChangedListener(this); mStatusBarStateController.addCallback(this, StatusBarStateController.RANK_STATUS_BAR); @@ -1018,7 +1017,7 @@ public class StatusBar extends SystemUI implements DemoMode, return new QSFragment(); } - protected void setUpPresenter() { + private void setUpPresenter() { // Set up the initial notification state. mActivityLaunchAnimator = new ActivityLaunchAnimator( mStatusBarWindow, this, mNotificationPanel, @@ -1036,7 +1035,10 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationActivityStarter = new StatusBarNotificationActivityStarter( mContext, mNotificationPanel, mPresenter, mHeadsUpManager, mActivityLaunchAnimator); mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter); - mEntryManager.setNotificationActivityStarter(mNotificationActivityStarter); + + mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager); + mEntryManager.setNotificationClicker(new NotificationClicker( + this, Dependency.get(BubbleController.class), mNotificationActivityStarter)); } /** @@ -2230,6 +2232,11 @@ public class StatusBar extends SystemUI implements DemoMode, mNavigationBar.getBarTransitions().setAutoDim(false); } mHandler.removeCallbacks(mAutoDim); + + // Do not dim the navigation buttons if the its tint is controlled by the bar's background + if (NavBarTintController.isEnabled(mContext)) { + return; + } if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) { mHandler.postDelayed(mAutoDim, AUTOHIDE_TIMEOUT_MS); } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java index dae1472c42af..0a29e04ce20f 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java @@ -184,7 +184,11 @@ public class PluginFragment extends PreferenceFragment { mInfo.services[i].name); if (mPluginEnabler.isEnabled(componentName) != isEnabled) { - mPluginEnabler.setEnabled(componentName, isEnabled); + if (isEnabled) { + mPluginEnabler.setEnabled(componentName); + } else { + mPluginEnabler.setDisabled(componentName, PluginEnabler.DISABLED_MANUALLY); + } shouldSendBroadcast = true; } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java index 7ca54231fe7b..fb2ceac4b810 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java @@ -35,6 +35,7 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; import android.text.TextPaint; import android.view.LayoutInflater; +import android.widget.FrameLayout; import android.widget.TextClock; import com.android.systemui.SysuiTestCase; @@ -51,10 +52,14 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWithLooper @RunWith(AndroidTestingRunner.class) +// Need to run on the main thread because KeyguardSliceView$Row init checks for +// the main thread before acquiring a wake lock. This class is constructed when +// the keyguard_clcok_switch layout is inflated. +@RunWithLooper(setAsMainLooper = true) public class KeyguardClockSwitchTest extends SysuiTestCase { private PluginManager mPluginManager; + private FrameLayout mClockContainer; @Mock TextClock mClockView; @@ -67,6 +72,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { LayoutInflater layoutInflater = LayoutInflater.from(getContext()); mKeyguardClockSwitch = (KeyguardClockSwitch) layoutInflater.inflate(R.layout.keyguard_clock_switch, null); + mClockContainer = mKeyguardClockSwitch.findViewById(R.id.clock_view); MockitoAnnotations.initMocks(this); when(mClockView.getPaint()).thenReturn(mock(TextPaint.class)); } @@ -97,7 +103,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { listener.onPluginConnected(plugin, null); verify(mClockView).setVisibility(GONE); - assertThat(plugin.getView().getParent()).isEqualTo(mKeyguardClockSwitch); + assertThat(plugin.getView().getParent()).isEqualTo(mClockContainer); } @Test @@ -120,7 +126,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { when(plugin2.getView()).thenReturn(new TextClock(getContext())); listener.onPluginConnected(plugin2, null); // THEN only the view from the second plugin should be a child of KeyguardClockSwitch. - assertThat(plugin2.getView().getParent()).isEqualTo(mKeyguardClockSwitch); + assertThat(plugin2.getView().getParent()).isEqualTo(mClockContainer); assertThat(plugin1.getView().getParent()).isNull(); } @@ -161,7 +167,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { // WHEN the first plugin is disconnected listener.onPluginDisconnected(plugin1); // THEN the view from the second plugin is still a child of KeyguardClockSwitch. - assertThat(plugin2.getView().getParent()).isEqualTo(mKeyguardClockSwitch); + assertThat(plugin2.getView().getParent()).isEqualTo(mClockContainer); assertThat(plugin1.getView().getParent()).isNull(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java index 5cc3b3c7823b..458377017fb9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java @@ -17,6 +17,7 @@ package com.android.systemui.shared.plugins; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; + import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; @@ -26,22 +27,6 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.plugins.Plugin; -import com.android.systemui.plugins.PluginEnablerImpl; -import com.android.systemui.plugins.PluginListener; -import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo; -import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException; -import com.android.systemui.plugins.annotations.Requires; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mockito; - import android.app.Activity; import android.app.NotificationManager; import android.content.BroadcastReceiver; @@ -60,6 +45,21 @@ import android.support.test.annotation.UiThreadTest; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; +import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.Plugin; +import com.android.systemui.plugins.PluginListener; +import com.android.systemui.plugins.annotations.Requires; +import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo; +import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -79,6 +79,9 @@ public class PluginInstanceManagerTest extends SysuiTestCase { private PluginInstanceManager mPluginInstanceManager; private PluginManagerImpl mMockManager; private VersionInfo mMockVersionInfo; + private PluginEnabler mMockEnabler; + ComponentName mTestPluginComponentName = + new ComponentName(WHITELISTED_PACKAGE, TestPlugin.class.getName()); @Before public void setup() throws Exception { @@ -88,9 +91,9 @@ public class PluginInstanceManagerTest extends SysuiTestCase { mMockPm = mock(PackageManager.class); mMockListener = mock(PluginListener.class); mMockManager = mock(PluginManagerImpl.class); - when(mMockManager.getClassLoader(any(), any())) - .thenReturn(getClass().getClassLoader()); - when(mMockManager.getPluginEnabler()).thenReturn(new PluginEnablerImpl(mMockPm)); + when(mMockManager.getClassLoader(any(), any())).thenReturn(getClass().getClassLoader()); + mMockEnabler = mock(PluginEnabler.class); + when(mMockManager.getPluginEnabler()).thenReturn(mMockEnabler); mMockVersionInfo = mock(VersionInfo.class); mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction", mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo, @@ -230,18 +233,13 @@ public class PluginInstanceManagerTest extends SysuiTestCase { // Start with an unrelated class. boolean result = mPluginInstanceManager.checkAndDisable(Activity.class.getName()); assertFalse(result); - verify(mMockPm, never()).setComponentEnabledSetting( - ArgumentCaptor.forClass(ComponentName.class).capture(), - ArgumentCaptor.forClass(int.class).capture(), - ArgumentCaptor.forClass(int.class).capture()); + verify(mMockEnabler, never()).setDisabled(any(ComponentName.class), anyInt()); // Now hand it a real class and make sure it disables the plugin. result = mPluginInstanceManager.checkAndDisable(TestPlugin.class.getName()); assertTrue(result); - verify(mMockPm).setComponentEnabledSetting( - ArgumentCaptor.forClass(ComponentName.class).capture(), - ArgumentCaptor.forClass(int.class).capture(), - ArgumentCaptor.forClass(int.class).capture()); + verify(mMockEnabler).setDisabled( + mTestPluginComponentName, PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH); } @Test @@ -250,10 +248,8 @@ public class PluginInstanceManagerTest extends SysuiTestCase { mPluginInstanceManager.disableAll(); - verify(mMockPm).setComponentEnabledSetting( - ArgumentCaptor.forClass(ComponentName.class).capture(), - ArgumentCaptor.forClass(int.class).capture(), - ArgumentCaptor.forClass(int.class).capture()); + verify(mMockEnabler).setDisabled( + mTestPluginComponentName, PluginEnabler.DISABLED_FROM_SYSTEM_CRASH); } @Test @@ -275,8 +271,8 @@ public class PluginInstanceManagerTest extends SysuiTestCase { List<ResolveInfo> list = new ArrayList<>(); ResolveInfo info = new ResolveInfo(); info.serviceInfo = mock(ServiceInfo.class); - info.serviceInfo.packageName = "com.android.systemui"; - info.serviceInfo.name = TestPlugin.class.getName(); + info.serviceInfo.packageName = mTestPluginComponentName.getPackageName(); + info.serviceInfo.name = mTestPluginComponentName.getClassName(); when(info.serviceInfo.loadLabel(any())).thenReturn("Test Plugin"); list.add(info); when(mMockPm.queryIntentServices(any(), Mockito.anyInt())).thenReturn(list); @@ -288,6 +284,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase { ApplicationInfo appInfo = getContext().getApplicationInfo(); when(mMockPm.getApplicationInfo(Mockito.anyString(), Mockito.anyInt())).thenReturn( appInfo); + when(mMockEnabler.isEnabled(mTestPluginComponentName)).thenReturn(true); } private void createPlugin() throws Exception { diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java index ff1bc8abf5d1..76e68f1df724 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java @@ -27,7 +27,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; -import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -82,17 +81,18 @@ public class PluginManagerTest extends SysuiTestCase { .thenReturn(mMockPluginInstance); mMockPackageManager = mock(PackageManager.class); - mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, true, + mPluginManager = new PluginManagerImpl( + getContext(), mMockFactory, true, mMockExceptionHandler, new PluginInitializerImpl() { - @Override - public String[] getWhitelistedPlugins(Context context) { - return new String[0]; - } - - @Override - public PluginEnabler getPluginEnabler(Context context) { - return new PluginEnablerImpl(mMockPackageManager); - } + @Override + public String[] getWhitelistedPlugins(Context context) { + return new String[0]; + } + + @Override + public PluginEnabler getPluginEnabler(Context context) { + return new PluginEnablerImpl(context, mMockPackageManager); + } }); resetExceptionHandler(); mMockListener = mock(PluginListener.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java index 8d52ccd71808..14e611a26b4c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java @@ -51,6 +51,7 @@ public class SmartReplyControllerTest extends SysuiTestCase { private static final String TEST_CHOICE_TEXT = "A Reply"; private static final int TEST_CHOICE_INDEX = 2; private static final int TEST_CHOICE_COUNT = 4; + private static final int TEST_ACTION_COUNT = 3; private Notification mNotification; private NotificationData.Entry mEntry; @@ -117,12 +118,14 @@ public class SmartReplyControllerTest extends SysuiTestCase { } @Test - public void testShowSmartReply_logsToStatusBar() throws RemoteException { - mSmartReplyController.smartRepliesAdded(mEntry, TEST_CHOICE_COUNT); + public void testShowSmartSuggestions_logsToStatusBar() throws RemoteException { + final boolean generatedByAsssistant = true; + mSmartReplyController.smartSuggestionsAdded(mEntry, TEST_CHOICE_COUNT, TEST_ACTION_COUNT, + generatedByAsssistant); // Check we log the result to the status bar service. - verify(mIStatusBarService).onNotificationSmartRepliesAdded(mSbn.getKey(), - TEST_CHOICE_COUNT); + verify(mIStatusBarService).onNotificationSmartSuggestionsAdded(mSbn.getKey(), + TEST_CHOICE_COUNT, TEST_ACTION_COUNT, generatedByAsssistant); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index 904e5b99b0ab..7f0e435eb7da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -231,6 +231,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { mEntryManager = new TestableNotificationEntryManager(mContext, mBarService); Dependency.get(InitController.class).executePostInitTasks(); mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mCallback, mHeadsUpManager); + mEntryManager.setNotificationClicker(mock(NotificationClicker.class)); setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL); } diff --git a/packages/overlays/AccentColorBlackOverlay/Android.mk b/packages/overlays/AccentColorBlackOverlay/Android.mk index d316fbd8907c..b81ae5bbe3fa 100644 --- a/packages/overlays/AccentColorBlackOverlay/Android.mk +++ b/packages/overlays/AccentColorBlackOverlay/Android.mk @@ -19,6 +19,7 @@ include $(CLEAR_VARS) LOCAL_RRO_THEME := AccentColorBlack LOCAL_CERTIFICATE := platform +LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/AccentColorGreenOverlay/Android.mk b/packages/overlays/AccentColorGreenOverlay/Android.mk index afc42873a4d6..db92157c8fdf 100644 --- a/packages/overlays/AccentColorGreenOverlay/Android.mk +++ b/packages/overlays/AccentColorGreenOverlay/Android.mk @@ -19,6 +19,7 @@ include $(CLEAR_VARS) LOCAL_RRO_THEME := AccentColorGreen LOCAL_CERTIFICATE := platform +LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/AccentColorPurpleOverlay/Android.mk b/packages/overlays/AccentColorPurpleOverlay/Android.mk index 336616921d71..d7dc4978e2ca 100644 --- a/packages/overlays/AccentColorPurpleOverlay/Android.mk +++ b/packages/overlays/AccentColorPurpleOverlay/Android.mk @@ -19,6 +19,7 @@ include $(CLEAR_VARS) LOCAL_RRO_THEME := AccentColorPurple LOCAL_CERTIFICATE := platform +LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/CleanSpec.mk b/packages/overlays/CleanSpec.mk new file mode 100644 index 000000000000..16fbaa202aa1 --- /dev/null +++ b/packages/overlays/CleanSpec.mk @@ -0,0 +1,53 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# If you don't need to do a full clean build but would like to touch +# a file or delete some intermediate files, add a clean step to the end +# of the list. These steps will only be run once, if they haven't been +# run before. +# +# E.g.: +# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) +# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) +# +# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with +# files that are missing or have been moved. +# +# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. +# Use $(OUT_DIR) to refer to the "out" directory. +# +# If you need to re-do something that's already mentioned, just copy +# the command and add it to the bottom of the list. E.g., if a change +# that you made last week required touching a file and a change you +# made today requires touching the same file, just copy the old +# touch step and add it to the end of the list. +# +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ + +# For example: +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) +#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) +#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/AccentColor*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/DisplayCutout*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/IconShape*) + +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk index 74c43b40616f..bf2b6312d68f 100644 --- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk +++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk @@ -4,6 +4,8 @@ include $(CLEAR_VARS) LOCAL_RRO_THEME := DisplayCutoutEmulationCorner LOCAL_CERTIFICATE := platform +LOCAL_PRODUCT_MODULE := true + LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk index d83b30a8785a..70429064ec2b 100644 --- a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk +++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk @@ -4,6 +4,8 @@ include $(CLEAR_VARS) LOCAL_RRO_THEME := DisplayCutoutEmulationDouble LOCAL_CERTIFICATE := platform +LOCAL_PRODUCT_MODULE := true + LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk index f5afad24676f..ae69e1137e60 100644 --- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk +++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk @@ -4,6 +4,8 @@ include $(CLEAR_VARS) LOCAL_RRO_THEME := DisplayCutoutEmulationNarrow LOCAL_CERTIFICATE := platform +LOCAL_PRODUCT_MODULE := true + LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk index f1f8c27d94f1..7dcadfbd4708 100644 --- a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk +++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk @@ -4,6 +4,8 @@ include $(CLEAR_VARS) LOCAL_RRO_THEME := DisplayCutoutEmulationTall LOCAL_CERTIFICATE := platform +LOCAL_PRODUCT_MODULE := true + LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk index d149d8ecf4df..3f7be73aa5fa 100644 --- a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk +++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk @@ -4,6 +4,8 @@ include $(CLEAR_VARS) LOCAL_RRO_THEME := DisplayCutoutEmulationWide LOCAL_CERTIFICATE := platform +LOCAL_PRODUCT_MODULE := true + LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res diff --git a/packages/overlays/IconShapeRoundedRectOverlay/Android.mk b/packages/overlays/IconShapeRoundedRectOverlay/Android.mk index a734a6b46947..08428d192fae 100644 --- a/packages/overlays/IconShapeRoundedRectOverlay/Android.mk +++ b/packages/overlays/IconShapeRoundedRectOverlay/Android.mk @@ -19,6 +19,7 @@ include $(CLEAR_VARS) LOCAL_RRO_THEME := IconShapeRoundedRect LOCAL_CERTIFICATE := platform +LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/IconShapeSquareOverlay/Android.mk b/packages/overlays/IconShapeSquareOverlay/Android.mk index 217da9feb534..ceb745ae1429 100644 --- a/packages/overlays/IconShapeSquareOverlay/Android.mk +++ b/packages/overlays/IconShapeSquareOverlay/Android.mk @@ -19,6 +19,7 @@ include $(CLEAR_VARS) LOCAL_RRO_THEME := IconShapeSquare LOCAL_CERTIFICATE := platform +LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/IconShapeSquircleOverlay/Android.mk b/packages/overlays/IconShapeSquircleOverlay/Android.mk index fd3bfa06de83..34edc3b78b09 100644 --- a/packages/overlays/IconShapeSquircleOverlay/Android.mk +++ b/packages/overlays/IconShapeSquircleOverlay/Android.mk @@ -19,6 +19,7 @@ include $(CLEAR_VARS) LOCAL_RRO_THEME := IconShapeSquircle LOCAL_CERTIFICATE := platform +LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/IconShapeTeardropOverlay/Android.mk b/packages/overlays/IconShapeTeardropOverlay/Android.mk index ea43423f93ba..834a1c357c61 100644 --- a/packages/overlays/IconShapeTeardropOverlay/Android.mk +++ b/packages/overlays/IconShapeTeardropOverlay/Android.mk @@ -19,6 +19,7 @@ include $(CLEAR_VARS) LOCAL_RRO_THEME := IconShapeTeardrop LOCAL_CERTIFICATE := platform +LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index 66d64b1a5f7a..529d78f577ed 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -6649,6 +6649,21 @@ message MetricsEvent { // OS: Q QS_SENSOR_PRIVACY = 1598; + // Tagged data for SMART_REPLY_VISIBLE. Count of number of smart actions. + // OS: Q + NOTIFICATION_SMART_ACTION_COUNT = 1599; + + // Tagged data for SMART_REPLY_VISIBLE and NOTIFICATION_ITEM_ACTION. + // Whether the notification has notification-assistant generated + // actions/replies. + // OS: Q + NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED = 1600; + + // Tagged data for NOTIFICATION_ITEM_ACTION. Whether the action is a smart + // action. + // OS: Q + NOTIFICATION_ACTION_IS_SMART = 1601; + // ---- End Q Constants, all Q constants go above this line ---- // Add new aosp constants above this line. diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 2cbab492a605..a533640b526b 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -16,10 +16,13 @@ package com.android.server.backup; +import static com.android.internal.util.Preconditions.checkNotNull; + import android.Manifest; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; +import android.app.backup.BackupManager; import android.app.backup.IBackupManagerMonitor; import android.app.backup.IBackupObserver; import android.app.backup.IFullBackupRestoreObserver; @@ -41,6 +44,7 @@ import android.os.RemoteException; import android.os.Trace; import android.os.UserHandle; import android.util.Slog; +import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; import com.android.server.SystemConfig; @@ -83,22 +87,27 @@ public class BackupManagerService { } private final Context mContext; - private UserBackupManagerService mUserBackupManagerService; + private final Trampoline mTrampoline; + private final HandlerThread mBackupThread; + + // Keeps track of all unlocked users registered with this service. Indexed by user id. + private final SparseArray<UserBackupManagerService> mServiceUsers = new SparseArray<>(); + + private Set<ComponentName> mTransportWhitelist; /** Instantiate a new instance of {@link BackupManagerService}. */ public BackupManagerService( Context context, Trampoline trampoline, HandlerThread backupThread) { - // Set up our transport options and initialize the default transport + mContext = checkNotNull(context); + mTrampoline = checkNotNull(trampoline); + mBackupThread = checkNotNull(backupThread); + + // Set up our transport options. SystemConfig systemConfig = SystemConfig.getInstance(); - Set<ComponentName> transportWhitelist = systemConfig.getBackupTransportWhitelist(); - if (transportWhitelist == null) { - transportWhitelist = Collections.emptySet(); + mTransportWhitelist = systemConfig.getBackupTransportWhitelist(); + if (mTransportWhitelist == null) { + mTransportWhitelist = Collections.emptySet(); } - - mContext = context; - mUserBackupManagerService = - UserBackupManagerService.createAndInitializeService( - context, trampoline, backupThread, transportWhitelist); } /** @@ -115,12 +124,6 @@ public class BackupManagerService { } } - // TODO(b/118520567): Remove when tests are modified to use per-user instance. - @VisibleForTesting - void setUserBackupManagerService(UserBackupManagerService userBackupManagerService) { - mUserBackupManagerService = userBackupManagerService; - } - /** * Called through Trampoline from {@link Lifecycle#onUnlockUser(int)}. We run the heavy work on * a background thread to keep the unlock time down. @@ -139,9 +142,42 @@ public class BackupManagerService { * Starts the backup service for user {@code userId} by creating a new instance of {@link * UserBackupManagerService} and registering it with this service. */ - // TODO(b/120212806): Add UserBackupManagerService initialization logic. - void startServiceForUser(int userId) { - // Intentionally empty. + @VisibleForTesting + protected void startServiceForUser(int userId) { + UserBackupManagerService userBackupManagerService = + UserBackupManagerService.createAndInitializeService( + mContext, mTrampoline, mBackupThread, mTransportWhitelist); + startServiceForUser(userId, userBackupManagerService); + } + + /** + * Starts the backup service for user {@code userId} by registering its instance of {@link + * UserBackupManagerService} with this service. + */ + void startServiceForUser(int userId, UserBackupManagerService userBackupManagerService) { + mServiceUsers.put(userId, userBackupManagerService); + } + + SparseArray<UserBackupManagerService> getServiceUsers() { + return mServiceUsers; + } + + /** + * Returns the {@link UserBackupManagerService} instance for the specified user {@code userId}. + * If the user is not registered with the service (either the user is locked or not eligible for + * the backup service) then return {@code null}. + * + * @param userId The id of the user to retrieve its instance of {@link + * UserBackupManagerService}. + * @param caller A {@link String} identifying the caller for logging purposes. + */ + @Nullable + private UserBackupManagerService getServiceForUser(@UserIdInt int userId, String caller) { + UserBackupManagerService userBackupManagerService = mServiceUsers.get(userId); + if (userBackupManagerService == null) { + Slog.w(TAG, "Called " + caller + " for unknown user: " + userId); + } + return userBackupManagerService; } /* @@ -149,7 +185,7 @@ public class BackupManagerService { * They delegate to the appropriate per-user instance of UserBackupManagerService to perform the * action on the passed in user. Currently this is a straight redirection (see TODO). */ - // TODO (b/118520567): Take in user id and call per-user instance of UserBackupManagerService. + // TODO (b/118520567): Stop hardcoding system user when we pass in user id as a parameter // --------------------------------------------- // BACKUP AGENT OPERATIONS @@ -161,7 +197,12 @@ public class BackupManagerService { * backup. */ public void dataChanged(String packageName) { - mUserBackupManagerService.dataChanged(packageName); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "dataChanged()"); + + if (userBackupManagerService != null) { + userBackupManagerService.dataChanged(packageName); + } } /** @@ -169,7 +210,12 @@ public class BackupManagerService { * {@link ActivityManager}. */ public void agentConnected(String packageName, IBinder agentBinder) { - mUserBackupManagerService.agentConnected(packageName, agentBinder); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "agentConnected()"); + + if (userBackupManagerService != null) { + userBackupManagerService.agentConnected(packageName, agentBinder); + } } /** @@ -177,7 +223,12 @@ public class BackupManagerService { * called from the {@link ActivityManager}. */ public void agentDisconnected(String packageName) { - mUserBackupManagerService.agentDisconnected(packageName); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "agentDisconnected()"); + + if (userBackupManagerService != null) { + userBackupManagerService.agentDisconnected(packageName); + } } /** @@ -185,7 +236,12 @@ public class BackupManagerService { * outstanding asynchronous backup/restore operation. */ public void opComplete(int token, long result) { - mUserBackupManagerService.opComplete(token, result); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "opComplete()"); + + if (userBackupManagerService != null) { + userBackupManagerService.opComplete(token, result); + } } // --------------------------------------------- @@ -194,7 +250,12 @@ public class BackupManagerService { /** Run an initialize operation for the given transports {@code transportNames}. */ public void initializeTransports(String[] transportNames, IBackupObserver observer) { - mUserBackupManagerService.initializeTransports(transportNames, observer); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "initializeTransports()"); + + if (userBackupManagerService != null) { + userBackupManagerService.initializeTransports(transportNames, observer); + } } /** @@ -202,35 +263,70 @@ public class BackupManagerService { * transportName}. */ public void clearBackupData(String transportName, String packageName) { - mUserBackupManagerService.clearBackupData(transportName, packageName); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "clearBackupData()"); + + if (userBackupManagerService != null) { + userBackupManagerService.clearBackupData(transportName, packageName); + } } /** Return the name of the currently active transport. */ + @Nullable public String getCurrentTransport() { - return mUserBackupManagerService.getCurrentTransport(); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "getCurrentTransport()"); + + return userBackupManagerService == null + ? null + : userBackupManagerService.getCurrentTransport(); } /** * Returns the {@link ComponentName} of the host service of the selected transport or {@code * null} if no transport selected or if the transport selected is not registered. */ + @Nullable public ComponentName getCurrentTransportComponent() { - return mUserBackupManagerService.getCurrentTransportComponent(); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "getCurrentTransportComponent()"); + + return userBackupManagerService == null + ? null + : userBackupManagerService.getCurrentTransportComponent(); } /** Report all known, available backup transports by name. */ + @Nullable public String[] listAllTransports() { - return mUserBackupManagerService.listAllTransports(); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "listAllTransports()"); + + return userBackupManagerService == null + ? null + : userBackupManagerService.listAllTransports(); } /** Report all known, available backup transports by {@link ComponentName}. */ + @Nullable public ComponentName[] listAllTransportComponents() { - return mUserBackupManagerService.listAllTransportComponents(); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "listAllTransportComponents()"); + + return userBackupManagerService == null + ? null + : userBackupManagerService.listAllTransportComponents(); } /** Report all system whitelisted transports. */ + @Nullable public String[] getTransportWhitelist() { - return mUserBackupManagerService.getTransportWhitelist(); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "getTransportWhitelist()"); + + return userBackupManagerService == null + ? null + : userBackupManagerService.getTransportWhitelist(); } /** @@ -263,13 +359,18 @@ public class BackupManagerService { String currentDestinationString, @Nullable Intent dataManagementIntent, String dataManagementLabel) { - mUserBackupManagerService.updateTransportAttributes( - transportComponent, - name, - configurationIntent, - currentDestinationString, - dataManagementIntent, - dataManagementLabel); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "updateTransportAttributes()"); + + if (userBackupManagerService != null) { + userBackupManagerService.updateTransportAttributes( + transportComponent, + name, + configurationIntent, + currentDestinationString, + dataManagementIntent, + dataManagementLabel); + } } /** @@ -281,7 +382,12 @@ public class BackupManagerService { @Deprecated @Nullable public String selectBackupTransport(String transportName) { - return mUserBackupManagerService.selectBackupTransport(transportName); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "selectBackupTransport()"); + + return userBackupManagerService == null + ? null + : userBackupManagerService.selectBackupTransport(transportName); } /** @@ -290,7 +396,12 @@ public class BackupManagerService { */ public void selectBackupTransportAsync( ComponentName transportComponent, ISelectBackupTransportCallback listener) { - mUserBackupManagerService.selectBackupTransportAsync(transportComponent, listener); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "selectBackupTransportAsync()"); + + if (userBackupManagerService != null) { + userBackupManagerService.selectBackupTransportAsync(transportComponent, listener); + } } /** @@ -298,8 +409,14 @@ public class BackupManagerService { * available transports, or if the transport does not supply any configuration UI, the method * returns {@code null}. */ + @Nullable public Intent getConfigurationIntent(String transportName) { - return mUserBackupManagerService.getConfigurationIntent(transportName); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "getConfigurationIntent()"); + + return userBackupManagerService == null + ? null + : userBackupManagerService.getConfigurationIntent(transportName); } /** @@ -311,21 +428,39 @@ public class BackupManagerService { * @param transportName The name of the registered transport. * @return The current destination string or null if the transport is not registered. */ + @Nullable public String getDestinationString(String transportName) { - return mUserBackupManagerService.getDestinationString(transportName); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "getDestinationString()"); + + return userBackupManagerService == null + ? null + : userBackupManagerService.getDestinationString(transportName); } /** Supply the manage-data intent for the given transport. */ + @Nullable public Intent getDataManagementIntent(String transportName) { - return mUserBackupManagerService.getDataManagementIntent(transportName); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "getDataManagementIntent()"); + + return userBackupManagerService == null + ? null + : userBackupManagerService.getDataManagementIntent(transportName); } /** * Supply the menu label for affordances that fire the manage-data intent for the given * transport. */ + @Nullable public String getDataManagementLabel(String transportName) { - return mUserBackupManagerService.getDataManagementLabel(transportName); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "getDataManagementLabel()"); + + return userBackupManagerService == null + ? null + : userBackupManagerService.getDataManagementLabel(transportName); } // --------------------------------------------- @@ -335,17 +470,32 @@ public class BackupManagerService { /** Enable/disable the backup service. This is user-configurable via backup settings. */ public void setBackupEnabled(@UserIdInt int userId, boolean enable) { enforceCallingPermissionOnUserId(userId, "setBackupEnabled"); - mUserBackupManagerService.setBackupEnabled(enable); + UserBackupManagerService userBackupManagerService = + getServiceForUser(userId, "setBackupEnabled()"); + + if (userBackupManagerService != null) { + userBackupManagerService.setBackupEnabled(enable); + } } /** Enable/disable automatic restore of app data at install time. */ public void setAutoRestore(boolean autoRestore) { - mUserBackupManagerService.setAutoRestore(autoRestore); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "setAutoRestore()"); + + if (userBackupManagerService != null) { + userBackupManagerService.setAutoRestore(autoRestore); + } } /** Mark the backup service as having been provisioned (device has gone through SUW). */ public void setBackupProvisioned(boolean provisioned) { - mUserBackupManagerService.setBackupProvisioned(provisioned); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "setBackupProvisioned()"); + + if (userBackupManagerService != null) { + userBackupManagerService.setBackupProvisioned(provisioned); + } } /** @@ -353,7 +503,10 @@ public class BackupManagerService { */ public boolean isBackupEnabled(@UserIdInt int userId) { enforceCallingPermissionOnUserId(userId, "isBackupEnabled"); - return mUserBackupManagerService.isBackupEnabled(); + UserBackupManagerService userBackupManagerService = + getServiceForUser(userId, "isBackupEnabled()"); + + return userBackupManagerService != null && userBackupManagerService.isBackupEnabled(); } // --------------------------------------------- @@ -362,14 +515,24 @@ public class BackupManagerService { /** Checks if the given package {@code packageName} is eligible for backup. */ public boolean isAppEligibleForBackup(String packageName) { - return mUserBackupManagerService.isAppEligibleForBackup(packageName); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "isAppEligibleForBackup()"); + + return userBackupManagerService != null + && userBackupManagerService.isAppEligibleForBackup(packageName); } /** * Returns from the inputted packages {@code packages}, the ones that are eligible for backup. */ + @Nullable public String[] filterAppsEligibleForBackup(String[] packages) { - return mUserBackupManagerService.filterAppsEligibleForBackup(packages); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "filterAppsEligibleForBackup()"); + + return userBackupManagerService == null + ? null + : userBackupManagerService.filterAppsEligibleForBackup(packages); } /** @@ -378,7 +541,12 @@ public class BackupManagerService { */ public void backupNow(@UserIdInt int userId) { enforceCallingPermissionOnUserId(userId, "backupNow"); - mUserBackupManagerService.backupNow(); + UserBackupManagerService userBackupManagerService = + getServiceForUser(userId, "backupNow()"); + + if (userBackupManagerService != null) { + userBackupManagerService.backupNow(); + } } /** @@ -392,13 +560,23 @@ public class BackupManagerService { IBackupManagerMonitor monitor, int flags) { enforceCallingPermissionOnUserId(userId, "requestBackup"); - return mUserBackupManagerService.requestBackup(packages, observer, monitor, flags); + UserBackupManagerService userBackupManagerService = + getServiceForUser(userId, "requestBackup()"); + + return userBackupManagerService == null + ? BackupManager.ERROR_BACKUP_NOT_ALLOWED + : userBackupManagerService.requestBackup(packages, observer, monitor, flags); } /** Cancel all running backup operations. */ public void cancelBackups(@UserIdInt int userId) { enforceCallingPermissionOnUserId(userId, "cancelBackups"); - mUserBackupManagerService.cancelBackups(); + UserBackupManagerService userBackupManagerService = + getServiceForUser(userId, "cancelBackups()"); + + if (userBackupManagerService != null) { + userBackupManagerService.cancelBackups(); + } } /** @@ -410,7 +588,11 @@ public class BackupManagerService { * return value to the callback {@link JobService#onStartJob(JobParameters)}. */ public boolean beginFullBackup(FullBackupJob scheduledJob) { - return mUserBackupManagerService.beginFullBackup(scheduledJob); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "beginFullBackup()"); + + return userBackupManagerService != null + && userBackupManagerService.beginFullBackup(scheduledJob); } /** @@ -418,14 +600,24 @@ public class BackupManagerService { * longer met for running the full backup job. */ public void endFullBackup() { - mUserBackupManagerService.endFullBackup(); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "endFullBackup()"); + + if (userBackupManagerService != null) { + userBackupManagerService.endFullBackup(); + } } /** * Run a full backup pass for the given packages {@code packageNames}. Used by 'adb shell bmgr'. */ public void fullTransportBackup(String[] packageNames) { - mUserBackupManagerService.fullTransportBackup(packageNames); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "fullTransportBackup()"); + + if (userBackupManagerService != null) { + userBackupManagerService.fullTransportBackup(packageNames); + } } // --------------------------------------------- @@ -437,15 +629,26 @@ public class BackupManagerService { * called from the {@link PackageManager}. */ public void restoreAtInstall(String packageName, int token) { - mUserBackupManagerService.restoreAtInstall(packageName, token); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "restoreAtInstall()"); + + if (userBackupManagerService != null) { + userBackupManagerService.restoreAtInstall(packageName, token); + } } /** * Begin a restore for the specified package {@code packageName} using the specified transport * {@code transportName}. */ + @Nullable public IRestoreSession beginRestoreSession(String packageName, String transportName) { - return mUserBackupManagerService.beginRestoreSession(packageName, transportName); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "beginRestoreSession()"); + + return userBackupManagerService == null + ? null + : userBackupManagerService.beginRestoreSession(packageName, transportName); } /** @@ -453,7 +656,12 @@ public class BackupManagerService { * the active set if possible, else the ancestral one. Returns zero if none available. */ public long getAvailableRestoreToken(String packageName) { - return mUserBackupManagerService.getAvailableRestoreToken(packageName); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "getAvailableRestoreToken()"); + + return userBackupManagerService == null + ? 0 + : userBackupManagerService.getAvailableRestoreToken(packageName); } // --------------------------------------------- @@ -462,12 +670,19 @@ public class BackupManagerService { /** Sets the backup password used when running adb backup. */ public boolean setBackupPassword(String currentPassword, String newPassword) { - return mUserBackupManagerService.setBackupPassword(currentPassword, newPassword); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "setBackupPassword()"); + + return userBackupManagerService != null + && userBackupManagerService.setBackupPassword(currentPassword, newPassword); } /** Returns {@code true} if adb backup was run with a password, else returns {@code false}. */ public boolean hasBackupPassword() { - return mUserBackupManagerService.hasBackupPassword(); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "hasBackupPassword()"); + + return userBackupManagerService != null && userBackupManagerService.hasBackupPassword(); } /** @@ -489,18 +704,22 @@ public class BackupManagerService { boolean doKeyValue, String[] packageNames) { enforceCallingPermissionOnUserId(userId, "adbBackup"); - - mUserBackupManagerService.adbBackup( - fd, - includeApks, - includeObbs, - includeShared, - doWidgets, - doAllApps, - includeSystem, - doCompress, - doKeyValue, - packageNames); + UserBackupManagerService userBackupManagerService = + getServiceForUser(userId, "adbBackup()"); + + if (userBackupManagerService != null) { + userBackupManagerService.adbBackup( + fd, + includeApks, + includeObbs, + includeShared, + doWidgets, + doAllApps, + includeSystem, + doCompress, + doKeyValue, + packageNames); + } } /** @@ -510,8 +729,12 @@ public class BackupManagerService { */ public void adbRestore(@UserIdInt int userId, ParcelFileDescriptor fd) { enforceCallingPermissionOnUserId(userId, "setBackupEnabled"); + UserBackupManagerService userBackupManagerService = + getServiceForUser(userId, "adbRestore()"); - mUserBackupManagerService.adbRestore(fd); + if (userBackupManagerService != null) { + userBackupManagerService.adbRestore(fd); + } } /** @@ -524,8 +747,13 @@ public class BackupManagerService { String currentPassword, String encryptionPassword, IFullBackupRestoreObserver observer) { - mUserBackupManagerService.acknowledgeAdbBackupOrRestore( - token, allow, currentPassword, encryptionPassword, observer); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "acknowledgeAdbBackupOrRestore()"); + + if (userBackupManagerService != null) { + userBackupManagerService.acknowledgeAdbBackupOrRestore( + token, allow, currentPassword, encryptionPassword, observer); + } } // --------------------------------------------- @@ -534,7 +762,12 @@ public class BackupManagerService { /** Prints service state for 'dumpsys backup'. */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - mUserBackupManagerService.dump(fd, pw, args); + UserBackupManagerService userBackupManagerService = + getServiceForUser(UserHandle.USER_SYSTEM, "dump()"); + + if (userBackupManagerService != null) { + userBackupManagerService.dump(fd, pw, args); + } } private static boolean readBackupEnableState(int userId) { @@ -592,7 +825,7 @@ public class BackupManagerService { if (userId == UserHandle.USER_SYSTEM) { sInstance.initializeServiceAndUnlockSystemUser(); } else { - sInstance.startServiceForUser(userId); + sInstance.unlockUser(userId); } } } diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java index 4acd5c494ac7..59b72f9262b6 100644 --- a/services/backup/java/com/android/server/backup/Trampoline.java +++ b/services/backup/java/com/android/server/backup/Trampoline.java @@ -116,7 +116,7 @@ public class Trampoline extends IBackupManager.Stub { return SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false); } - protected boolean isMultiUserEnabled() { + private boolean isMultiUserEnabled() { return Settings.Global.getInt( mContext.getContentResolver(), Settings.Global.BACKUP_MULTI_USER_ENABLED, @@ -145,6 +145,10 @@ public class Trampoline extends IBackupManager.Stub { return new BackupManagerService(mContext, this, mHandlerThread); } + protected void postToHandler(Runnable runnable) { + mHandler.post(runnable); + } + /** * Initialize {@link BackupManagerService} if the backup service is not disabled. Only the * system user can initialize the service. @@ -174,14 +178,18 @@ public class Trampoline extends IBackupManager.Stub { * to initialize {@link BackupManagerService} and set backup state for the system user. */ void initializeServiceAndUnlockSystemUser() { - mHandler.post( + postToHandler( () -> { + // Initialize the backup service. Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init"); initializeService(UserHandle.USER_SYSTEM); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + // Start the service for the system user. BackupManagerService service = mService; if (service != null) { + Slog.i(TAG, "Starting service for system user"); + service.startServiceForUser(UserHandle.USER_SYSTEM); Slog.i(TAG, "Unlocking system user"); service.unlockSystemUser(); } @@ -195,20 +203,21 @@ public class Trampoline extends IBackupManager.Stub { */ // TODO(b/120212806): Consolidate service start for system and non-system users when system // user-only logic is removed. - void startServiceForUser(int userId) { + void unlockUser(int userId) { if (!isMultiUserEnabled()) { Slog.i(TAG, "Multi-user disabled, cannot start service for user: " + userId); return; } - mHandler.post( - () -> { - BackupManagerService service = mService; - if (service != null) { - Slog.i(TAG, "Starting service for user: " + userId); - service.startServiceForUser(userId); - } - }); + postToHandler(() -> startServiceForUser(userId)); + } + + private void startServiceForUser(int userId) { + BackupManagerService service = mService; + if (service != null) { + Slog.i(TAG, "Starting service for user: " + userId); + service.startServiceForUser(userId); + } } /** @@ -242,6 +251,7 @@ public class Trampoline extends IBackupManager.Stub { if (makeActive) { mService = createBackupManagerService(); mSuppressFile.delete(); + startServiceForUser(userId); } else { mService = null; try { diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 10e713d9dabe..e8820ae4e32a 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -25,6 +25,7 @@ import android.annotation.UserIdInt; import android.app.ActivityManagerInternal; import android.content.ComponentName; import android.content.Context; +import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; @@ -34,7 +35,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.util.Slog; import android.view.contentcapture.ContentCaptureContext; -import android.view.contentcapture.ContentCaptureEvent; import android.view.contentcapture.IContentCaptureManager; import com.android.internal.annotations.GuardedBy; @@ -47,7 +47,6 @@ import com.android.server.infra.AbstractMasterSystemService; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.List; /** * A service used to observe the contents of the screen. @@ -182,30 +181,18 @@ public final class ContentCaptureManagerService extends synchronized (mLock) { final ContentCapturePerUserService service = getServiceForUserLocked(userId); service.startSessionLocked(activityToken, componentName, taskId, displayId, - sessionId, clientContext, flags, mAllowInstantService, result); + sessionId, Binder.getCallingUid(), clientContext, flags, + mAllowInstantService, result); } } @Override - public void sendEvents(@UserIdInt int userId, @NonNull String sessionId, - @NonNull List<ContentCaptureEvent> events) { + public void finishSession(@UserIdInt int userId, @NonNull String sessionId) { Preconditions.checkNotNull(sessionId); - Preconditions.checkNotNull(events); synchronized (mLock) { final ContentCapturePerUserService service = getServiceForUserLocked(userId); - service.sendEventsLocked(sessionId, events); - } - } - - @Override - public void finishSession(@UserIdInt int userId, @NonNull String sessionId, - @Nullable List<ContentCaptureEvent> events) { - Preconditions.checkNotNull(sessionId); - - synchronized (mLock) { - final ContentCapturePerUserService service = getServiceForUserLocked(userId); - service.finishSessionLocked(sessionId, events); + service.finishSessionLocked(sessionId); } } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java index 81309122d0ca..f21b0d810411 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -16,6 +16,8 @@ package com.android.server.contentcapture; +import static android.service.contentcapture.ContentCaptureService.setClientState; + import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE; @@ -38,7 +40,6 @@ import android.service.contentcapture.SnapshotData; import android.util.ArrayMap; import android.util.Slog; import android.view.contentcapture.ContentCaptureContext; -import android.view.contentcapture.ContentCaptureEvent; import android.view.contentcapture.ContentCaptureSession; import com.android.internal.annotations.GuardedBy; @@ -48,7 +49,6 @@ import com.android.server.infra.FrameworkResourcesServiceNameResolver; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.List; /** * Per-user instance of {@link ContentCaptureManagerService}. @@ -114,10 +114,10 @@ final class ContentCapturePerUserService @GuardedBy("mLock") public void startSessionLocked(@NonNull IBinder activityToken, @NonNull ComponentName componentName, int taskId, int displayId, - @NonNull String sessionId, @Nullable ContentCaptureContext clientContext, - int flags, boolean bindInstantServiceAllowed, @NonNull IResultReceiver resultReceiver) { + @NonNull String sessionId, int uid, @Nullable ContentCaptureContext clientContext, + int flags, boolean bindInstantServiceAllowed, @NonNull IResultReceiver clientReceiver) { if (!isEnabledLocked()) { - sendToClient(resultReceiver, ContentCaptureSession.STATE_DISABLED); + setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED, /* binder=*/ null); return; } final ComponentName serviceComponentName = getServiceComponentName(); @@ -131,35 +131,30 @@ final class ContentCapturePerUserService return; } - ContentCaptureServerSession session = mSessions.get(sessionId); - if (session != null) { - if (mMaster.debug) { - Slog.d(TAG, "startSession(): reusing session " + sessionId + " for " - + componentName); - } - // TODO(b/111276913): check if local ids match and decide what to do if they don't - // TODO(b/111276913): should we call session.notifySessionStartedLocked() again?? - // if not, move notifySessionStartedLocked() into session constructor - sendToClient(resultReceiver, ContentCaptureSession.STATE_ACTIVE); + final ContentCaptureServerSession existingSession = mSessions.get(sessionId); + if (existingSession != null) { + Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken + + ": ignoring because it already exists for " + existingSession.mActivityToken); + setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_DUPLICATED_ID, + /* binder=*/ null); return; } - session = new ContentCaptureServerSession(getContext(), mUserId, mLock, activityToken, - this, serviceComponentName, componentName, taskId, displayId, sessionId, - clientContext, flags, bindInstantServiceAllowed, mMaster.verbose); + final ContentCaptureServerSession newSession = new ContentCaptureServerSession(getContext(), + mUserId, mLock, activityToken, this, serviceComponentName, componentName, taskId, + displayId, sessionId, uid, clientContext, flags, bindInstantServiceAllowed, + mMaster.verbose); if (mMaster.verbose) { - Slog.v(TAG, "startSession(): new session for " + componentName + " and id " - + sessionId); + Slog.v(TAG, "startSession(): new session for " + + ComponentName.flattenToShortString(componentName) + " and id " + sessionId); } - mSessions.put(sessionId, session); - session.notifySessionStartedLocked(); - sendToClient(resultReceiver, ContentCaptureSession.STATE_ACTIVE); + mSessions.put(sessionId, newSession); + newSession.notifySessionStartedLocked(clientReceiver); } // TODO(b/111276913): log metrics @GuardedBy("mLock") - public void finishSessionLocked(@NonNull String sessionId, - @Nullable List<ContentCaptureEvent> events) { + public void finishSessionLocked(@NonNull String sessionId) { if (!isEnabledLocked()) { return; } @@ -171,41 +166,8 @@ final class ContentCapturePerUserService } return; } - if (events != null && !events.isEmpty()) { - // TODO(b/111276913): for now we're sending the events and the onDestroy() in 2 separate - // calls because it's not clear yet whether we'll change the manager to send events - // to the service directly (i.e., without passing through system server). Once we - // decide, we might need to split IContentCaptureManager.onSessionLifecycle() in 2 - // methods, one for start and another for finish (and passing the events to finish), - // otherwise the service might receive the 2 calls out of order. - session.sendEventsLocked(events); - } - if (mMaster.verbose) { - Slog.v(TAG, "finishSession(" + (events == null ? 0 : events.size()) + " events): " - + session); - } - session.removeSelfLocked(true); - } - - // TODO(b/111276913): need to figure out why some events are sent before session is started; - // probably because ContentCaptureManager is not buffering them until it gets the session back - @GuardedBy("mLock") - public void sendEventsLocked(@NonNull String sessionId, - @NonNull List<ContentCaptureEvent> events) { - if (!isEnabledLocked()) { - return; - } - final ContentCaptureServerSession session = mSessions.get(sessionId); - if (session == null) { - if (mMaster.verbose) { - Slog.v(TAG, "sendEvents(): no session for " + sessionId); - } - return; - } - if (mMaster.verbose) { - Slog.v(TAG, "sendEvents(): id=" + sessionId + ", events=" + events.size()); - } - session.sendEventsLocked(events); + if (mMaster.verbose) Slog.v(TAG, "finishSession(): id=" + sessionId); + session.removeSelfLocked(/* notifyRemoteService= */ true); } @GuardedBy("mLock") @@ -308,12 +270,4 @@ final class ContentCapturePerUserService } return null; } - - private static void sendToClient(@NonNull IResultReceiver resultReceiver, int value) { - try { - resultReceiver.send(value, null); - } catch (RemoteException e) { - Slog.w(TAG, "Error async reporting result to client: " + e); - } - } } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java index 181a2daa2467..ba98b95082f7 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java @@ -24,15 +24,14 @@ import android.service.contentcapture.ContentCaptureService; import android.service.contentcapture.SnapshotData; import android.util.Slog; import android.view.contentcapture.ContentCaptureContext; -import android.view.contentcapture.ContentCaptureEvent; import android.view.contentcapture.ContentCaptureSessionId; import com.android.internal.annotations.GuardedBy; +import com.android.internal.os.IResultReceiver; import com.android.internal.util.Preconditions; import com.android.server.contentcapture.RemoteContentCaptureService.ContentCaptureServiceCallbacks; import java.io.PrintWriter; -import java.util.List; final class ContentCaptureServerSession implements ContentCaptureServiceCallbacks { @@ -43,18 +42,28 @@ final class ContentCaptureServerSession implements ContentCaptureServiceCallback private final ContentCapturePerUserService mService; private final RemoteContentCaptureService mRemoteService; private final ContentCaptureContext mContentCaptureContext; + + /** + * Canonical session id. + */ private final String mId; + /** + * UID of the app whose contents is being captured. + */ + private final int mUid; + ContentCaptureServerSession(@NonNull Context context, int userId, @NonNull Object lock, @NonNull IBinder activityToken, @NonNull ContentCapturePerUserService service, @NonNull ComponentName serviceComponentName, @NonNull ComponentName appComponentName, - int taskId, int displayId, @NonNull String sessionId, + int taskId, int displayId, @NonNull String sessionId, int uid, @Nullable ContentCaptureContext clientContext, int flags, boolean bindInstantServiceAllowed, boolean verbose) { mLock = lock; mActivityToken = activityToken; mService = service; mId = Preconditions.checkNotNull(sessionId); + mUid = uid; mRemoteService = new RemoteContentCaptureService(context, ContentCaptureService.SERVICE_INTERFACE, serviceComponentName, userId, this, bindInstantServiceAllowed, verbose); @@ -73,15 +82,8 @@ final class ContentCaptureServerSession implements ContentCaptureServiceCallback * Notifies the {@link ContentCaptureService} that the service started. */ @GuardedBy("mLock") - public void notifySessionStartedLocked() { - mRemoteService.onSessionLifecycleRequest(mContentCaptureContext, mId); - } - - /** - * Notifies the {@link ContentCaptureService} of a batch of events. - */ - public void sendEventsLocked(@NonNull List<ContentCaptureEvent> events) { - mRemoteService.onContentCaptureEventsRequest(mId, events); + public void notifySessionStartedLocked(@NonNull IResultReceiver clientReceiver) { + mRemoteService.onSessionStarted(mContentCaptureContext, mId, mUid, clientReceiver); } /** @@ -118,11 +120,11 @@ final class ContentCaptureServerSession implements ContentCaptureServiceCallback @GuardedBy("mLock") public void destroyLocked(boolean notifyRemoteService) { if (mService.isVerbose()) { - Slog.v(TAG, "destroyLocked(notifyRemoteService=" + notifyRemoteService + ")"); + Slog.v(TAG, "destroy(notifyRemoteService=" + notifyRemoteService + ")"); } // TODO(b/111276913): must call client to set session as FINISHED_BY_SERVER if (notifyRemoteService) { - mRemoteService.onSessionLifecycleRequest(/* context= */ null, mId); + mRemoteService.onSessionFinished(mId); } } @@ -133,13 +135,14 @@ final class ContentCaptureServerSession implements ContentCaptureServiceCallback Slog.d(TAG, "onServiceDied() for " + mId); } synchronized (mLock) { - removeSelfLocked(/* notifyRemoteService= */ false); + removeSelfLocked(/* notifyRemoteService= */ true); } } @GuardedBy("mLock") public void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) { pw.print(prefix); pw.print("id: "); pw.print(mId); pw.println(); + pw.print(prefix); pw.print("uid: "); pw.print(mUid); pw.println(); pw.print(prefix); pw.print("context: "); mContentCaptureContext.dump(pw); pw.println(); pw.print(prefix); pw.print("activity token: "); pw.println(mActivityToken); pw.print(prefix); pw.print("has autofill callback: "); diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java index b4edf7e60bc4..b9b19435408f 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java @@ -20,17 +20,14 @@ import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.os.IBinder; -import android.service.contentcapture.ContentCaptureEventsRequest; import android.service.contentcapture.IContentCaptureService; import android.service.contentcapture.SnapshotData; import android.text.format.DateUtils; import android.view.contentcapture.ContentCaptureContext; -import android.view.contentcapture.ContentCaptureEvent; +import com.android.internal.os.IResultReceiver; import com.android.server.infra.AbstractMultiplePendingRequestsRemoteService; -import java.util.List; - final class RemoteContentCaptureService extends AbstractMultiplePendingRequestsRemoteService<RemoteContentCaptureService, IContentCaptureService> { @@ -67,21 +64,19 @@ final class RemoteContentCaptureService /** * Called by {@link ContentCaptureServerSession} to generate a call to the - * {@link RemoteContentCaptureService} to indicate the session was created (when {@code context} - * is not {@code null} or destroyed (when {@code context} is {@code null}). + * {@link RemoteContentCaptureService} to indicate the session was created. */ - public void onSessionLifecycleRequest(@Nullable ContentCaptureContext context, - @NonNull String sessionId) { - scheduleAsyncRequest((s) -> s.onSessionLifecycle(context, sessionId)); + public void onSessionStarted(@Nullable ContentCaptureContext context, + @NonNull String sessionId, int uid, @NonNull IResultReceiver clientReceiver) { + scheduleAsyncRequest((s) -> s.onSessionStarted(context, sessionId, uid, clientReceiver)); } /** - * Called by {@link ContentCaptureServerSession} to send a batch of events to the service. + * Called by {@link ContentCaptureServerSession} to generate a call to the + * {@link RemoteContentCaptureService} to indicate the session was finished. */ - public void onContentCaptureEventsRequest(@NonNull String sessionId, - @NonNull List<ContentCaptureEvent> events) { - scheduleAsyncRequest((s) -> s.onContentCaptureEventsRequest(sessionId, - new ContentCaptureEventsRequest(events))); + public void onSessionFinished(@NonNull String sessionId) { + scheduleAsyncRequest((s) -> s.onSessionFinished(sessionId)); } /** diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 08034f734bea..0fa996ed7657 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -1123,8 +1123,6 @@ class AlarmManagerService extends SystemService { rescheduleKernelAlarmsLocked(); updateNextAlarmClockLocked(); - // And send a TIME_TICK right now, since it is important to get the UI updated. - mHandler.post(() -> getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL)); } static final class InFlight { @@ -1298,7 +1296,7 @@ class AlarmManagerService extends SystemService { mInjector.init(); synchronized (mLock) { - mHandler = new AlarmHandler(Looper.myLooper()); + mHandler = new AlarmHandler(); mConstants = new Constants(mHandler); mNextWakeup = mNextNonWakeup = 0; @@ -3050,6 +3048,9 @@ class AlarmManagerService extends SystemService { mNonInteractiveTime = dur; } } + // And send a TIME_TICK right now, since it is important to get the UI updated. + mHandler.post(() -> + getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL)); } else { mNonInteractiveStartTime = nowELAPSED; } @@ -3838,7 +3839,8 @@ class AlarmManagerService extends SystemService { mWakeLock.setWorkSource(null); } - private class AlarmHandler extends Handler { + @VisibleForTesting + class AlarmHandler extends Handler { public static final int ALARM_EVENT = 1; public static final int SEND_NEXT_ALARM_CLOCK_CHANGED = 2; public static final int LISTENER_TIMEOUT = 3; @@ -3847,8 +3849,8 @@ class AlarmManagerService extends SystemService { public static final int APP_STANDBY_PAROLE_CHANGED = 6; public static final int REMOVE_FOR_STOPPED = 7; - AlarmHandler(Looper looper) { - super(looper); + AlarmHandler() { + super(Looper.myLooper()); } public void postRemoveForStopped(int uid) { @@ -3961,8 +3963,8 @@ class AlarmManagerService extends SystemService { final WorkSource workSource = null; // Let system take blame for time tick events. setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0, - 0, null, mTimeTickTrigger, null, AlarmManager.FLAG_STANDALONE, workSource, - null, Process.myUid(), "android"); + 0, null, mTimeTickTrigger, "TIME_TICK", AlarmManager.FLAG_STANDALONE, + workSource, null, Process.myUid(), "android"); // Finally, remember when we set the tick alarm synchronized (mLock) { diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index 8d912fadf6d1..f0ec69f488b1 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -86,6 +86,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IAppOpsActiveCallback; import com.android.internal.app.IAppOpsCallback; +import com.android.internal.app.IAppOpsNotedCallback; import com.android.internal.app.IAppOpsService; import com.android.internal.os.Zygote; import com.android.internal.util.ArrayUtils; @@ -456,6 +457,7 @@ public class AppOpsService extends IAppOpsService.Stub { final ArrayMap<String, ArraySet<ModeCallback>> mPackageModeWatchers = new ArrayMap<>(); final ArrayMap<IBinder, ModeCallback> mModeWatchers = new ArrayMap<>(); final ArrayMap<IBinder, SparseArray<ActiveCallback>> mActiveWatchers = new ArrayMap<>(); + final ArrayMap<IBinder, SparseArray<NotedCallback>> mNotedWatchers = new ArrayMap<>(); final SparseArray<SparseArray<Restriction>> mAudioRestrictions = new SparseArray<>(); final class ModeCallback implements DeathRecipient { @@ -475,6 +477,7 @@ public class AppOpsService extends IAppOpsService.Stub { try { mCallback.asBinder().linkToDeath(this, 0); } catch (RemoteException e) { + /*ignored*/ } } @@ -524,6 +527,7 @@ public class AppOpsService extends IAppOpsService.Stub { try { mCallback.asBinder().linkToDeath(this, 0); } catch (RemoteException e) { + /*ignored*/ } } @@ -552,6 +556,50 @@ public class AppOpsService extends IAppOpsService.Stub { } } + final class NotedCallback implements DeathRecipient { + final IAppOpsNotedCallback mCallback; + final int mWatchingUid; + final int mCallingUid; + final int mCallingPid; + + NotedCallback(IAppOpsNotedCallback callback, int watchingUid, int callingUid, + int callingPid) { + mCallback = callback; + mWatchingUid = watchingUid; + mCallingUid = callingUid; + mCallingPid = callingPid; + try { + mCallback.asBinder().linkToDeath(this, 0); + } catch (RemoteException e) { + /*ignored*/ + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(128); + sb.append("NotedCallback{"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(" watchinguid="); + UserHandle.formatUid(sb, mWatchingUid); + sb.append(" from uid="); + UserHandle.formatUid(sb, mCallingUid); + sb.append(" pid="); + sb.append(mCallingPid); + sb.append('}'); + return sb.toString(); + } + + void destroy() { + mCallback.asBinder().unlinkToDeath(this, 0); + } + + @Override + public void binderDied() { + stopWatchingNoted(mCallback); + } + } + final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>(); final class ClientState extends Binder implements DeathRecipient { @@ -1629,7 +1677,7 @@ public class AppOpsService extends IAppOpsService.Stub { UidState uidState = getUidStateLocked(uid, false); if (uidState != null && uidState.opModes != null && uidState.opModes.indexOfKey(code) >= 0) { - return uidState.opModes.get(code); + return uidState.evalMode(uidState.opModes.get(code)); } Op op = getOpLocked(code, uid, packageName, false, true, false); if (op == null) { @@ -1795,12 +1843,16 @@ public class AppOpsService extends IAppOpsService.Stub { final Ops ops = getOpsRawLocked(uid, packageName, true /* edit */, false /* uidMismatchExpected */); if (ops == null) { + scheduleOpNotedIfNeededLocked(code, uid, packageName, + AppOpsManager.MODE_IGNORED); if (DEBUG) Slog.d(TAG, "noteOperation: no op for code " + code + " uid " + uid + " package " + packageName); return AppOpsManager.MODE_ERRORED; } final Op op = getOpLocked(ops, code, true); if (isOpRestrictedLocked(uid, code, packageName)) { + scheduleOpNotedIfNeededLocked(code, uid, packageName, + AppOpsManager.MODE_IGNORED); return AppOpsManager.MODE_IGNORED; } final UidState uidState = ops.uidState; @@ -1820,6 +1872,7 @@ public class AppOpsService extends IAppOpsService.Stub { + switchCode + " (" + code + ") uid " + uid + " package " + packageName); op.rejectTime[uidState.state] = System.currentTimeMillis(); + scheduleOpNotedIfNeededLocked(code, uid, packageName, uidMode); return uidMode; } } else { @@ -1830,6 +1883,7 @@ public class AppOpsService extends IAppOpsService.Stub { + switchCode + " (" + code + ") uid " + uid + " package " + packageName); op.rejectTime[uidState.state] = System.currentTimeMillis(); + scheduleOpNotedIfNeededLocked(op.op, uid, packageName, mode); return mode; } } @@ -1839,6 +1893,8 @@ public class AppOpsService extends IAppOpsService.Stub { op.rejectTime[uidState.state] = 0; op.proxyUid = proxyUid; op.proxyPackageName = proxyPackageName; + scheduleOpNotedIfNeededLocked(code, uid, packageName, + AppOpsManager.MODE_ALLOWED); return AppOpsManager.MODE_ALLOWED; } } @@ -1886,10 +1942,50 @@ public class AppOpsService extends IAppOpsService.Stub { } final int callbackCount = activeCallbacks.size(); for (int i = 0; i < callbackCount; i++) { - // Apps ops are mapped to a singleton - if (i == 0) { - activeCallbacks.valueAt(i).destroy(); - } + activeCallbacks.valueAt(i).destroy(); + } + } + } + + @Override + public void startWatchingNoted(@NonNull int[] ops, @NonNull IAppOpsNotedCallback callback) { + int watchedUid = Process.INVALID_UID; + final int callingUid = Binder.getCallingUid(); + final int callingPid = Binder.getCallingPid(); + if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS) + != PackageManager.PERMISSION_GRANTED) { + watchedUid = callingUid; + } + Preconditions.checkArgument(!ArrayUtils.isEmpty(ops), "Ops cannot be null or empty"); + Preconditions.checkArrayElementsInRange(ops, 0, AppOpsManager._NUM_OP - 1, + "Invalid op code in: " + Arrays.toString(ops)); + Preconditions.checkNotNull(callback, "Callback cannot be null"); + synchronized (this) { + SparseArray<NotedCallback> callbacks = mNotedWatchers.get(callback.asBinder()); + if (callbacks == null) { + callbacks = new SparseArray<>(); + mNotedWatchers.put(callback.asBinder(), callbacks); + } + final NotedCallback notedCallback = new NotedCallback(callback, watchedUid, + callingUid, callingPid); + for (int op : ops) { + callbacks.put(op, notedCallback); + } + } + } + + @Override + public void stopWatchingNoted(IAppOpsNotedCallback callback) { + Preconditions.checkNotNull(callback, "Callback cannot be null"); + synchronized (this) { + final SparseArray<NotedCallback> notedCallbacks = + mNotedWatchers.remove(callback.asBinder()); + if (notedCallbacks == null) { + return; + } + final int callbackCount = notedCallbacks.size(); + for (int i = 0; i < callbackCount; i++) { + notedCallbacks.valueAt(i).destroy(); } } } @@ -2052,6 +2148,51 @@ public class AppOpsService extends IAppOpsService.Stub { } } + private void scheduleOpNotedIfNeededLocked(int code, int uid, String packageName, + int result) { + ArraySet<NotedCallback> dispatchedCallbacks = null; + final int callbackListCount = mNotedWatchers.size(); + for (int i = 0; i < callbackListCount; i++) { + final SparseArray<NotedCallback> callbacks = mNotedWatchers.valueAt(i); + final NotedCallback callback = callbacks.get(code); + if (callback != null) { + if (callback.mWatchingUid >= 0 && callback.mWatchingUid != uid) { + continue; + } + if (dispatchedCallbacks == null) { + dispatchedCallbacks = new ArraySet<>(); + } + dispatchedCallbacks.add(callback); + } + } + if (dispatchedCallbacks == null) { + return; + } + mHandler.sendMessage(PooledLambda.obtainMessage( + AppOpsService::notifyOpChecked, + this, dispatchedCallbacks, code, uid, packageName, result)); + } + + private void notifyOpChecked(ArraySet<NotedCallback> callbacks, + int code, int uid, String packageName, int result) { + // There are components watching for checks in our process. The callbacks in + // these components may require permissions our remote caller does not have. + final long identity = Binder.clearCallingIdentity(); + try { + final int callbackCount = callbacks.size(); + for (int i = 0; i < callbackCount; i++) { + final NotedCallback callback = callbacks.valueAt(i); + try { + callback.mCallback.opNoted(code, uid, packageName, result); + } catch (RemoteException e) { + /* do nothing */ + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + @Override public int permissionToOpCode(String permission) { if (permission == null) { @@ -3463,6 +3604,46 @@ public class AppOpsService extends IAppOpsService.Stub { pw.println(cb); } } + if (mNotedWatchers.size() > 0 && dumpMode < 0) { + needSep = true; + boolean printedHeader = false; + for (int i = 0; i < mNotedWatchers.size(); i++) { + final SparseArray<NotedCallback> notedWatchers = mNotedWatchers.valueAt(i); + if (notedWatchers.size() <= 0) { + continue; + } + final NotedCallback cb = notedWatchers.valueAt(0); + if (dumpOp >= 0 && notedWatchers.indexOfKey(dumpOp) < 0) { + continue; + } + if (dumpPackage != null && cb.mWatchingUid >= 0 + && dumpUid != UserHandle.getAppId(cb.mWatchingUid)) { + continue; + } + if (!printedHeader) { + pw.println(" All op noted watchers:"); + printedHeader = true; + } + pw.print(" "); + pw.print(Integer.toHexString(System.identityHashCode( + mNotedWatchers.keyAt(i)))); + pw.println(" ->"); + pw.print(" ["); + final int opCount = notedWatchers.size(); + for (i = 0; i < opCount; i++) { + if (i > 0) { + pw.print(' '); + } + pw.print(AppOpsManager.opToName(notedWatchers.keyAt(i))); + if (i < opCount - 1) { + pw.print(','); + } + } + pw.println("]"); + pw.print(" "); + pw.println(cb); + } + } if (mClients.size() > 0 && dumpMode < 0) { needSep = true; boolean printedHeader = false; diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 89194e43bf95..66ceae432c6e 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -5714,7 +5714,6 @@ public class ConnectivityService extends IConnectivityManager.Stub // This should never fail. Specifying an already in use NetID will cause failure. if (networkAgent.isVPN()) { mNMS.createVirtualNetwork(networkAgent.network.netId, - !networkAgent.linkProperties.getDnsServers().isEmpty(), (networkAgent.networkMisc == null || !networkAgent.networkMisc.allowBypass)); } else { diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 4e8ef54a6465..d33b61757141 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -302,12 +302,14 @@ public class LocationManagerService extends ILocationManager.Stub { AppOpsManager.OnOpChangedListener callback = new AppOpsManager.OnOpChangedInternalListener() { public void onOpChanged(int op, String packageName) { - synchronized (mLock) { - for (Receiver receiver : mReceivers.values()) { - receiver.updateMonitoring(true); - } - applyAllProviderRequirementsLocked(); - } + mLocationHandler.post(() -> { + synchronized (mLock) { + for (Receiver receiver : mReceivers.values()) { + receiver.updateMonitoring(true); + } + applyAllProviderRequirementsLocked(); + } + }); } }; mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null, diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 8869af4e334a..b0ca2df20f1f 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -2314,11 +2314,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub } @Override - public void createVirtualNetwork(int netId, boolean hasDNS, boolean secure) { + public void createVirtualNetwork(int netId, boolean secure) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { - mNetdService.networkCreateVpn(netId, hasDNS, secure); + mNetdService.networkCreateVpn(netId, secure); } catch (RemoteException | ServiceSpecificException e) { throw new IllegalStateException(e); } diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 6c62725bb5bd..7adcabac26ee 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -1504,6 +1504,10 @@ class StorageManagerService extends IStorageManager.Stub public StorageManagerService(Context context) { sSelf = this; + // Snapshot feature flag used for this boot + SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT, Boolean.toString( + SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false))); + mContext = context; mCallbacks = new Callbacks(FgThread.get().getLooper()); mLockPatternUtils = new LockPatternUtils(mContext); diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 1ef3e942ca23..d07cf78e47ed 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -214,6 +214,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private PreciseCallState mPreciseCallState = new PreciseCallState(); + private int mCallDisconnectCause = DisconnectCause.NOT_VALID; + + private int mCallPreciseDisconnectCause = PreciseDisconnectCause.NOT_VALID; + private boolean mCarrierNetworkChangeState = false; private PhoneCapability mPhoneCapability = null; @@ -714,6 +718,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } + if ((events & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) { + try { + r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause, + mCallPreciseDisconnectCause); + } catch (RemoteException ex) { + remove(r.binder); + } + } if ((events & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) { try { r.callback.onPreciseDataConnectionStateChanged( @@ -1491,9 +1503,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } handleRemoveListLocked(); } - broadcastPreciseCallStateChanged(ringingCallState, foregroundCallState, backgroundCallState, - DisconnectCause.NOT_VALID, - PreciseDisconnectCause.NOT_VALID); + broadcastPreciseCallStateChanged(ringingCallState, foregroundCallState, + backgroundCallState); } public void notifyDisconnectCause(int disconnectCause, int preciseDisconnectCause) { @@ -1501,12 +1512,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { return; } synchronized (mRecords) { - mPreciseCallState = new PreciseCallState(mRingingCallState, mForegroundCallState, - mBackgroundCallState, disconnectCause, preciseDisconnectCause); + mCallDisconnectCause = disconnectCause; + mCallPreciseDisconnectCause = preciseDisconnectCause; for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_PRECISE_CALL_STATE)) { + if (r.matchPhoneStateListenerEvent(PhoneStateListener + .LISTEN_CALL_DISCONNECT_CAUSES)) { try { - r.callback.onPreciseCallStateChanged(mPreciseCallState); + r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause, + mCallPreciseDisconnectCause); } catch (RemoteException ex) { mRemoveList.add(r.binder); } @@ -1514,8 +1527,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } handleRemoveListLocked(); } - broadcastPreciseCallStateChanged(mRingingCallState, mForegroundCallState, - mBackgroundCallState, disconnectCause, preciseDisconnectCause); } public void notifyPreciseDataConnectionFailed(String reason, String apnType, @@ -1737,6 +1748,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } pw.println("mPreciseDataConnectionState=" + mPreciseDataConnectionState); pw.println("mPreciseCallState=" + mPreciseCallState); + pw.println("mCallDisconnectCause=" + mCallDisconnectCause); + pw.println("mCallPreciseDisconnectCause=" + mCallPreciseDisconnectCause); pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState); pw.println("mRingingCallState=" + mRingingCallState); pw.println("mForegroundCallState=" + mForegroundCallState); @@ -1912,13 +1925,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } private void broadcastPreciseCallStateChanged(int ringingCallState, int foregroundCallState, - int backgroundCallState, int disconnectCause, int preciseDisconnectCause) { + int backgroundCallState) { Intent intent = new Intent(TelephonyManager.ACTION_PRECISE_CALL_STATE_CHANGED); intent.putExtra(TelephonyManager.EXTRA_RINGING_CALL_STATE, ringingCallState); intent.putExtra(TelephonyManager.EXTRA_FOREGROUND_CALL_STATE, foregroundCallState); intent.putExtra(TelephonyManager.EXTRA_BACKGROUND_CALL_STATE, backgroundCallState); - intent.putExtra(TelephonyManager.EXTRA_DISCONNECT_CAUSE, disconnectCause); - intent.putExtra(TelephonyManager.EXTRA_PRECISE_DISCONNECT_CAUSE, preciseDisconnectCause); mContext.sendBroadcastAsUser(intent, UserHandle.ALL, android.Manifest.permission.READ_PRECISE_PHONE_STATE); } @@ -2006,6 +2017,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { + "LISTEN_PREFERRED_DATA_SUBID_CHANGE"); } + if ((events & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.READ_PRECISE_PHONE_STATE, null); + } + return true; } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 5afb90d681af..fe632e52cae6 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1665,7 +1665,7 @@ public final class ActiveServices { AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp); ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags, clientLabel, clientIntent, - callerApp.uid, callerApp.processName); + callerApp.uid, callerApp.processName, callingPackage); IBinder binder = connection.asBinder(); ArrayList<ConnectionRecord> clist = s.connections.get(binder); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 739dbbc65f6f..d11439755693 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2329,9 +2329,16 @@ public class ActivityManagerService extends IActivityManager.Stub fos.close(); long[] rssAfter = Process.getRss(pid); long end = SystemClock.uptimeMillis(); + long time = end - start; EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name, action, rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3], - rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], end-start); + rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time, + lastCompactAction, lastCompactTime, msg.arg1, msg.arg2); + StatsLog.write(StatsLog.APP_COMPACTED, pid, name, pendingAction, + rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3], + rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time, + lastCompactAction, lastCompactTime, msg.arg1, + ActivityManager.processStateAmToProto(msg.arg2)); synchronized(ActivityManagerService.this) { proc.lastCompactTime = end; proc.lastCompactAction = pendingAction; @@ -6284,7 +6291,7 @@ public class ActivityManagerService extends IActivityManager.Stub ContentProviderConnection incProviderCountLocked(ProcessRecord r, final ContentProviderRecord cpr, IBinder externalProcessToken, int callingUid, - String callingTag, boolean stable) { + String callingPackage, String callingTag, boolean stable) { if (r != null) { for (int i=0; i<r.conProviders.size(); i++) { ContentProviderConnection conn = r.conProviders.get(i); @@ -6304,7 +6311,7 @@ public class ActivityManagerService extends IActivityManager.Stub return conn; } } - ContentProviderConnection conn = new ContentProviderConnection(cpr, r); + ContentProviderConnection conn = new ContentProviderConnection(cpr, r, callingPackage); conn.startAssociationIfNeeded(); if (stable) { conn.stableCount = 1; @@ -6411,8 +6418,8 @@ public class ActivityManagerService extends IActivityManager.Stub } private ContentProviderHolder getContentProviderImpl(IApplicationThread caller, - String name, IBinder token, int callingUid, String callingTag, boolean stable, - int userId) { + String name, IBinder token, int callingUid, String callingPackage, String callingTag, + boolean stable, int userId) { ContentProviderRecord cpr; ContentProviderConnection conn = null; ProviderInfo cpi = null; @@ -6535,7 +6542,8 @@ public class ActivityManagerService extends IActivityManager.Stub // In this case the provider instance already exists, so we can // return it right away. - conn = incProviderCountLocked(r, cpr, token, callingUid, callingTag, stable); + conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag, + stable); if (conn != null && (conn.stableCount+conn.unstableCount) == 1) { if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) { // If this is a perceptible app accessing the provider, @@ -6782,7 +6790,8 @@ public class ActivityManagerService extends IActivityManager.Stub } mProviderMap.putProviderByName(name, cpr); - conn = incProviderCountLocked(r, cpr, token, callingUid, callingTag, stable); + conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag, + stable); if (conn != null) { conn.waiting = true; } @@ -6924,7 +6933,8 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public final ContentProviderHolder getContentProvider( - IApplicationThread caller, String name, int userId, boolean stable) { + IApplicationThread caller, String callingPackage, String name, int userId, + boolean stable) { enforceNotIsolatedCaller("getContentProvider"); if (caller == null) { String msg = "null IApplicationThread when getting content provider " @@ -6934,8 +6944,14 @@ public class ActivityManagerService extends IActivityManager.Stub } // The incoming user check is now handled in checkContentProviderPermissionLocked() to deal // with cross-user grant. - return getContentProviderImpl(caller, name, null, Binder.getCallingUid(), null, stable, - userId); + final int callingUid = Binder.getCallingUid(); + if (callingPackage != null && mAppOpsService.checkPackage(callingUid, callingPackage) + != AppOpsManager.MODE_ALLOWED) { + throw new SecurityException("Given calling package " + callingPackage + + " does not match caller's uid " + callingUid); + } + return getContentProviderImpl(caller, name, null, callingUid, callingPackage, + null, stable, userId); } public ContentProviderHolder getContentProviderExternal( @@ -6950,7 +6966,8 @@ public class ActivityManagerService extends IActivityManager.Stub private ContentProviderHolder getContentProviderExternalUnchecked(String name, IBinder token, int callingUid, String callingTag, int userId) { - return getContentProviderImpl(null, name, token, callingUid, callingTag, true, userId); + return getContentProviderImpl(null, name, token, callingUid, null, callingTag, + true, userId); } /** @@ -17028,12 +17045,16 @@ public class ActivityManagerService extends IActivityManager.Stub app.curAdj == ProcessList.HOME_APP_ADJ)) { app.reqCompactAction = COMPACT_PROCESS_SOME; mPendingCompactionProcesses.add(app); - mCompactionHandler.sendEmptyMessage(COMPACT_PROCESS_MSG); + mCompactionHandler.sendMessage( + mCompactionHandler.obtainMessage( + COMPACT_PROCESS_MSG, app.curAdj, app.setProcState)); } else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ && app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ) { app.reqCompactAction = COMPACT_PROCESS_FULL; mPendingCompactionProcesses.add(app); - mCompactionHandler.sendEmptyMessage(COMPACT_PROCESS_MSG); + mCompactionHandler.sendMessage( + mCompactionHandler.obtainMessage( + COMPACT_PROCESS_MSG, app.curAdj, app.setProcState)); } } ProcessList.setOomAdj(app.pid, app.uid, app.curAdj); diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java index aa76b3d6b856..af1031e5b887 100644 --- a/services/core/java/com/android/server/am/ConnectionRecord.java +++ b/services/core/java/com/android/server/am/ConnectionRecord.java @@ -43,6 +43,7 @@ final class ConnectionRecord { final PendingIntent clientIntent; // How to launch the client. final int clientUid; // The identity of this connection's client final String clientProcessName; // The source process of this connection's client + final String clientPackageName; // The source package of this connection's client public AssociationState.SourceState association; // Association tracking String stringName; // Caching of toString. boolean serviceDead; // Well is it? @@ -96,7 +97,7 @@ final class ConnectionRecord { ActivityServiceConnectionsHolder<ConnectionRecord> _activity, IServiceConnection _conn, int _flags, int _clientLabel, PendingIntent _clientIntent, - int _clientUid, String _clientProcessName) { + int _clientUid, String _clientProcessName, String _clientPackageName) { binding = _binding; activity = _activity; conn = _conn; @@ -105,6 +106,7 @@ final class ConnectionRecord { clientIntent = _clientIntent; clientUid = _clientUid; clientProcessName = _clientProcessName; + clientPackageName = _clientPackageName; } public void startAssociationIfNeeded() { @@ -125,7 +127,7 @@ final class ConnectionRecord { } else { association = holder.pkg.getAssociationStateLocked(holder.state, binding.service.instanceName.getClassName()).startSource(clientUid, - clientProcessName); + clientProcessName, clientPackageName); } } diff --git a/services/core/java/com/android/server/am/ContentProviderConnection.java b/services/core/java/com/android/server/am/ContentProviderConnection.java index f2d4f739c8bf..5f184c2dabee 100644 --- a/services/core/java/com/android/server/am/ContentProviderConnection.java +++ b/services/core/java/com/android/server/am/ContentProviderConnection.java @@ -32,6 +32,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; public final class ContentProviderConnection extends Binder { public final ContentProviderRecord provider; public final ProcessRecord client; + public final String clientPackage; public AssociationState.SourceState association; public final long createTime; public int stableCount; @@ -46,9 +47,11 @@ public final class ContentProviderConnection extends Binder { public int numStableIncs; public int numUnstableIncs; - public ContentProviderConnection(ContentProviderRecord _provider, ProcessRecord _client) { + public ContentProviderConnection(ContentProviderRecord _provider, ProcessRecord _client, + String _clientPackage) { provider = _provider; client = _client; + clientPackage = _clientPackage; createTime = SystemClock.elapsedRealtime(); } @@ -69,7 +72,8 @@ public final class ContentProviderConnection extends Binder { + provider.name.toShortString() + ": proc=" + provider.proc); } else { association = holder.pkg.getAssociationStateLocked(holder.state, - provider.name.getClassName()).startSource(client.uid, client.processName); + provider.name.getClassName()).startSource(client.uid, client.processName, + clientPackage); } } diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java index 2fc4adc2dc6c..46dfc7c60fd9 100644 --- a/services/core/java/com/android/server/am/ContentProviderRecord.java +++ b/services/core/java/com/android/server/am/ContentProviderRecord.java @@ -305,7 +305,7 @@ final class ContentProviderRecord implements ComponentName.WithComponentName { } else { mAssociation = holder.pkg.getAssociationStateLocked(holder.state, provider.name.getClassName()).startSource(mOwningUid, - mOwningProcessName); + mOwningProcessName, null); } } diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags index fa7a4c532f42..a71f6af80bec 100644 --- a/services/core/java/com/android/server/am/EventLogTags.logtags +++ b/services/core/java/com/android/server/am/EventLogTags.logtags @@ -138,4 +138,4 @@ option java_package com.android.server.am 30061 am_remove_task (Task ID|1|5), (Stack ID|1|5) # The task is being compacted -30063 am_compact (Pid|1|5),(Process Name|3),(Action|3),(BeforeRssTotal|2|2),(BeforeRssFile|2|2),(BeforeRssAnon|2|2),(BeforeRssSwap|2|2),(AfterRssTotal|2|2),(AfterRssFile|2|2),(AfterRssAnon|2|2),(AfterRssSwap|2|2),(Time|2|3)
\ No newline at end of file +30063 am_compact (Pid|1|5),(Process Name|3),(Action|3),(BeforeRssTotal|2|2),(BeforeRssFile|2|2),(BeforeRssAnon|2|2),(BeforeRssSwap|2|2),(AfterRssTotal|2|2),(AfterRssFile|2|2),(AfterRssAnon|2|2),(AfterRssSwap|2|2),(Time|2|3),(LastAction|1|2),(LastActionTimestamp|2|3),(setAdj|1|2),(procState|1|2) diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 4c4a09060706..d5ede5b63edc 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -19,8 +19,10 @@ package com.android.server.am; import android.content.ContentResolver; import android.database.ContentObserver; import android.net.Uri; +import android.os.AsyncTask; import android.os.Build; import android.os.SystemProperties; +import android.provider.DeviceConfig; import android.provider.Settings; import android.text.TextUtils; import android.util.Slog; @@ -61,13 +63,18 @@ class SettingsToPropertiesMapper { // Add the global setting you want to push to native level as experiment flag into this list. // // NOTE: please grant write permission system property prefix - // with format persist.experiment.[experiment_category_name]. in system_server.te and grant read - // permission in the corresponding .te file your feature belongs to. + // with format persist.device_config.global_settings.[flag_name] in system_server.te and grant + // read permission in the corresponding .te file your feature belongs to. @VisibleForTesting static final String[] sGlobalSettings = new String[] { Settings.Global.NATIVE_FLAGS_HEALTH_CHECK_ENABLED, }; + // All the flags under the listed DeviceConfig scopes will be synced to native level. + // + // NOTE: please grant write permission system property prefix + // with format persist.device_config.[device_config_scope]. in system_server.te and grant read + // permission in the corresponding .te file your feature belongs to. @VisibleForTesting static final String[] sDeviceConfigScopes = new String[] { }; @@ -104,19 +111,31 @@ class SettingsToPropertiesMapper { ContentObserver co = new ContentObserver(null) { @Override public void onChange(boolean selfChange) { - updatePropertyFromSetting(globalSetting, propName, true); + updatePropertyFromSetting(globalSetting, propName); } }; // only updating on starting up when no native flags reset is performed during current // booting. if (!isNativeFlagsResetPerformed()) { - updatePropertyFromSetting(globalSetting, propName, true); + updatePropertyFromSetting(globalSetting, propName); } mContentResolver.registerContentObserver(settingUri, false, co); } - // TODO: address sDeviceConfigScopes after DeviceConfig APIs are available. + for (String deviceConfigScope : mDeviceConfigScopes) { + DeviceConfig.addOnPropertyChangedListener( + deviceConfigScope, + AsyncTask.THREAD_POOL_EXECUTOR, + (String scope, String name, String value) -> { + String propertyName = makePropertyName(scope, name); + if (propertyName == null) { + log("unable to construct system property for " + scope + "/" + name); + return; + } + setProperty(propertyName, value); + }); + } } public static SettingsToPropertiesMapper start(ContentResolver contentResolver) { @@ -184,15 +203,6 @@ class SettingsToPropertiesMapper { return propertyName; } - private String getSetting(String name, boolean isGlobalSetting) { - if (isGlobalSetting) { - return Settings.Global.getString(mContentResolver, name); - } else { - // TODO: complete the code after DeviceConfig APIs implemented. - return null; - } - } - private void setProperty(String key, String value) { // Check if need to clear the property if (value == null) { @@ -259,8 +269,8 @@ class SettingsToPropertiesMapper { } @VisibleForTesting - void updatePropertyFromSetting(String settingName, String propName, boolean isGlobalSetting) { - String settingValue = getSetting(settingName, isGlobalSetting); + void updatePropertyFromSetting(String settingName, String propName) { + String settingValue = Settings.Global.getString(mContentResolver, settingName); setProperty(propName, settingValue); } } diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java index 422f5566eee1..e40949b785d9 100644 --- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java +++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java @@ -217,6 +217,19 @@ public class NetdEventListenerService extends INetdEventListener.Stub { @Override // Called concurrently by multiple binder threads. // This method must not block or perform long-running operations. + public synchronized void onNat64PrefixEvent(int netId, + boolean added, String prefixString, int prefixLength) + throws RemoteException { + for (INetdEventCallback callback : mNetdEventCallbackList) { + if (callback != null) { + callback.onNat64PrefixEvent(netId, added, prefixString, prefixLength); + } + } + } + + @Override + // Called concurrently by multiple binder threads. + // This method must not block or perform long-running operations. public synchronized void onPrivateDnsValidationEvent(int netId, String ipAddress, String hostname, boolean validated) throws RemoteException { diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index e7c3c7bbe21b..d96b6cba119b 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -1951,11 +1951,6 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. - private int getPointerDisplayId() { - return mWindowManagerCallbacks.getPointerDisplayId(); - } - - // Native callback. private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier) { if (!mSystemReady) { return null; @@ -2022,8 +2017,6 @@ public class InputManagerService extends IInputManager.Stub KeyEvent event, int policyFlags); public int getPointerLayer(); - - public int getPointerDisplayId(); } /** diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 28a6ba4ceb1d..3552e6635738 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -675,13 +675,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private final int mHardKeyboardBehavior; /** - * Whether we temporarily allow IMEs implemented in instant apps to run for testing. - * - * <p>Note: This is quite dangerous. Don't forget to reset after you finish testing.</p> - */ - private boolean mBindInstantServiceAllowed = false; - - /** * Internal state snapshot when {@link #MSG_START_INPUT} message is about to be posted to the * internal message queue. Any subsequent state change inside {@link InputMethodManagerService} * will not affect those tasks that are already posted. @@ -1135,8 +1128,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final PackageManager pm = mContext.getPackageManager(); final List<ResolveInfo> services = pm.queryIntentServicesAsUser( new Intent(InputMethod.SERVICE_INTERFACE).setPackage(packageName), - getComponentMatchingFlags(PackageManager.MATCH_DISABLED_COMPONENTS), - getChangingUserId()); + PackageManager.MATCH_DISABLED_COMPONENTS, getChangingUserId()); // No need to lock this because we access it only on getRegisteredHandler(). if (!services.isEmpty()) { mImePackageAppeared = true; @@ -1684,9 +1676,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub Slog.e(TAG, "--- bind failed: service = " + service + ", conn = " + conn); return false; } - if (mBindInstantServiceAllowed) { - flags |= Context.BIND_ALLOW_INSTANT; - } return mContext.bindServiceAsUser(service, conn, flags, new UserHandle(mSettings.getCurrentUserId())); } @@ -1696,6 +1685,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return getInputMethodList(false /* isVrOnly */); } + @Override public List<InputMethodInfo> getVrInputMethodList() { return getInputMethodList(true /* isVrOnly */); } @@ -3631,16 +3621,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return false; } - @PackageManager.ResolveInfoFlags - private int getComponentMatchingFlags(@PackageManager.ResolveInfoFlags int baseFlags) { - synchronized (mMethodMap) { - if (mBindInstantServiceAllowed) { - baseFlags |= PackageManager.MATCH_INSTANT; - } - return baseFlags; - } - } - @GuardedBy("mMethodMap") void buildInputMethodListLocked(boolean resetDefaultEnabledIme) { if (DEBUG) { @@ -3664,8 +3644,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // services depending on the unlock state for the specified user. final List<ResolveInfo> services = pm.queryIntentServicesAsUser( new Intent(InputMethod.SERVICE_INTERFACE), - getComponentMatchingFlags(PackageManager.GET_META_DATA - | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS), + PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, mSettings.getCurrentUserId()); final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap = @@ -3707,8 +3686,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // conservative, but it seems we cannot use it for now (Issue 35176630). final List<ResolveInfo> allInputMethodServices = pm.queryIntentServicesAsUser( new Intent(InputMethod.SERVICE_INTERFACE), - getComponentMatchingFlags(PackageManager.MATCH_DISABLED_COMPONENTS), - mSettings.getCurrentUserId()); + PackageManager.MATCH_DISABLED_COMPONENTS, mSettings.getCurrentUserId()); final int N = allInputMethodServices.size(); for (int i = 0; i < N; ++i) { final ServiceInfo si = allInputMethodServices.get(i).serviceInfo; @@ -4606,8 +4584,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub synchronized (mMethodMap) { p.println("Current Input Method Manager state:"); int N = mMethodList.size(); - p.println(" Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount - + " mBindInstantServiceAllowed=" + mBindInstantServiceAllowed); + p.println(" Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount); for (int i=0; i<N; i++) { InputMethodInfo info = mMethodList.get(i); p.println(" InputMethod #" + i + ":"); @@ -4719,9 +4696,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if ("refresh_debug_properties".equals(cmd)) { return refreshDebugProperties(); } - if ("set-bind-instant-service-allowed".equals(cmd)) { - return setBindInstantServiceAllowed(); - } // For existing "adb shell ime <command>". if ("ime".equals(cmd)) { @@ -4752,12 +4726,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @BinderThread @ShellCommandResult - private int setBindInstantServiceAllowed() { - return mService.handleSetBindInstantServiceAllowed(this); - } - - @BinderThread - @ShellCommandResult private int refreshDebugProperties() { DebugFlags.FLAG_OPTIMIZE_START_INPUT.refresh(); return ShellCommandResult.SUCCESS; @@ -4774,9 +4742,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub pw.println(" Synonym of dumpsys."); pw.println(" ime <command> [options]"); pw.println(" Manipulate IMEs. Run \"ime help\" for details."); - pw.println(" set-bind-instant-service-allowed true|false "); - pw.println(" Set whether binding to services provided by instant apps is " - + "allowed."); } } @@ -4825,53 +4790,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // Shell command handlers: /** - * Handles {@code adb shell cmd input_method set-bind-instant-service-allowed}. - * - * @param shellCommand {@link ShellCommand} object that is handling this command. - * @return Exit code of the command. - */ - @BinderThread - @RequiresPermission(android.Manifest.permission.MANAGE_BIND_INSTANT_SERVICE) - @ShellCommandResult - private int handleSetBindInstantServiceAllowed(@NonNull ShellCommand shellCommand) { - final String allowedString = shellCommand.getNextArgRequired(); - if (allowedString == null) { - shellCommand.getErrPrintWriter().println("Error: no true/false specified"); - return ShellCommandResult.FAILURE; - } - final boolean allowed = Boolean.parseBoolean(allowedString); - synchronized (mMethodMap) { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.MANAGE_BIND_INSTANT_SERVICE) - != PackageManager.PERMISSION_GRANTED) { - shellCommand.getErrPrintWriter().print( - "Caller must have MANAGE_BIND_INSTANT_SERVICE permission"); - return ShellCommandResult.FAILURE; - } - - if (mBindInstantServiceAllowed == allowed) { - // Nothing to do. - return ShellCommandResult.SUCCESS; - } - mBindInstantServiceAllowed = allowed; - - // Rebuild everything. - final long ident = Binder.clearCallingIdentity(); - try { - // Reset the current IME - resetSelectedInputMethodAndSubtypeLocked(null); - // Also reset the settings of the current IME - mSettings.putSelectedInputMethod(null); - buildInputMethodListLocked(false /* resetDefaultEnabledIme */); - updateInputMethodsFromSettingsLocked(true /* enabledMayChange */); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - return ShellCommandResult.SUCCESS; - } - - /** * Handles {@code adb shell ime list}. * @param shellCommand {@link ShellCommand} object that is handling this command. * @return Exit code of the command. diff --git a/services/core/java/com/android/server/job/JobConcurrencyManager.java b/services/core/java/com/android/server/job/JobConcurrencyManager.java index ee1d8476c471..827c0f1df71a 100644 --- a/services/core/java/com/android/server/job/JobConcurrencyManager.java +++ b/services/core/java/com/android/server/job/JobConcurrencyManager.java @@ -44,17 +44,11 @@ class JobConcurrencyManager { * We manipulate this array until we arrive at what jobs should be running on * what JobServiceContext. */ - JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT]; + JobStatus[] mRecycledAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT]; - /** - * Indicates whether we need to act on this jobContext id - */ - boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT]; + boolean[] mRecycledSlotChanged = new boolean[MAX_JOB_CONTEXTS_COUNT]; - /** - * The uid whose jobs we would like to assign to a context. - */ - int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT]; + int[] mRecycledPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT]; JobConcurrencyManager(JobSchedulerService service) { mService = service; @@ -99,28 +93,30 @@ class JobConcurrencyManager { break; } - JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap; - boolean[] act = mTmpAssignAct; - int[] preferredUidForContext = mTmpAssignPreferredUidForContext; - int numActive = 0; - int numForeground = 0; + // To avoid GC churn, we recycle the arrays. + JobStatus[] contextIdToJobMap = mRecycledAssignContextIdToJobMap; + boolean[] slotChanged = mRecycledSlotChanged; + int[] preferredUidForContext = mRecycledPreferredUidForContext; + + int numTotalRunningJobs = 0; + int numForegroundJobs = 0; for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { final JobServiceContext js = mService.mActiveServices.get(i); final JobStatus status = js.getRunningJobLocked(); if ((contextIdToJobMap[i] = status) != null) { - numActive++; + numTotalRunningJobs++; if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) { - numForeground++; + numForegroundJobs++; } } - act[i] = false; + slotChanged[i] = false; preferredUidForContext[i] = js.getPreferredUid(); } if (DEBUG) { Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial")); } for (int i=0; i<pendingJobs.size(); i++) { - JobStatus nextPending = pendingJobs.get(i); + final JobStatus nextPending = pendingJobs.get(i); // If job is already running, go to next job. int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap); @@ -131,23 +127,32 @@ class JobConcurrencyManager { final int priority = mService.evaluateJobPriorityLocked(nextPending); nextPending.lastEvaluatedPriority = priority; - // Find a context for nextPending. The context should be available OR + // Find an available slot for nextPending. The context should be available OR // it should have lowest priority among all running jobs // (sharing the same Uid as nextPending) - int minPriority = Integer.MAX_VALUE; - int minPriorityContextId = -1; + int minPriorityForPreemption = Integer.MAX_VALUE; + int selectedContextId = -1; + boolean startingJob = false; for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) { JobStatus job = contextIdToJobMap[j]; int preferredUid = preferredUidForContext[j]; if (job == null) { - if ((numActive < mService.mMaxActiveJobs || - (priority >= JobInfo.PRIORITY_TOP_APP && - numForeground < mConstants.FG_JOB_COUNT)) && - (preferredUid == nextPending.getUid() || - preferredUid == JobServiceContext.NO_PREFERRED_UID)) { + final boolean totalCountOk = numTotalRunningJobs < mService.mMaxActiveJobs; + final boolean fgCountOk = (priority >= JobInfo.PRIORITY_TOP_APP) + && (numForegroundJobs < mConstants.FG_JOB_COUNT); + final boolean preferredUidOkay = (preferredUid == nextPending.getUid()) + || (preferredUid == JobServiceContext.NO_PREFERRED_UID); + + // TODO: The following check is slightly wrong. + // Depending on how the pending jobs are sorted, we sometimes cap the total + // job count at mMaxActiveJobs (when all jobs are FG jobs), or + // at [mMaxActiveJobs + FG_JOB_COUNT] (when there are mMaxActiveJobs BG jobs + // and then FG_JOB_COUNT FG jobs.) + if ((totalCountOk || fgCountOk) && preferredUidOkay) { // This slot is free, and we haven't yet hit the limit on // concurrent jobs... we can just throw the job in to here. - minPriorityContextId = j; + selectedContextId = j; + startingJob = true; break; } // No job on this context, but nextPending can't run here because @@ -158,30 +163,39 @@ class JobConcurrencyManager { if (job.getUid() != nextPending.getUid()) { continue; } - if (mService.evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) { + + final int jobPriority = mService.evaluateJobPriorityLocked(job); + if (jobPriority >= nextPending.lastEvaluatedPriority) { continue; } - if (minPriority > nextPending.lastEvaluatedPriority) { - minPriority = nextPending.lastEvaluatedPriority; - minPriorityContextId = j; + + // TODO lastEvaluatedPriority should be evaluateJobPriorityLocked. (double check it) + if (minPriorityForPreemption > nextPending.lastEvaluatedPriority) { + minPriorityForPreemption = nextPending.lastEvaluatedPriority; + selectedContextId = j; + // In this case, we're just going to preempt a low priority job, we're not + // actually starting a job, so don't set startingJob. } } - if (minPriorityContextId != -1) { - contextIdToJobMap[minPriorityContextId] = nextPending; - act[minPriorityContextId] = true; - numActive++; + if (selectedContextId != -1) { + contextIdToJobMap[selectedContextId] = nextPending; + slotChanged[selectedContextId] = true; + } + if (startingJob) { + // Increase the counters when we're going to start a job. + numTotalRunningJobs++; if (priority >= JobInfo.PRIORITY_TOP_APP) { - numForeground++; + numForegroundJobs++; } } } if (DEBUG) { Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final")); } - tracker.noteConcurrency(numActive, numForeground); + tracker.noteConcurrency(numTotalRunningJobs, numForegroundJobs); for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { boolean preservePreferredUid = false; - if (act[i]) { + if (slotChanged[i]) { JobStatus js = activeServices.get(i).getRunningJobLocked(); if (js != null) { if (DEBUG) { @@ -195,7 +209,7 @@ class JobConcurrencyManager { final JobStatus pendingJob = contextIdToJobMap[i]; if (DEBUG) { Slog.d(TAG, "About to run job on context " - + String.valueOf(i) + ", job: " + pendingJob); + + i + ", job: " + pendingJob); } for (int ic=0; ic<controllers.size(); ic++) { controllers.get(ic).prepareForExecutionLocked(pendingJob); diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index 10dc156fbc01..3f9d928e1986 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -1261,6 +1261,9 @@ public class JobSchedulerService extends com.android.server.SystemService @Override public void onDeviceIdleStateChanged(boolean deviceIdle) { synchronized (mLock) { + if (DEBUG) { + Slog.d(TAG, "Doze state changed: " + deviceIdle); + } if (deviceIdle) { // When becoming idle, make sure no jobs are actively running, // except those using the idle exemption flag. @@ -1829,6 +1832,9 @@ public class JobSchedulerService extends com.android.server.SystemService } } break; case MSG_CHECK_JOB: + if (DEBUG) { + Slog.d(TAG, "MSG_CHECK_JOB"); + } if (mReportedActive) { // if jobs are currently being run, queue all ready jobs for execution. queueReadyJobsForExecutionLocked(); @@ -1838,6 +1844,9 @@ public class JobSchedulerService extends com.android.server.SystemService } break; case MSG_CHECK_JOB_GREEDY: + if (DEBUG) { + Slog.d(TAG, "MSG_CHECK_JOB_GREEDY"); + } queueReadyJobsForExecutionLocked(); break; case MSG_STOP_JOB: diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java index 84bb13ec92d3..ee60daa20837 100644 --- a/services/core/java/com/android/server/notification/NotificationDelegate.java +++ b/services/core/java/com/android/server/notification/NotificationDelegate.java @@ -45,7 +45,12 @@ public interface NotificationDelegate { void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded); void onNotificationDirectReplied(String key); void onNotificationSettingsViewed(String key); - void onNotificationSmartRepliesAdded(String key, int replyCount); + + /** + * Notifies that smart replies and actions have been added to the UI. + */ + void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount, int smartActionCount, + boolean generatedByAssistant); /** * Notifies a smart reply is sent. diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index bad259bafeb8..405edd216f0f 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -762,7 +762,13 @@ public class NotificationManagerService extends SystemService { .setType(MetricsEvent.TYPE_ACTION) .setSubtype(actionIndex) .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, nv.rank) - .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count)); + .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count) + .addTaggedData(MetricsEvent.NOTIFICATION_ACTION_IS_SMART, + (Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION + == action.getSemanticAction()) ? 1 : 0) + .addTaggedData( + MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED, + generatedByAssistant ? 1 : 0)); EventLogTags.writeNotificationActionClicked(key, actionIndex, r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now), nv.rank, nv.count); @@ -837,15 +843,20 @@ public class NotificationManagerService extends SystemService { if (DBG) Slog.d(TAG, "Marking notification as visible " + nv.key); reportSeen(r); - // If the newly visible notification has smart replies + // If the newly visible notification has smart suggestions // then log that the user has seen them. - if (r.getNumSmartRepliesAdded() > 0 + if ((r.getNumSmartRepliesAdded() > 0 || r.getNumSmartActionsAdded() > 0) && !r.hasSeenSmartReplies()) { r.setSeenSmartReplies(true); LogMaker logMaker = r.getLogMaker() .setCategory(MetricsEvent.SMART_REPLY_VISIBLE) .addTaggedData(MetricsEvent.NOTIFICATION_SMART_REPLY_COUNT, - r.getNumSmartRepliesAdded()); + r.getNumSmartRepliesAdded()) + .addTaggedData(MetricsEvent.NOTIFICATION_SMART_ACTION_COUNT, + r.getNumSmartActionsAdded()) + .addTaggedData( + MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED, + r.getSuggestionsGeneratedByAssistant()); mMetricsLogger.write(logMaker); } } @@ -908,11 +919,14 @@ public class NotificationManagerService extends SystemService { } @Override - public void onNotificationSmartRepliesAdded(String key, int replyCount) { + public void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount, + int smartActionCount, boolean generatedByAssistant) { synchronized (mNotificationLock) { NotificationRecord r = mNotificationsByKey.get(key); if (r != null) { - r.setNumSmartRepliesAdded(replyCount); + r.setNumSmartRepliesAdded(smartReplyCount); + r.setNumSmartActionsAdded(smartActionCount); + r.setSuggestionsGeneratedByAssistant(generatedByAssistant); } } } @@ -920,6 +934,7 @@ public class NotificationManagerService extends SystemService { @Override public void onNotificationSmartReplySent(String key, int replyIndex, CharSequence reply, boolean generatedByAssistant) { + synchronized (mNotificationLock) { NotificationRecord r = mNotificationsByKey.get(key); if (r != null) { diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index 39451d40a97e..89ec38db99e5 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -178,6 +178,8 @@ public final class NotificationRecord { private boolean mTextChanged; private boolean mRecordedInterruption; private int mNumberOfSmartRepliesAdded; + private int mNumberOfSmartActionsAdded; + private boolean mSuggestionsGeneratedByAssistant; private boolean mHasSeenSmartReplies; /** * Whether this notification (and its channels) should be considered user locked. Used in @@ -1140,6 +1142,22 @@ public final class NotificationRecord { return mNumberOfSmartRepliesAdded; } + public void setNumSmartActionsAdded(int noActions) { + mNumberOfSmartActionsAdded = noActions; + } + + public int getNumSmartActionsAdded() { + return mNumberOfSmartActionsAdded; + } + + public void setSuggestionsGeneratedByAssistant(boolean generatedByAssistant) { + mSuggestionsGeneratedByAssistant = generatedByAssistant; + } + + public boolean getSuggestionsGeneratedByAssistant() { + return mSuggestionsGeneratedByAssistant; + } + public boolean hasSeenSmartReplies() { return mHasSeenSmartReplies; } diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java index 6d5982708f4c..16143d3ae9e0 100644 --- a/services/core/java/com/android/server/om/IdmapManager.java +++ b/services/core/java/com/android/server/om/IdmapManager.java @@ -60,7 +60,6 @@ class IdmapManager { boolean createIdmap(@NonNull final PackageInfo targetPackage, @NonNull final PackageInfo overlayPackage, int userId) { - // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible if (DEBUG) { Slog.d(TAG, "create idmap for " + targetPackage.packageName + " and " + overlayPackage.packageName); @@ -70,16 +69,19 @@ class IdmapManager { final String overlayPath = overlayPackage.applicationInfo.getBaseCodePath(); try { if (FEATURE_FLAG_IDMAP2) { - mIdmap2Service.createIdmap(targetPath, overlayPath, userId); + if (mIdmap2Service.verifyIdmap(overlayPath, userId)) { + return true; + } + return mIdmap2Service.createIdmap(targetPath, overlayPath, userId) != null; } else { mInstaller.idmap(targetPath, overlayPath, sharedGid); + return true; } } catch (Exception e) { Slog.w(TAG, "failed to generate idmap for " + targetPath + " and " + overlayPath + ": " + e.getMessage()); return false; } - return true; } boolean removeIdmap(@NonNull final OverlayInfo oi, final int userId) { @@ -88,15 +90,15 @@ class IdmapManager { } try { if (FEATURE_FLAG_IDMAP2) { - mIdmap2Service.removeIdmap(oi.baseCodePath, userId); + return mIdmap2Service.removeIdmap(oi.baseCodePath, userId); } else { mInstaller.removeIdmap(oi.baseCodePath); + return true; } } catch (Exception e) { Slog.w(TAG, "failed to remove idmap for " + oi.baseCodePath + ": " + e.getMessage()); return false; } - return true; } boolean idmapExists(@NonNull final OverlayInfo oi) { diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index d4719049e426..a7f114655dcb 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -223,8 +223,8 @@ public final class OverlayManagerService extends SystemService { public OverlayManagerService(@NonNull final Context context, @NonNull final Installer installer) { super(context); - mSettingsFile = - new AtomicFile(new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays"); + mSettingsFile = new AtomicFile( + new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays"); mPackageManager = new PackageManagerHelper(); mUserManager = UserManagerService.getInstance(); IdmapManager im = new IdmapManager(installer); @@ -717,9 +717,9 @@ public final class OverlayManagerService extends SystemService { final Map<String, List<String>> pendingChanges = new ArrayMap<>(targetPackageNames.size()); synchronized (mLock) { final List<String> frameworkOverlays = - mImpl.getEnabledOverlayPackageNames("android", userId); - final int N = targetPackageNames.size(); - for (int i = 0; i < N; i++) { + mImpl.getEnabledOverlayPackageNames("android", userId); + final int n = targetPackageNames.size(); + for (int i = 0; i < n; i++) { final String targetPackageName = targetPackageNames.get(i); List<String> list = new ArrayList<>(); if (!"android".equals(targetPackageName)) { @@ -730,8 +730,8 @@ public final class OverlayManagerService extends SystemService { } } - final int N = targetPackageNames.size(); - for (int i = 0; i < N; i++) { + final int n = targetPackageNames.size(); + for (int i = 0; i < n; i++) { final String targetPackageName = targetPackageNames.get(i); if (DEBUG) { Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=[" @@ -789,7 +789,7 @@ public final class OverlayManagerService extends SystemService { if (!mSettingsFile.getBaseFile().exists()) { return; } - try (final FileInputStream stream = mSettingsFile.openRead()) { + try (FileInputStream stream = mSettingsFile.openRead()) { mSettings.restore(stream); // We might have data for dying users if the device was @@ -915,8 +915,8 @@ public final class OverlayManagerService extends SystemService { if (!verbose) { int count = 0; - final int N = mCache.size(); - for (int i = 0; i < N; i++) { + final int n = mCache.size(); + for (int i = 0; i < n; i++) { final int userId = mCache.keyAt(i); count += mCache.get(userId).size(); } @@ -929,8 +929,8 @@ public final class OverlayManagerService extends SystemService { return; } - final int N = mCache.size(); - for (int i = 0; i < N; i++) { + final int n = mCache.size(); + for (int i = 0; i < n; i++) { final int userId = mCache.keyAt(i); pw.println(TAB1 + "User " + userId); final HashMap<String, PackageInfo> map = mCache.get(userId); diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java index 112059daf95e..b0d2704196a6 100644 --- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java @@ -55,8 +55,8 @@ import java.util.Set; */ final class OverlayManagerServiceImpl { // Flags to use in conjunction with updateState. - private static final int FLAG_TARGET_IS_UPGRADING = 1<<0; - private static final int FLAG_OVERLAY_IS_UPGRADING = 1<<1; + private static final int FLAG_TARGET_IS_UPGRADING = 1 << 0; + private static final int FLAG_OVERLAY_IS_UPGRADING = 1 << 1; private final PackageManagerHelper mPackageManager; private final IdmapManager mIdmapManager; @@ -89,8 +89,8 @@ final class OverlayManagerServiceImpl { // a change in priority is only relevant for static RROs: specifically, // a regular RRO should not have its state reset only because a change // in priority - if (theTruth.isStaticOverlayPackage() && - theTruth.overlayPriority != oldSettings.priority) { + if (theTruth.isStaticOverlayPackage() + && theTruth.overlayPriority != oldSettings.priority) { return true; } return false; @@ -294,8 +294,8 @@ final class OverlayManagerServiceImpl { final int userId, final int flags) { boolean modified = false; final List<OverlayInfo> ois = mSettings.getOverlaysForTarget(targetPackageName, userId); - final int N = ois.size(); - for (int i = 0; i < N; i++) { + final int n = ois.size(); + for (int i = 0; i < n; i++) { final OverlayInfo oi = ois.get(i); final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName, userId); @@ -610,8 +610,8 @@ final class OverlayManagerServiceImpl { final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName, userId); final List<String> paths = new ArrayList<>(overlays.size()); - final int N = overlays.size(); - for (int i = 0; i < N; i++) { + final int n = overlays.size(); + for (int i = 0; i < n; i++) { final OverlayInfo oi = overlays.get(i); if (oi.isEnabled()) { paths.add(oi.packageName); @@ -632,8 +632,8 @@ final class OverlayManagerServiceImpl { userId); // Static RROs targeting to "android", ie framework-res.apk, are handled by native layers. - if (targetPackage != null && overlayPackage != null && - !("android".equals(targetPackageName) + if (targetPackage != null && overlayPackage != null + && !("android".equals(targetPackageName) && overlayPackage.isStaticOverlayPackage())) { mIdmapManager.createIdmap(targetPackage, overlayPackage, userId); } @@ -663,7 +663,7 @@ final class OverlayManagerServiceImpl { private @OverlayInfo.State int calculateNewState(@Nullable final PackageInfo targetPackage, @Nullable final PackageInfo overlayPackage, final int userId, final int flags) - throws OverlayManagerSettings.BadKeyException { + throws OverlayManagerSettings.BadKeyException { if ((flags & FLAG_TARGET_IS_UPGRADING) != 0) { return STATE_TARGET_UPGRADING; diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java index 572d36897040..ee067460cc45 100644 --- a/services/core/java/com/android/server/om/OverlayManagerSettings.java +++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java @@ -290,21 +290,22 @@ final class OverlayManagerSettings { return; } - final int N = mItems.size(); - for (int i = 0; i < N; i++) { + final int n = mItems.size(); + for (int i = 0; i < n; i++) { final SettingsItem item = mItems.get(i); pw.println(item.mPackageName + ":" + item.getUserId() + " {"); pw.increaseIndent(); - pw.print("mPackageName.......: "); pw.println(item.mPackageName); - pw.print("mUserId............: "); pw.println(item.getUserId()); - pw.print("mTargetPackageName.: "); pw.println(item.getTargetPackageName()); - pw.print("mBaseCodePath......: "); pw.println(item.getBaseCodePath()); - pw.print("mState.............: "); pw.println(OverlayInfo.stateToString(item.getState())); - pw.print("mIsEnabled.........: "); pw.println(item.isEnabled()); - pw.print("mIsStatic..........: "); pw.println(item.isStatic()); - pw.print("mPriority..........: "); pw.println(item.mPriority); - pw.print("mCategory..........: "); pw.println(item.mCategory); + pw.println("mPackageName.......: " + item.mPackageName); + pw.println("mUserId............: " + item.getUserId()); + pw.println("mTargetPackageName.: " + item.getTargetPackageName()); + pw.println("mBaseCodePath......: " + item.getBaseCodePath()); + pw.println("mState.............: " + OverlayInfo.stateToString(item.getState())); + pw.println("mState.............: " + OverlayInfo.stateToString(item.getState())); + pw.println("mIsEnabled.........: " + item.isEnabled()); + pw.println("mIsStatic..........: " + item.isStatic()); + pw.println("mPriority..........: " + item.mPriority); + pw.println("mCategory..........: " + item.mCategory); pw.decreaseIndent(); pw.println("}"); @@ -400,8 +401,8 @@ final class OverlayManagerSettings { xml.startTag(null, TAG_OVERLAYS); XmlUtils.writeIntAttribute(xml, ATTR_VERSION, CURRENT_VERSION); - final int N = table.size(); - for (int i = 0; i < N; i++) { + final int n = table.size(); + for (int i = 0; i < n; i++) { final SettingsItem item = table.get(i); persistRow(xml, item); } @@ -542,8 +543,8 @@ final class OverlayManagerSettings { } private int select(@NonNull final String packageName, final int userId) { - final int N = mItems.size(); - for (int i = 0; i < N; i++) { + final int n = mItems.size(); + for (int i = 0; i < n; i++) { final SettingsItem item = mItems.get(i); if (item.mUserId == userId && item.mPackageName.equals(packageName)) { return i; diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java index d576d330c4a8..e2b1cba3819f 100644 --- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java +++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java @@ -121,8 +121,8 @@ final class OverlayManagerShellCommand extends ShellCommand { for (final String targetPackageName : allOverlays.keySet()) { out.println(targetPackageName); List<OverlayInfo> overlaysForTarget = allOverlays.get(targetPackageName); - final int N = overlaysForTarget.size(); - for (int i = 0; i < N; i++) { + final int n = overlaysForTarget.size(); + for (int i = 0; i < n; i++) { final OverlayInfo oi = overlaysForTarget.get(i); String status; switch (oi.state) { diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java index 519a20d51ddb..33a9650a9994 100644 --- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java +++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java @@ -16,9 +16,9 @@ package com.android.server.pm.dex; +import android.os.Build; import android.util.AtomicFile; import android.util.Slog; -import android.os.Build; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -26,26 +26,27 @@ import com.android.internal.util.FastPrintWriter; import com.android.server.pm.AbstractStatsBase; import com.android.server.pm.PackageManagerServiceUtils; +import dalvik.system.VMRuntime; + +import libcore.io.IoUtils; + import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.io.InputStreamReader; import java.io.IOException; +import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.StringWriter; import java.io.Writer; import java.util.Collections; -import java.util.Iterator; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.Map; import java.util.Objects; import java.util.Set; -import dalvik.system.VMRuntime; -import libcore.io.IoUtils; - /** * Stat file which store usage information about dex files. */ @@ -86,6 +87,13 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { private static final String UNSUPPORTED_CLASS_LOADER_CONTEXT = "=UnsupportedClassLoaderContext="; + /** + * Limit on how many secondary DEX paths we store for a single owner, to avoid one app causing + * unbounded memory consumption. + */ + @VisibleForTesting + /* package */ static final int MAX_SECONDARY_FILES_PER_OWNER = 100; + // Map which structures the information we have on a package. // Maps package name to package data (which stores info about UsedByOtherApps and // secondary dex files.). @@ -164,8 +172,12 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { DexUseInfo existingData = packageUseInfo.mDexUseInfoMap.get(dexPath); if (existingData == null) { // It's the first time we see this dex file. - packageUseInfo.mDexUseInfoMap.put(dexPath, newData); - return true; + if (packageUseInfo.mDexUseInfoMap.size() < MAX_SECONDARY_FILES_PER_OWNER) { + packageUseInfo.mDexUseInfoMap.put(dexPath, newData); + return true; + } else { + return updateLoadingPackages; + } } else { if (ownerUserId != existingData.mOwnerUserId) { // Oups, this should never happen, the DexManager who calls this should 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 51619cf940a8..164af38be5a6 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -143,6 +143,13 @@ public final class DefaultPermissionGrantPolicy { LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION); } + private static final Set<String> ALWAYS_LOCATION_PERMISSIONS = new ArraySet<>(); + static { + ALWAYS_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION); + ALWAYS_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION); + ALWAYS_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION); + } + private static final Set<String> ACTIVITY_RECOGNITION_PERMISSIONS = new ArraySet<>(); static { ACTIVITY_RECOGNITION_PERMISSIONS.add(Manifest.permission.ACTIVITY_RECOGNITION); @@ -690,7 +697,7 @@ public final class DefaultPermissionGrantPolicy { // Companion devices grantSystemFixedPermissionsToSystemPackage( CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, userId, - LOCATION_PERMISSIONS); + ALWAYS_LOCATION_PERMISSIONS); // Ringtone Picker grantSystemFixedPermissionsToSystemPackage( diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 6e4c00eed181..02d8c0bcb584 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -1254,12 +1254,13 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } @Override - public void onNotificationSmartRepliesAdded(String key, int replyCount) - throws RemoteException { + public void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount, + int smartActionCount, boolean generatedByAssistant) { enforceStatusBarService(); long identity = Binder.clearCallingIdentity(); try { - mNotificationDelegate.onNotificationSmartRepliesAdded(key, replyCount); + mNotificationDelegate.onNotificationSmartSuggestionsAdded(key, smartReplyCount, + smartActionCount, generatedByAssistant); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 64ff9cf21ba0..7c61e373c53b 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -60,6 +60,7 @@ import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.Binder; import android.os.Bundle; +import android.os.Debug; import android.os.Environment; import android.os.FileObserver; import android.os.FileUtils; @@ -85,6 +86,7 @@ import android.system.Os; import android.util.EventLog; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.util.Xml; import android.view.Display; import android.view.IWindowManager; @@ -120,12 +122,13 @@ import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.function.Consumer; +import java.util.function.Predicate; public class WallpaperManagerService extends IWallpaperManager.Stub implements IWallpaperManagerService { - static final String TAG = "WallpaperManagerService"; - static final boolean DEBUG = false; - static final boolean DEBUG_LIVE = DEBUG || true; + private static final String TAG = "WallpaperManagerService"; + private static final boolean DEBUG = false; + private static final boolean DEBUG_LIVE = true; public static class Lifecycle extends SystemService { private IWallpaperManagerService mService; @@ -163,14 +166,14 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } - final Object mLock = new Object(); + private final Object mLock = new Object(); /** * Minimum time between crashes of a wallpaper service for us to consider * restarting it vs. just reverting to the static wallpaper. */ - static final long MIN_WALLPAPER_CRASH_TIME = 10000; - static final int MAX_WALLPAPER_COMPONENT_LOG_LENGTH = 128; + private static final long MIN_WALLPAPER_CRASH_TIME = 10000; + private static final int MAX_WALLPAPER_COMPONENT_LOG_LENGTH = 128; static final String WALLPAPER = "wallpaper_orig"; static final String WALLPAPER_CROP = "wallpaper"; static final String WALLPAPER_LOCK_ORIG = "wallpaper_lock_orig"; @@ -178,7 +181,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub static final String WALLPAPER_INFO = "wallpaper_info.xml"; // All the various per-user state files we need to be aware of - static final String[] sPerUserFiles = new String[] { + private static final String[] sPerUserFiles = new String[] { WALLPAPER, WALLPAPER_CROP, WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP, WALLPAPER_INFO @@ -335,7 +338,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } - void notifyLockWallpaperChanged() { + private void notifyLockWallpaperChanged() { final IWallpaperManagerCallback cb = mKeyguardListener; if (cb != null) { try { @@ -487,7 +490,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub boolean success = false; // Only generate crop for default display. - final WallpaperData.DisplayData wpData = getDisplayDataOrCreate(wallpaper, DEFAULT_DISPLAY); + final DisplayData wpData = getDisplayDataOrCreate(DEFAULT_DISPLAY); Rect cropHint = new Rect(wallpaper.cropHint); if (DEBUG) { @@ -640,11 +643,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } - final Context mContext; - final IWindowManager mIWindowManager; - final IPackageManager mIPackageManager; - final MyPackageMonitor mMonitor; - final AppOpsManager mAppOpsManager; + private final Context mContext; + private final IWindowManager mIWindowManager; + private final IPackageManager mIPackageManager; + private final MyPackageMonitor mMonitor; + private final AppOpsManager mAppOpsManager; private final DisplayManager mDisplayManager; private final DisplayManager.DisplayListener mDisplayListener = @@ -654,11 +657,23 @@ public class WallpaperManagerService extends IWallpaperManager.Stub public void onDisplayAdded(int displayId) { synchronized (mLock) { if (mLastWallpaper != null) { - final WallpaperConnection.DisplayConnector connector = - mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId); - if (connector == null) return; - - connector.connectLocked(mLastWallpaper.connection, mLastWallpaper); + if (supportsMultiDisplay(mLastWallpaper.connection)) { + final WallpaperConnection.DisplayConnector connector = + mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId); + if (connector == null) return; + connector.connectLocked(mLastWallpaper.connection, mLastWallpaper); + return; + } + // System wallpaper does not support multiple displays, attach this display to + // the fallback wallpaper. + if (mFallbackWallpaper != null) { + final WallpaperConnection.DisplayConnector connector = mFallbackWallpaper + .connection.getDisplayConnectorOrCreate(displayId); + if (connector == null) return; + connector.connectLocked(mFallbackWallpaper.connection, mFallbackWallpaper); + } else { + Slog.w(TAG, "No wallpaper can be added to the new display"); + } } } } @@ -667,12 +682,19 @@ public class WallpaperManagerService extends IWallpaperManager.Stub public void onDisplayRemoved(int displayId) { synchronized (mLock) { if (mLastWallpaper != null) { - final WallpaperConnection.DisplayConnector connector = - mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId); + WallpaperData targetWallpaper = null; + if (mLastWallpaper.connection.containsDisplay(displayId)) { + targetWallpaper = mLastWallpaper; + } else if (mFallbackWallpaper.connection.containsDisplay(displayId)) { + targetWallpaper = mFallbackWallpaper; + } + if (targetWallpaper == null) return; + WallpaperConnection.DisplayConnector connector = + targetWallpaper.connection.getDisplayConnectorOrCreate(displayId); if (connector == null) return; connector.disconnectLocked(); - mLastWallpaper.connection.removeDisplayConnector(displayId); - mLastWallpaper.removeDisplayData(displayId); + targetWallpaper.connection.removeDisplayConnector(displayId); + removeDisplayData(displayId); } } } @@ -686,35 +708,40 @@ public class WallpaperManagerService extends IWallpaperManager.Stub * Map of color listeners per user id. * The key will be the id of a user or UserHandle.USER_ALL - for wildcard listeners. */ - final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> mColorsChangedListeners; - WallpaperData mLastWallpaper; - IWallpaperManagerCallback mKeyguardListener; - boolean mWaitingForUnlock; - boolean mShuttingDown; + private final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> + mColorsChangedListeners; + private WallpaperData mLastWallpaper; + private IWallpaperManagerCallback mKeyguardListener; + private boolean mWaitingForUnlock; + private boolean mShuttingDown; /** * ID of the current wallpaper, changed every time anything sets a wallpaper. * This is used for external detection of wallpaper update activity. */ - int mWallpaperId; + private int mWallpaperId; /** * Name of the component used to display bitmap wallpapers from either the gallery or * built-in wallpapers. */ - final ComponentName mImageWallpaper; + private final ComponentName mImageWallpaper; /** * Name of the default wallpaper component; might be different from mImageWallpaper */ - final ComponentName mDefaultWallpaperComponent; + private final ComponentName mDefaultWallpaperComponent; - final SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>(); - final SparseArray<WallpaperData> mLockWallpaperMap = new SparseArray<WallpaperData>(); + private final SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>(); + private final SparseArray<WallpaperData> mLockWallpaperMap = new SparseArray<WallpaperData>(); - final SparseArray<Boolean> mUserRestorecon = new SparseArray<Boolean>(); - int mCurrentUserId = UserHandle.USER_NULL; - boolean mInAmbientMode; + private SparseArray<DisplayData> mDisplayDatas = new SparseArray<>(); + + private WallpaperData mFallbackWallpaper; + + private final SparseBooleanArray mUserRestorecon = new SparseBooleanArray(); + private int mCurrentUserId = UserHandle.USER_NULL; + private boolean mInAmbientMode; static class WallpaperData { @@ -780,18 +807,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub private RemoteCallbackList<IWallpaperManagerCallback> callbacks = new RemoteCallbackList<IWallpaperManagerCallback>(); - private static final class DisplayData { - int mWidth = -1; - int mHeight = -1; - final Rect mPadding = new Rect(0, 0, 0, 0); - final int mDisplayId; - - DisplayData(int displayId) { - mDisplayId = displayId; - } - } - private SparseArray<DisplayData> mDisplayDatas = new SparseArray<>(); - /** * The crop hint supplied for displaying a subset of the source image */ @@ -812,24 +827,34 @@ public class WallpaperManagerService extends IWallpaperManager.Stub boolean sourceExists() { return wallpaperFile.exists(); } + } - void removeDisplayData(int displayId) { - mDisplayDatas.remove(displayId); + private static final class DisplayData { + int mWidth = -1; + int mHeight = -1; + final Rect mPadding = new Rect(0, 0, 0, 0); + final int mDisplayId; + + DisplayData(int displayId) { + mDisplayId = displayId; } } - private WallpaperData.DisplayData getDisplayDataOrCreate(WallpaperData data, int displayId) { - WallpaperData.DisplayData wpdData = data.mDisplayDatas.get(displayId); + private void removeDisplayData(int displayId) { + mDisplayDatas.remove(displayId); + } + + private DisplayData getDisplayDataOrCreate(int displayId) { + DisplayData wpdData = mDisplayDatas.get(displayId); if (wpdData == null) { - wpdData = new WallpaperData.DisplayData(displayId); + wpdData = new DisplayData(displayId); ensureSaneWallpaperDisplaySize(wpdData, displayId); - data.mDisplayDatas.append(displayId, wpdData); + mDisplayDatas.append(displayId, wpdData); } return wpdData; } - private void ensureSaneWallpaperDisplaySize(WallpaperData.DisplayData wpdData, - int displayId) { + private void ensureSaneWallpaperDisplaySize(DisplayData wpdData, int displayId) { // We always want to have some reasonable width hint. final int baseSize = getMaximumSizeDimension(displayId); if (wpdData.mWidth < baseSize) { @@ -842,12 +867,16 @@ public class WallpaperManagerService extends IWallpaperManager.Stub private int getMaximumSizeDimension(int displayId) { Display display = mDisplayManager.getDisplay(displayId); + if (display == null) { + Slog.w(TAG, "Invalid displayId=" + displayId + " " + Debug.getCallers(4)); + display = mDisplayManager.getDisplay(DEFAULT_DISPLAY); + } return display.getMaximumSizeDimension(); } - void forEachDisplayData(WallpaperData data, Consumer<WallpaperData.DisplayData> action) { - for (int i = data.mDisplayDatas.size() - 1; i >= 0; i--) { - final WallpaperData.DisplayData wpdData = data.mDisplayDatas.valueAt(i); + void forEachDisplayData(Consumer<DisplayData> action) { + for (int i = mDisplayDatas.size() - 1; i >= 0; i--) { + final DisplayData wpdData = mDisplayDatas.valueAt(i); action.accept(wpdData); } } @@ -859,6 +888,45 @@ public class WallpaperManagerService extends IWallpaperManager.Stub return mWallpaperId; } + private boolean supportsMultiDisplay(WallpaperConnection connection) { + if (connection != null) { + return connection.mInfo == null // This is image wallpaper + || connection.mInfo.supportsMultipleDisplays(); + } + return false; + } + + private void updateFallbackConnection() { + if (mLastWallpaper == null || mFallbackWallpaper == null) return; + final WallpaperConnection systemConnection = mLastWallpaper.connection; + final WallpaperConnection fallbackConnection = mFallbackWallpaper.connection; + if (fallbackConnection == null) { + Slog.w(TAG, "Fallback wallpaper connection has not been created yet!!"); + return; + } + if (supportsMultiDisplay(systemConnection) + && fallbackConnection.getConnectedEngineSize() != 0) { + fallbackConnection.forEachDisplayConnector( + WallpaperConnection.DisplayConnector::disconnectLocked); + fallbackConnection.mDisplayConnector.clear(); + } else { + // TODO(b/121181553) Handle wallpaper service disconnect case. + if (fallbackConnection.mService == null) { + Slog.w(TAG, "There is no fallback wallpaper service"); + return; + } + fallbackConnection.appendConnectorWithCondition(display -> + fallbackConnection.isUsableDisplay(display) + && display.getDisplayId() != DEFAULT_DISPLAY + && !fallbackConnection.containsDisplay(display.getDisplayId())); + fallbackConnection.forEachDisplayConnector(connector -> { + if (connector.mEngine == null) { + connector.connectLocked(fallbackConnection, mFallbackWallpaper); + } + }); + } + } + class WallpaperConnection extends IWallpaperConnection.Stub implements ServiceConnection { @@ -877,8 +945,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } void ensureStatusHandled() { - final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(mWallpaper, - mDisplayId); + final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId); if (mDimensionsChanged) { try { mEngine.setDesiredSize(wpdData.mWidth, wpdData.mHeight); @@ -906,8 +973,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub return; } - final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, - mDisplayId); + final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId); try { connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false, wpdData.mWidth, wpdData.mHeight, @@ -982,19 +1048,33 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } private void initDisplayState() { + // Do not initialize fallback wallpaper + if (!mWallpaper.equals(mFallbackWallpaper)) { + if (supportsMultiDisplay(this)) { + // The system wallpaper is image wallpaper or it can supports multiple displays. + appendConnectorWithCondition(this::isUsableDisplay); + } else { + // The system wallpaper does not support multiple displays, so just attach it on + // default display. + mDisplayConnector.append(DEFAULT_DISPLAY, + new DisplayConnector(DEFAULT_DISPLAY)); + } + } + } + + private void appendConnectorWithCondition(Predicate<Display> tester) { final Display[] displays = mDisplayManager.getDisplays(); for (Display display : displays) { - if (isUsableDisplay(display)) { + if (tester.test(display)) { final int displayId = display.getDisplayId(); - mDisplayConnector.append(displayId, new DisplayConnector(displayId)); + mDisplayConnector.append(displayId, + new DisplayConnector(displayId)); } } } - // TODO(b/115486823) Support the system decorations change at runtime. private boolean isUsableDisplay(Display display) { return display != null && display.hasAccess(mClientUid) - // TODO(b/114338689) Use WindowManager.supportsSystemDecorations when ready && (display.supportsSystemDecorations() || display.getDisplayId() == DEFAULT_DISPLAY); } @@ -1027,6 +1107,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub return connector; } + boolean containsDisplay(int displayId) { + return mDisplayConnector.get(displayId) != null; + } + void removeDisplayConnector(int displayId) { final DisplayConnector connector = mDisplayConnector.get(displayId); if (connector != null) { @@ -1044,7 +1128,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // when we have an engine, but I'm not sure about // locking there and anyway we always need to be able to // recover if there is something wrong. - saveSettingsLocked(mWallpaper.userId); + if (!mWallpaper.equals(mFallbackWallpaper)) { + saveSettingsLocked(mWallpaper.userId); + } FgThread.getHandler().removeCallbacks(mResetRunnable); } } @@ -1533,8 +1619,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // This corrects for mislabeling bugs that might have arisen from move-to // operations involving the wallpaper files. This isn't timing-critical, // so we do it in the background to avoid holding up the user unlock operation. - if (mUserRestorecon.get(userId) != Boolean.TRUE) { - mUserRestorecon.put(userId, Boolean.TRUE); + if (!mUserRestorecon.get(userId)) { + mUserRestorecon.put(userId, true); Runnable relabeler = new Runnable() { @Override public void run() { @@ -1562,7 +1648,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub for (String filename : sPerUserFiles) { new File(wallpaperDir, filename).delete(); } - mUserRestorecon.remove(userId); + mUserRestorecon.delete(userId); } } @@ -1789,7 +1875,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub throw new IllegalArgumentException("Cannot find display with id=" + displayId); } - final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, displayId); + final DisplayData wpdData = getDisplayDataOrCreate(displayId); if (width != wpdData.mWidth || height != wpdData.mHeight) { wpdData.mWidth = width; wpdData.mHeight = height; @@ -1826,8 +1912,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId()); if (wallpaper != null) { - final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, - displayId); + final DisplayData wpdData = getDisplayDataOrCreate(displayId); return wpdData.mWidth; } else { return 0; @@ -1845,8 +1930,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId()); if (wallpaper != null) { - final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, - displayId); + final DisplayData wpdData = getDisplayDataOrCreate(displayId); return wpdData.mHeight; } else { return 0; @@ -1872,7 +1956,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub throw new IllegalArgumentException("padding must be positive: " + padding); } - final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, displayId); + final DisplayData wpdData = getDisplayDataOrCreate(displayId); if (!padding.equals(wpdData.mPadding)) { wpdData.mPadding.set(padding); if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId); @@ -1940,8 +2024,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub return null; } // Only for default display. - final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, - DEFAULT_DISPLAY); + final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY); try { if (outParams != null) { outParams.putInt("width", wpdData.mWidth); @@ -2173,14 +2256,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // We know a-priori that there is no lock-only wallpaper currently WallpaperData lockWP = new WallpaperData(userId, WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP); - final WallpaperData.DisplayData lockWPDData = getDisplayDataOrCreate(lockWP, - DEFAULT_DISPLAY); - final WallpaperData.DisplayData sysWPDData = getDisplayDataOrCreate(sysWP, - DEFAULT_DISPLAY); lockWP.wallpaperId = sysWP.wallpaperId; lockWP.cropHint.set(sysWP.cropHint); - lockWPDData.mWidth = sysWPDData.mWidth; - lockWPDData.mHeight = sysWPDData.mHeight; lockWP.allowBackup = sysWP.allowBackup; lockWP.primaryColors = sysWP.primaryColors; @@ -2320,7 +2397,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub return false; } - boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force, + private boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force, boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) { if (DEBUG_LIVE) { Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName); @@ -2443,15 +2520,17 @@ public class WallpaperManagerService extends IWallpaperManager.Stub Slog.w(TAG, msg); return false; } - if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null) { + if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null + && !wallpaper.equals(mFallbackWallpaper)) { detachWallpaperLocked(mLastWallpaper); } wallpaper.wallpaperComponent = componentName; wallpaper.connection = newConn; newConn.mReply = reply; - if (wallpaper.userId == mCurrentUserId) { + if (wallpaper.userId == mCurrentUserId && !wallpaper.equals(mFallbackWallpaper)) { mLastWallpaper = wallpaper; } + updateFallbackConnection(); } catch (RemoteException e) { String msg = "Remote exception for " + componentName + "\n" + e; if (fromUser) { @@ -2463,7 +2542,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub return true; } - void detachWallpaperLocked(WallpaperData wallpaper) { + private void detachWallpaperLocked(WallpaperData wallpaper) { if (wallpaper.connection != null) { if (wallpaper.connection.mReply != null) { try { @@ -2473,7 +2552,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub wallpaper.connection.mReply = null; } mContext.unbindService(wallpaper.connection); - wallpaper.connection.forEachDisplayConnector(connector -> connector.disconnectLocked()); + wallpaper.connection.forEachDisplayConnector( + WallpaperConnection.DisplayConnector::disconnectLocked); wallpaper.connection.mService = null; wallpaper.connection.mDisplayConnector.clear(); wallpaper.connection = null; @@ -2481,12 +2561,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } - void clearWallpaperComponentLocked(WallpaperData wallpaper) { + private void clearWallpaperComponentLocked(WallpaperData wallpaper) { wallpaper.wallpaperComponent = null; detachWallpaperLocked(wallpaper); } - void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) { + private void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) { conn.forEachDisplayConnector(connector-> connector.connectLocked(conn, wallpaper)); } @@ -2596,8 +2676,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub if (DEBUG) { Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId); } - final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, - DEFAULT_DISPLAY); + final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY); out.startTag(null, tag); out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId)); out.attribute(null, "width", Integer.toString(wpdData.mWidth)); @@ -2755,10 +2834,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub Slog.i(TAG, "No static wallpaper imagery; defaults will be shown"); } } + initializeFallbackWallpaper(); } boolean success = false; - final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, - DEFAULT_DISPLAY); + final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY); try { stream = new FileInputStream(file); XmlPullParser parser = Xml.newPullParser(); @@ -2845,8 +2924,19 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } + private void initializeFallbackWallpaper() { + if (mFallbackWallpaper == null) { + if (DEBUG) Slog.d(TAG, "Initialize fallback wallpaper"); + mFallbackWallpaper = new WallpaperData( + UserHandle.USER_SYSTEM, WALLPAPER, WALLPAPER_CROP); + mFallbackWallpaper.allowBackup = false; + mFallbackWallpaper.wallpaperId = makeWallpaperIdLocked(); + bindWallpaperComponentLocked(mImageWallpaper, true, false, mFallbackWallpaper, null); + } + } + private void ensureSaneWallpaperData(WallpaperData wallpaper, int displayId) { - final WallpaperData.DisplayData size = getDisplayDataOrCreate(wallpaper, displayId); + final DisplayData size = getDisplayDataOrCreate(displayId); if (displayId == DEFAULT_DISPLAY) { // crop, if not previously specified @@ -2869,7 +2959,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub wallpaper.wallpaperId = makeWallpaperIdLocked(); } - final WallpaperData.DisplayData wpData = getDisplayDataOrCreate(wallpaper, DEFAULT_DISPLAY); + final DisplayData wpData = getDisplayDataOrCreate(DEFAULT_DISPLAY); if (!keepDimensionHints) { wpData.mWidth = Integer.parseInt(parser.getAttributeValue(null, "width")); @@ -2963,7 +3053,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } // Restore the named resource bitmap to both source + crop files - boolean restoreNamedResourceLocked(WallpaperData wallpaper) { + private boolean restoreNamedResourceLocked(WallpaperData wallpaper) { if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) { String resName = wallpaper.name.substring(4); @@ -3048,8 +3138,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub for (int i = 0; i < mWallpaperMap.size(); i++) { WallpaperData wallpaper = mWallpaperMap.valueAt(i); pw.print(" User "); pw.print(wallpaper.userId); - pw.print(": id="); pw.println(wallpaper.wallpaperId); - forEachDisplayData(wallpaper, wpSize -> { + pw.print(": id="); pw.println(wallpaper.wallpaperId); + pw.println(" Display state:"); + forEachDisplayData(wpSize -> { pw.print(" displayId="); pw.println(wpSize.mDisplayId); pw.print(" mWidth="); @@ -3072,11 +3163,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub pw.println(conn.mInfo.getComponent()); } conn.forEachDisplayConnector(connector -> { - pw.print(" mDisplayId="); + pw.print(" mDisplayId="); pw.println(connector.mDisplayId); - pw.print(" mToken="); + pw.print(" mToken="); pw.println(connector.mToken); - pw.print(" mEngine="); + pw.print(" mEngine="); pw.println(connector.mEngine); }); pw.print(" mService="); @@ -3090,18 +3181,38 @@ public class WallpaperManagerService extends IWallpaperManager.Stub WallpaperData wallpaper = mLockWallpaperMap.valueAt(i); pw.print(" User "); pw.print(wallpaper.userId); pw.print(": id="); pw.println(wallpaper.wallpaperId); - forEachDisplayData(wallpaper, wpSize -> { - pw.print(" displayId="); - pw.println(wpSize.mDisplayId); - pw.print(" mWidth="); pw.print(wpSize.mWidth); - pw.print(" mHeight="); pw.println(wpSize.mHeight); - pw.print(" mPadding="); pw.println(wpSize.mPadding); - }); pw.print(" mCropHint="); pw.println(wallpaper.cropHint); pw.print(" mName="); pw.println(wallpaper.name); pw.print(" mAllowBackup="); pw.println(wallpaper.allowBackup); } - + pw.println("Fallback wallpaper state:"); + pw.print(" User "); pw.print(mFallbackWallpaper.userId); + pw.print(": id="); pw.println(mFallbackWallpaper.wallpaperId); + pw.print(" mCropHint="); pw.println(mFallbackWallpaper.cropHint); + pw.print(" mName="); pw.println(mFallbackWallpaper.name); + pw.print(" mAllowBackup="); pw.println(mFallbackWallpaper.allowBackup); + if (mFallbackWallpaper.connection != null) { + WallpaperConnection conn = mFallbackWallpaper.connection; + pw.print(" Fallback Wallpaper connection "); + pw.print(conn); + pw.println(":"); + if (conn.mInfo != null) { + pw.print(" mInfo.component="); + pw.println(conn.mInfo.getComponent()); + } + conn.forEachDisplayConnector(connector -> { + pw.print(" mDisplayId="); + pw.println(connector.mDisplayId); + pw.print(" mToken="); + pw.println(connector.mToken); + pw.print(" mEngine="); + pw.println(connector.mEngine); + }); + pw.print(" mService="); + pw.println(conn.mService); + pw.print(" mLastDiedTime="); + pw.println(mFallbackWallpaper.lastDiedTime - SystemClock.uptimeMillis()); + } } } } diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 2663d997162c..6755c7363ede 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -4914,7 +4914,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // Update override configurations of all tasks in the stack. final Rect taskBounds = tempTaskBounds != null ? tempTaskBounds : bounds; - final Rect insetBounds = tempTaskInsetBounds != null ? tempTaskInsetBounds : taskBounds; mTmpBounds.clear(); mTmpInsetBounds.clear(); @@ -4922,7 +4921,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai for (int i = mTaskHistory.size() - 1; i >= 0; i--) { final TaskRecord task = mTaskHistory.get(i); if (task.isResizeable()) { - task.updateOverrideConfiguration(taskBounds, insetBounds); + task.updateOverrideConfiguration(taskBounds, tempTaskInsetBounds); } if (task.hasDisplayedBounds()) { diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 8b8cadc7a4a6..6d3c69385a09 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -505,23 +505,6 @@ public class DisplayPolicy { // TODO: Make it can take screenshot on external display mScreenshotHelper = displayContent.isDefaultDisplay ? new ScreenshotHelper(mContext) : null; - } - - void systemReady() { - mSystemGestures.systemReady(); - } - - private int getDisplayId() { - return mDisplayContent.getDisplayId(); - } - - void onDisplayRemoved() { - mDisplayContent.unregisterPointerEventListener(mSystemGestures); - } - - void configure(int width, int height, int shortSizeDp) { - // Allow the navigation bar to move on non-square small devices (phones). - mNavigationBarCanMove = width != height && shortSizeDp < 600; if (mDisplayContent.isDefaultDisplay) { mHasStatusBar = true; @@ -541,6 +524,23 @@ public class DisplayPolicy { } } + void systemReady() { + mSystemGestures.systemReady(); + } + + private int getDisplayId() { + return mDisplayContent.getDisplayId(); + } + + void onDisplayRemoved() { + mDisplayContent.unregisterPointerEventListener(mSystemGestures); + } + + void configure(int width, int height, int shortSizeDp) { + // Allow the navigation bar to move on non-square small devices (phones). + mNavigationBarCanMove = width != height && shortSizeDp < 600; + } + void updateConfigurationDependentBehaviors() { mNavBarOpacityMode = mContext.getResources().getInteger(R.integer.config_navBarOpacityMode); } diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java index f9c9d33c561a..639ed02a1e48 100644 --- a/services/core/java/com/android/server/wm/InputManagerCallback.java +++ b/services/core/java/com/android/server/wm/InputManagerCallback.java @@ -1,6 +1,5 @@ package com.android.server.wm; -import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; @@ -10,6 +9,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.os.Debug; import android.os.IBinder; import android.util.Slog; +import android.view.InputApplicationHandle; import android.view.KeyEvent; import android.view.WindowManager; @@ -204,37 +204,6 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal + WindowManagerService.TYPE_LAYER_OFFSET; } - /** Callback to get pointer display id. */ - @Override - public int getPointerDisplayId() { - synchronized (mService.mGlobalLock) { - // If desktop mode is not enabled, show on the default display. - if (!mService.mForceDesktopModeOnExternalDisplays) { - return DEFAULT_DISPLAY; - } - - // Look for the topmost freeform display. - int firstExternalDisplayId = DEFAULT_DISPLAY; - for (int i = mService.mRoot.mChildren.size() - 1; i >= 0; --i) { - final DisplayContent displayContent = mService.mRoot.mChildren.get(i); - // Heuristic solution here. Currently when "Freeform windows" developer option is - // enabled we automatically put secondary displays in freeform mode and emulating - // "desktop mode". It also makes sense to show the pointer on the same display. - if (displayContent.getWindowingMode() == WINDOWING_MODE_FREEFORM) { - return displayContent.getDisplayId(); - } - - if (firstExternalDisplayId == DEFAULT_DISPLAY - && displayContent.getDisplayId() != DEFAULT_DISPLAY) { - firstExternalDisplayId = displayContent.getDisplayId(); - } - } - - // Look for the topmost non-default display - return firstExternalDisplayId; - } - } - /** Waits until the built-in input devices have been configured. */ public boolean waitForInputDevicesReady(long timeoutMillis) { synchronized (mInputDevicesReadyMonitor) { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 801e5f2038ad..dcade2f012db 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -794,8 +794,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> private void handleResizingWindows() { for (int i = mWmService.mResizingWindows.size() - 1; i >= 0; i--) { WindowState win = mWmService.mResizingWindows.get(i); - if (win.mAppFreezing) { - // Don't remove this window until rotation has completed. + if (win.mAppFreezing || win.getDisplayContent().mWaitingForConfig) { + // Don't remove this window until rotation has completed and is not waiting for the + // complete configuration. continue; } win.reportResized(); diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java index 5bb64407a28d..8c800097e49d 100644 --- a/services/core/java/com/android/server/wm/TaskRecord.java +++ b/services/core/java/com/android/server/wm/TaskRecord.java @@ -1716,7 +1716,7 @@ class TaskRecord extends ConfigurationContainer { updateTaskDescription(); } - private void adjustForMinimalTaskDimensions(Rect bounds) { + private void adjustForMinimalTaskDimensions(Rect bounds, Rect previousBounds) { if (bounds == null) { return; } @@ -1747,9 +1747,8 @@ class TaskRecord extends ConfigurationContainer { return; } - final Rect configBounds = getRequestedOverrideBounds(); if (adjustWidth) { - if (!configBounds.isEmpty() && bounds.right == configBounds.right) { + if (!previousBounds.isEmpty() && bounds.right == previousBounds.right) { bounds.left = bounds.right - minWidth; } else { // Either left bounds match, or neither match, or the previous bounds were @@ -1758,7 +1757,7 @@ class TaskRecord extends ConfigurationContainer { } } if (adjustHeight) { - if (!configBounds.isEmpty() && bounds.bottom == configBounds.bottom) { + if (!previousBounds.isEmpty() && bounds.bottom == previousBounds.bottom) { bounds.top = bounds.bottom - minHeight; } else { // Either top bounds match, or neither match, or the previous bounds were @@ -2113,11 +2112,15 @@ class TaskRecord extends ConfigurationContainer { // TODO(b/113900640): remove this once ActivityRecord is changed to not need it anymore. void computeResolvedOverrideConfiguration(Configuration inOutConfig, Configuration parentConfig, Configuration overrideConfig) { + // Save previous bounds because adjustForMinimalTaskDimensions uses that to determine if it + // changes left bound vs. right bound, or top bound vs. bottom bound. + mTmpBounds.set(inOutConfig.windowConfiguration.getBounds()); + inOutConfig.setTo(overrideConfig); Rect outOverrideBounds = inOutConfig.windowConfiguration.getBounds(); if (outOverrideBounds != null && !outOverrideBounds.isEmpty()) { - adjustForMinimalTaskDimensions(outOverrideBounds); + adjustForMinimalTaskDimensions(outOverrideBounds, mTmpBounds); int windowingMode = overrideConfig.windowConfiguration.getWindowingMode(); if (windowingMode == WINDOWING_MODE_UNDEFINED) { diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index bf83ca912eed..43d2dcf7e0d1 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -107,7 +107,6 @@ static struct { jmethodID getLongPressTimeout; jmethodID getPointerLayer; jmethodID getPointerIcon; - jmethodID getPointerDisplayId; jmethodID getKeyboardLayoutOverlay; jmethodID getDeviceAlias; jmethodID getTouchCalibrationForInputDevice; @@ -175,6 +174,15 @@ static void loadSystemIconAsSprite(JNIEnv* env, jobject contextObj, int32_t styl loadSystemIconAsSpriteWithPointerIcon(env, contextObj, style, &pointerIcon, outSpriteIcon); } +static void updatePointerControllerFromViewport( + sp<PointerController> controller, const DisplayViewport* const viewport) { + if (controller != nullptr && viewport != nullptr) { + const int32_t width = viewport->logicalRight - viewport->logicalLeft; + const int32_t height = viewport->logicalBottom - viewport->logicalTop; + controller->setDisplayViewport(width, height, viewport->orientation); + } +} + enum { WM_ACTION_PASS_TO_USER = 1, }; @@ -234,7 +242,6 @@ public: jfloatArray matrixArr); virtual TouchAffineTransformation getTouchAffineTransformation( const std::string& inputDeviceDescriptor, int32_t surfaceRotation); - virtual void updatePointerDisplay(); /* --- InputDispatcherPolicyInterface implementation --- */ @@ -307,11 +314,10 @@ private: std::atomic<bool> mInteractive; - void updateInactivityTimeoutLocked(); + void updateInactivityTimeoutLocked(const sp<PointerController>& controller); void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags); void ensureSpriteControllerLocked(); - const DisplayViewport* findDisplayViewportLocked(int32_t displayId); - int32_t getPointerDisplayId(); + static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName); static inline JNIEnv* jniEnv() { @@ -385,10 +391,9 @@ bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const c return false; } -const DisplayViewport* NativeInputManager::findDisplayViewportLocked(int32_t displayId) - REQUIRES(mLock) { - for (const DisplayViewport& v : mLocked.viewports) { - if (v.displayId == displayId) { +static const DisplayViewport* findInternalViewport(const std::vector<DisplayViewport>& viewports) { + for (const DisplayViewport& v : viewports) { + if (v.type == ViewportType::VIEWPORT_INTERNAL) { return &v; } } @@ -415,10 +420,20 @@ void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportO } } - { // acquire lock + const DisplayViewport* newInternalViewport = findInternalViewport(viewports); + { AutoMutex _l(mLock); + const DisplayViewport* oldInternalViewport = findInternalViewport(mLocked.viewports); + // Internal viewport has changed if there wasn't one earlier, and there is one now, or, + // if they are different. + const bool internalViewportChanged = (newInternalViewport != nullptr) && + (oldInternalViewport == nullptr || (*oldInternalViewport != *newInternalViewport)); + if (internalViewportChanged) { + sp<PointerController> controller = mLocked.pointerController.promote(); + updatePointerControllerFromViewport(controller, newInternalViewport); + } mLocked.viewports = viewports; - } // release lock + } mInputManager->getReader()->requestRefreshConfiguration( InputReaderConfiguration::CHANGE_DISPLAY_INFO); @@ -541,41 +556,13 @@ sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32 controller = new PointerController(this, mLooper, mLocked.spriteController); mLocked.pointerController = controller; - updateInactivityTimeoutLocked(); - } - return controller; -} + const DisplayViewport* internalViewport = findInternalViewport(mLocked.viewports); + updatePointerControllerFromViewport(controller, internalViewport); -int32_t NativeInputManager::getPointerDisplayId() { - JNIEnv* env = jniEnv(); - jint pointerDisplayId = env->CallIntMethod(mServiceObj, - gServiceClassInfo.getPointerDisplayId); - if (checkAndClearExceptionFromCallback(env, "getPointerDisplayId")) { - pointerDisplayId = ADISPLAY_ID_DEFAULT; - } - - return pointerDisplayId; -} - -void NativeInputManager::updatePointerDisplay() { - ATRACE_CALL(); - - jint pointerDisplayId = getPointerDisplayId(); - - AutoMutex _l(mLock); - sp<PointerController> controller = mLocked.pointerController.promote(); - if (controller != nullptr) { - const DisplayViewport* viewport = findDisplayViewportLocked(pointerDisplayId); - if (viewport == nullptr) { - ALOGW("Can't find pointer display viewport, fallback to default display."); - viewport = findDisplayViewportLocked(ADISPLAY_ID_DEFAULT); - } - - if (viewport != nullptr) { - controller->setDisplayViewport(*viewport); - } + updateInactivityTimeoutLocked(controller); } + return controller; } void NativeInputManager::ensureSpriteControllerLocked() REQUIRES(mLock) { @@ -834,16 +821,16 @@ void NativeInputManager::setSystemUiVisibility(int32_t visibility) { if (mLocked.systemUiVisibility != visibility) { mLocked.systemUiVisibility = visibility; - updateInactivityTimeoutLocked(); - } -} -void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) { - sp<PointerController> controller = mLocked.pointerController.promote(); - if (controller == nullptr) { - return; + sp<PointerController> controller = mLocked.pointerController.promote(); + if (controller != nullptr) { + updateInactivityTimeoutLocked(controller); + } } +} +void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerController>& controller) + REQUIRES(mLock) { bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN; controller->setInactivityTimeout(lightsOut ? PointerController::INACTIVITY_TIMEOUT_SHORT @@ -1837,9 +1824,6 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gServiceClassInfo.getPointerIcon, clazz, "getPointerIcon", "()Landroid/view/PointerIcon;"); - GET_METHOD_ID(gServiceClassInfo.getPointerDisplayId, clazz, - "getPointerDisplayId", "()I"); - GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutOverlay, clazz, "getKeyboardLayoutOverlay", "(Landroid/hardware/input/InputDeviceIdentifier;)[Ljava/lang/String;"); diff --git a/services/net/java/android/net/dhcp/DhcpServer.java b/services/net/java/android/net/dhcp/DhcpServer.java index cee6fa96bbc5..35d29e75c0e4 100644 --- a/services/net/java/android/net/dhcp/DhcpServer.java +++ b/services/net/java/android/net/dhcp/DhcpServer.java @@ -39,7 +39,6 @@ import android.annotation.Nullable; import android.net.MacAddress; import android.net.NetworkUtils; import android.net.TrafficStats; -import android.net.util.InterfaceParams; import android.net.util.SharedLog; import android.os.Handler; import android.os.Looper; @@ -85,7 +84,7 @@ public class DhcpServer { @NonNull private final ServerHandler mHandler; @NonNull - private final InterfaceParams mIface; + private final String mIfName; @NonNull private final DhcpLeaseRepository mLeaseRepo; @NonNull @@ -161,20 +160,20 @@ public class DhcpServer { } } - public DhcpServer(@NonNull Looper looper, @NonNull InterfaceParams iface, + public DhcpServer(@NonNull Looper looper, @NonNull String ifName, @NonNull DhcpServingParams params, @NonNull SharedLog log) { - this(looper, iface, params, log, null); + this(looper, ifName, params, log, null); } @VisibleForTesting - DhcpServer(@NonNull Looper looper, @NonNull InterfaceParams iface, + DhcpServer(@NonNull Looper looper, @NonNull String ifName, @NonNull DhcpServingParams params, @NonNull SharedLog log, @Nullable Dependencies deps) { if (deps == null) { deps = new DependenciesImpl(); } mHandler = new ServerHandler(looper); - mIface = iface; + mIfName = ifName; mServingParams = params; mLog = log; mDeps = deps; @@ -444,7 +443,7 @@ public class DhcpServer { private boolean addArpEntry(@NonNull MacAddress macAddr, @NonNull Inet4Address inetAddr) { try { - mDeps.addArpEntry(inetAddr, macAddr, mIface.name, mSocket); + mDeps.addArpEntry(inetAddr, macAddr, mIfName, mSocket); return true; } catch (IOException e) { mLog.e("Error adding client to ARP table", e); @@ -526,7 +525,7 @@ public class DhcpServer { // SO_BINDTODEVICE actually takes a string. This works because the first member // of struct ifreq is a NULL-terminated interface name. // TODO: add a setsockoptString() - Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mIface.name); + Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mIfName); Os.setsockoptInt(mSocket, SOL_SOCKET, SO_BROADCAST, 1); Os.bind(mSocket, Inet4Address.ANY, DHCP_SERVER); NetworkUtils.protectFromVpn(mSocket); diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java index 823c0a1ac7b0..493350d776f3 100644 --- a/services/net/java/android/net/ip/IpServer.java +++ b/services/net/java/android/net/ip/IpServer.java @@ -138,9 +138,9 @@ public class IpServer extends StateMachine { return NetdService.getInstance(); } - public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface, + public DhcpServer makeDhcpServer(Looper looper, String ifName, DhcpServingParams params, SharedLog log) { - return new DhcpServer(looper, iface, params, log); + return new DhcpServer(looper, ifName, params, log); } } @@ -256,12 +256,6 @@ public class IpServer extends StateMachine { if (mUsingLegacyDhcp) { return true; } - - final InterfaceParams ifaceParams = mDeps.getInterfaceParams(mIfaceName); - if (ifaceParams == null) { - Log.e(TAG, "Failed to find interface params for DHCPv4"); - return false; - } final DhcpServingParams params; try { params = new DhcpServingParams.Builder() @@ -277,7 +271,7 @@ public class IpServer extends StateMachine { return false; } - mDhcpServer = mDeps.makeDhcpServer(getHandler().getLooper(), ifaceParams, params, + mDhcpServer = mDeps.makeDhcpServer(getHandler().getLooper(), mIfaceName, params, mLog.forSubComponent("DHCP")); mDhcpServer.start(); return true; diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk index 0c9c85acf6e2..9159f0d47621 100644 --- a/services/robotests/Android.mk +++ b/services/robotests/Android.mk @@ -41,7 +41,8 @@ include $(CLEAR_VARS) LOCAL_MODULE := FrameworksServicesRoboTests -LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_SRC_FILES := $(call all-java-files-under, src) \ + $(call all-java-files-under, backup/src) LOCAL_RESOURCE_DIR := \ $(LOCAL_PATH)/res @@ -81,4 +82,13 @@ LOCAL_JAVA_LIBRARIES := \ LOCAL_TEST_PACKAGE := FrameworksServicesLib -include external/robolectric-shadows/run_robotests.mk
\ No newline at end of file +LOCAL_ROBOTEST_FILES := $(call find-files-in-subdirs,$(LOCAL_PATH)/src,*Test.java,.) \ + $(call find-files-in-subdirs,$(LOCAL_PATH)/backup/src,*Test.java,.) + +include external/robolectric-shadows/run_robotests.mk + +################################################################### +# include subdir Android.mk files +################################################################### +include $(CLEAR_VARS) +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/services/robotests/backup/Android.mk b/services/robotests/backup/Android.mk new file mode 100644 index 000000000000..cc59b0c9bb16 --- /dev/null +++ b/services/robotests/backup/Android.mk @@ -0,0 +1,84 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH := $(call my-dir) + +################################################################### +# BackupFrameworksServicesLib app just for Robolectric test target # +################################################################### +include $(CLEAR_VARS) + +LOCAL_PACKAGE_NAME := BackupFrameworksServicesLib +LOCAL_PRIVATE_PLATFORM_APIS := true +LOCAL_MODULE_TAGS := optional + +LOCAL_PRIVILEGED_MODULE := true + +LOCAL_STATIC_JAVA_LIBRARIES := \ + bmgrlib \ + bu \ + services.backup \ + services.core \ + services.net + +include $(BUILD_PACKAGE) + +################################################################### +# BackupFrameworksServicesLib Robolectric test target. # +################################################################### +include $(CLEAR_VARS) + +LOCAL_MODULE := BackupFrameworksServicesRoboTests + +LOCAL_SRC_FILES := $(call all-java-files-under, src) \ + $(call all-java-files-under, ../src/com/android/server/testing/shadows) + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res + +LOCAL_JAVA_RESOURCE_DIRS := config + +# Include the testing libraries +LOCAL_JAVA_LIBRARIES := \ + platform-test-annotations \ + robolectric_android-all-stub \ + Robolectric_all-target \ + mockito-robolectric-prebuilt \ + truth-prebuilt \ + testng + +LOCAL_INSTRUMENTATION_FOR := BackupFrameworksServicesLib + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_STATIC_JAVA_LIBRARY) + +################################################################### +# BackupFrameworksServicesLib runner target to run the previous target. # +################################################################### +include $(CLEAR_VARS) + +LOCAL_MODULE := RunBackupFrameworksServicesRoboTests + +LOCAL_JAVA_LIBRARIES := \ + BackupFrameworksServicesRoboTests \ + platform-test-annotations \ + robolectric_android-all-stub \ + Robolectric_all-target \ + mockito-robolectric-prebuilt \ + truth-prebuilt \ + testng + +LOCAL_TEST_PACKAGE := BackupFrameworksServicesLib + +include external/robolectric-shadows/run_robotests.mk diff --git a/services/robotests/backup/config/robolectric.properties b/services/robotests/backup/config/robolectric.properties new file mode 100644 index 000000000000..850557a9b693 --- /dev/null +++ b/services/robotests/backup/config/robolectric.properties @@ -0,0 +1 @@ +sdk=NEWEST_SDK
\ No newline at end of file diff --git a/services/robotests/src/android/app/backup/BackupUtilsTest.java b/services/robotests/backup/src/android/app/backup/BackupUtilsTest.java index 099cde04bd5e..099cde04bd5e 100644 --- a/services/robotests/src/android/app/backup/BackupUtilsTest.java +++ b/services/robotests/backup/src/android/app/backup/BackupUtilsTest.java diff --git a/services/robotests/src/android/app/backup/ForwardingBackupAgent.java b/services/robotests/backup/src/android/app/backup/ForwardingBackupAgent.java index 4ff5b7c03a8f..4ff5b7c03a8f 100644 --- a/services/robotests/src/android/app/backup/ForwardingBackupAgent.java +++ b/services/robotests/backup/src/android/app/backup/ForwardingBackupAgent.java diff --git a/services/robotests/src/com/android/commands/bmgr/BmgrTest.java b/services/robotests/backup/src/com/android/commands/bmgr/BmgrTest.java index 1705f5b8c704..1705f5b8c704 100644 --- a/services/robotests/src/com/android/commands/bmgr/BmgrTest.java +++ b/services/robotests/backup/src/com/android/commands/bmgr/BmgrTest.java diff --git a/services/robotests/src/com/android/commands/bu/AdbBackupTest.java b/services/robotests/backup/src/com/android/commands/bu/AdbBackupTest.java index 6869f5612c1d..6869f5612c1d 100644 --- a/services/robotests/src/com/android/commands/bu/AdbBackupTest.java +++ b/services/robotests/backup/src/com/android/commands/bu/AdbBackupTest.java diff --git a/services/robotests/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java b/services/robotests/backup/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java index 5b226f36d565..5b226f36d565 100644 --- a/services/robotests/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java +++ b/services/robotests/backup/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java diff --git a/services/robotests/src/com/android/server/backup/BackupManagerConstantsTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerConstantsTest.java index affa1f3f97f3..affa1f3f97f3 100644 --- a/services/robotests/src/com/android/server/backup/BackupManagerConstantsTest.java +++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerConstantsTest.java diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java new file mode 100644 index 000000000000..83f66c5258b2 --- /dev/null +++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java @@ -0,0 +1,1480 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.backup; + +import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; + +import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startBackupThread; +import static com.android.server.backup.testing.TransportData.backupTransport; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.robolectric.Shadows.shadowOf; +import static org.testng.Assert.expectThrows; + +import android.annotation.UserIdInt; +import android.app.Application; +import android.app.backup.IBackupManagerMonitor; +import android.app.backup.IBackupObserver; +import android.app.backup.IFullBackupRestoreObserver; +import android.app.backup.ISelectBackupTransportCallback; +import android.content.Context; +import android.content.Intent; +import android.os.IBinder; +import android.os.ParcelFileDescriptor; +import android.os.UserHandle; +import android.platform.test.annotations.Presubmit; +import android.util.SparseArray; + +import com.android.server.backup.testing.TransportData; +import com.android.server.testing.shadows.ShadowBinder; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowContextWrapper; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.PrintWriter; + +/** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */ +@RunWith(RobolectricTestRunner.class) +@Config(shadows = {ShadowBinder.class}) +@Presubmit +public class BackupManagerServiceTest { + private static final String TEST_PACKAGE = "package"; + private static final String TEST_TRANSPORT = "transport"; + private static final String[] ADB_TEST_PACKAGES = {TEST_PACKAGE}; + + private ShadowContextWrapper mShadowContext; + private Context mContext; + @UserIdInt private int mUserOneId; + @UserIdInt private int mUserTwoId; + @Mock private UserBackupManagerService mUserOneService; + @Mock private UserBackupManagerService mUserTwoService; + + /** Initialize {@link BackupManagerService}. */ + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + Application application = RuntimeEnvironment.application; + mContext = application; + mShadowContext = shadowOf(application); + + // TODO(b/120212806): Hardcoding system user for now since most methods in BMS don't yet + // take an user parameter (and instead hardcode the system user). + mUserOneId = UserHandle.USER_SYSTEM; + mUserTwoId = mUserOneId + 1; + } + + /** + * Clean up and reset state that was created for testing {@link BackupManagerService} + * operations. + */ + @After + public void tearDown() throws Exception { + ShadowBinder.reset(); + } + + /** + * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}. This is + * specifically to prevent overloading the logs in production. + */ + @Test + public void testMoreDebug_isFalse() throws Exception { + boolean moreDebug = BackupManagerService.MORE_DEBUG; + + assertThat(moreDebug).isFalse(); + } + + /** Test that the constructor does not create {@link UserBackupManagerService} instances. */ + @Test + public void testConstructor_doesNotRegisterUsers() throws Exception { + BackupManagerService backupManagerService = createService(); + + assertThat(backupManagerService.getServiceUsers().size()).isEqualTo(0); + } + + /** Test that the constructor handles {@code null} parameters. */ + @Test + public void testConstructor_withNullContext_throws() throws Exception { + expectThrows( + NullPointerException.class, + () -> + new BackupManagerService( + /* context */ null, + new Trampoline(mContext), + startBackupThread(null))); + } + + /** Test that the constructor handles {@code null} parameters. */ + @Test + public void testConstructor_withNullTrampoline_throws() throws Exception { + expectThrows( + NullPointerException.class, + () -> + new BackupManagerService( + mContext, /* trampoline */ null, startBackupThread(null))); + } + + /** Test that the constructor handles {@code null} parameters. */ + @Test + public void testConstructor_withNullBackupThread_throws() throws Exception { + expectThrows( + NullPointerException.class, + () -> + new BackupManagerService( + mContext, new Trampoline(mContext), /* backupThread */ null)); + } + + /** Test that the service registers users. */ + @Test + public void testStartServiceForUser_registersUser() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.startServiceForUser(mUserOneId); + + SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers(); + assertThat(serviceUsers.size()).isEqualTo(1); + assertThat(serviceUsers.get(mUserOneId)).isNotNull(); + } + + /** Test that the service registers users. */ + @Test + public void testStartServiceForUser_withServiceInstance_registersUser() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.startServiceForUser(mUserOneId, mUserOneService); + + SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers(); + assertThat(serviceUsers.size()).isEqualTo(1); + assertThat(serviceUsers.get(mUserOneId)).isEqualTo(mUserOneService); + } + + // TODO(b/120212806): When BMS methods take in a user parameter, modify unknown user tests to + // check that that we don't call the method on another registered user. Currently these tests + // have no registered users since we hardcode the system user in BMS. + + // --------------------------------------------- + // Backup agent tests + // --------------------------------------------- + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testDataChanged_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + + backupManagerService.dataChanged(TEST_PACKAGE); + + verify(mUserOneService).dataChanged(TEST_PACKAGE); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testDataChanged_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.dataChanged(TEST_PACKAGE); + + verify(mUserOneService, never()).dataChanged(TEST_PACKAGE); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testAgentConnected_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + IBinder agentBinder = mock(IBinder.class); + + backupManagerService.agentConnected(TEST_PACKAGE, agentBinder); + + verify(mUserOneService).agentConnected(TEST_PACKAGE, agentBinder); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testAgentConnected_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + IBinder agentBinder = mock(IBinder.class); + + backupManagerService.agentConnected(TEST_PACKAGE, agentBinder); + + verify(mUserOneService, never()).agentConnected(TEST_PACKAGE, agentBinder); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testAgentDisconnected_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + + backupManagerService.agentDisconnected(TEST_PACKAGE); + + verify(mUserOneService).agentDisconnected(TEST_PACKAGE); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testAgentDisconnected_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.agentDisconnected(TEST_PACKAGE); + + verify(mUserOneService, never()).agentDisconnected(TEST_PACKAGE); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testOpComplete_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + + backupManagerService.opComplete(/* token */ 0, /* result */ 0L); + + verify(mUserOneService).opComplete(/* token */ 0, /* result */ 0L); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testOpComplete_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.opComplete(/* token */ 0, /* result */ 0L); + + verify(mUserOneService, never()).opComplete(/* token */ 0, /* result */ 0L); + } + + // --------------------------------------------- + // Transport tests + // --------------------------------------------- + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testInitializeTransports_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + String[] transports = {TEST_TRANSPORT}; + + backupManagerService.initializeTransports(transports, /* observer */ null); + + verify(mUserOneService).initializeTransports(transports, /* observer */ null); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testInitializeTransports_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + String[] transports = {TEST_TRANSPORT}; + + backupManagerService.initializeTransports(transports, /* observer */ null); + + verify(mUserOneService, never()).initializeTransports(transports, /* observer */ null); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testClearBackupData_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + + backupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE); + + verify(mUserOneService).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testClearBackupData_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE); + + verify(mUserOneService, never()).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testGetCurrentTransport_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + + backupManagerService.getCurrentTransport(); + + verify(mUserOneService).getCurrentTransport(); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testGetCurrentTransport_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.getCurrentTransport(); + + verify(mUserOneService, never()).getCurrentTransport(); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testGetCurrentTransportComponent_onRegisteredUser_callsMethodForUser() + throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + + backupManagerService.getCurrentTransportComponent(); + + verify(mUserOneService).getCurrentTransportComponent(); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testGetCurrentTransportComponent_onUnknownUser_doesNotPropagateCall() + throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.getCurrentTransportComponent(); + + verify(mUserOneService, never()).getCurrentTransportComponent(); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testListAllTransports_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + + backupManagerService.listAllTransports(); + + verify(mUserOneService).listAllTransports(); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testListAllTransports_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.listAllTransports(); + + verify(mUserOneService, never()).listAllTransports(); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testListAllTransportComponents_onRegisteredUser_callsMethodForUser() + throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + + backupManagerService.listAllTransportComponents(); + + verify(mUserOneService).listAllTransportComponents(); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testListAllTransportComponents_onUnknownUser_doesNotPropagateCall() + throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.listAllTransportComponents(); + + verify(mUserOneService, never()).listAllTransportComponents(); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testGetTransportWhitelist_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + + backupManagerService.getTransportWhitelist(); + + verify(mUserOneService).getTransportWhitelist(); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testGetTransportWhitelist_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.getTransportWhitelist(); + + verify(mUserOneService, never()).getTransportWhitelist(); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testUpdateTransportAttributes_onRegisteredUser_callsMethodForUser() + throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + TransportData transport = backupTransport(); + Intent configurationIntent = new Intent(); + Intent dataManagementIntent = new Intent(); + + backupManagerService.updateTransportAttributes( + transport.getTransportComponent(), + transport.transportName, + configurationIntent, + "currentDestinationString", + dataManagementIntent, + "dataManagementLabel"); + + verify(mUserOneService) + .updateTransportAttributes( + transport.getTransportComponent(), + transport.transportName, + configurationIntent, + "currentDestinationString", + dataManagementIntent, + "dataManagementLabel"); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testUpdateTransportAttributes_onUnknownUser_doesNotPropagateCall() + throws Exception { + BackupManagerService backupManagerService = createService(); + TransportData transport = backupTransport(); + Intent configurationIntent = new Intent(); + Intent dataManagementIntent = new Intent(); + + backupManagerService.updateTransportAttributes( + transport.getTransportComponent(), + transport.transportName, + configurationIntent, + "currentDestinationString", + dataManagementIntent, + "dataManagementLabel"); + + verify(mUserOneService, never()) + .updateTransportAttributes( + transport.getTransportComponent(), + transport.transportName, + configurationIntent, + "currentDestinationString", + dataManagementIntent, + "dataManagementLabel"); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testSelectBackupTransport_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + + backupManagerService.selectBackupTransport(TEST_TRANSPORT); + + verify(mUserOneService).selectBackupTransport(TEST_TRANSPORT); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testSelectBackupTransport_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.selectBackupTransport(TEST_TRANSPORT); + + verify(mUserOneService, never()).selectBackupTransport(TEST_TRANSPORT); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testSelectTransportAsync_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + TransportData transport = backupTransport(); + ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class); + + backupManagerService.selectBackupTransportAsync( + transport.getTransportComponent(), callback); + + verify(mUserOneService) + .selectBackupTransportAsync(transport.getTransportComponent(), callback); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testSelectBackupTransportAsync_onUnknownUser_doesNotPropagateCall() + throws Exception { + BackupManagerService backupManagerService = createService(); + TransportData transport = backupTransport(); + ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class); + + backupManagerService.selectBackupTransportAsync( + transport.getTransportComponent(), callback); + + verify(mUserOneService, never()) + .selectBackupTransportAsync(transport.getTransportComponent(), callback); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testGetConfigurationIntent_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + + backupManagerService.getConfigurationIntent(TEST_TRANSPORT); + + verify(mUserOneService).getConfigurationIntent(TEST_TRANSPORT); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testGetConfigurationIntent_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.getConfigurationIntent(TEST_TRANSPORT); + + verify(mUserOneService, never()).getConfigurationIntent(TEST_TRANSPORT); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testGetDestinationString_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + + backupManagerService.getDestinationString(TEST_TRANSPORT); + + verify(mUserOneService).getDestinationString(TEST_TRANSPORT); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testGetDestinationString_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.getDestinationString(TEST_TRANSPORT); + + verify(mUserOneService, never()).getDestinationString(TEST_TRANSPORT); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testGetDataManagementIntent_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + + backupManagerService.getDataManagementIntent(TEST_TRANSPORT); + + verify(mUserOneService).getDataManagementIntent(TEST_TRANSPORT); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testGetDataManagementIntent_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.getDataManagementIntent(TEST_TRANSPORT); + + verify(mUserOneService, never()).getDataManagementIntent(TEST_TRANSPORT); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testGetDataManagementLabel_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + + backupManagerService.getDataManagementLabel(TEST_TRANSPORT); + + verify(mUserOneService).getDataManagementLabel(TEST_TRANSPORT); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testGetDataManagementLabel_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.getDataManagementLabel(TEST_TRANSPORT); + + verify(mUserOneService, never()).getDataManagementLabel(TEST_TRANSPORT); + } + + // --------------------------------------------- + // Settings tests + // --------------------------------------------- + + /** + * Test that the backup services throws a {@link SecurityException} if the caller does not have + * INTERACT_ACROSS_USERS_FULL permission and passes a different user id. + */ + @Test + public void testSetBackupEnabled_withoutPermission_throwsSecurityExceptionForNonCallingUser() { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); + + expectThrows( + SecurityException.class, + () -> backupManagerService.setBackupEnabled(mUserTwoId, true)); + } + + /** + * Test that the backup service does not throw a {@link SecurityException} if the caller has + * INTERACT_ACROSS_USERS_FULL permission and passes a different user id. + */ + @Test + public void testSetBackupEnabled_withPermission_propagatesForNonCallingUser() { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true); + + backupManagerService.setBackupEnabled(mUserTwoId, true); + + verify(mUserTwoService).setBackupEnabled(true); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testSetBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); + + backupManagerService.setBackupEnabled(mUserOneId, true); + + verify(mUserOneService).setBackupEnabled(true); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testSetBackupEnabled_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); + + backupManagerService.setBackupEnabled(mUserTwoId, true); + + verify(mUserOneService, never()).setBackupEnabled(true); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testSetAutoRestore_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + + backupManagerService.setAutoRestore(true); + + verify(mUserOneService).setAutoRestore(true); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testSetAutoRestore_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.setAutoRestore(true); + + verify(mUserOneService, never()).setAutoRestore(true); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testSetBackupProvisioned_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + + backupManagerService.setBackupProvisioned(true); + + verify(mUserOneService).setBackupProvisioned(true); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testSetBackupProvisioned_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.setBackupProvisioned(true); + + verify(mUserOneService, never()).setBackupProvisioned(true); + } + + /** + * Test that the backup services throws a {@link SecurityException} if the caller does not have + * INTERACT_ACROSS_USERS_FULL permission and passes a different user id. + */ + @Test + public void testIsBackupEnabled_withoutPermission_throwsSecurityExceptionForNonCallingUser() { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); + + expectThrows( + SecurityException.class, () -> backupManagerService.isBackupEnabled(mUserTwoId)); + } + + /** + * Test that the backup service does not throw a {@link SecurityException} if the caller has + * INTERACT_ACROSS_USERS_FULL permission and passes a different user id. + */ + @Test + public void testIsBackupEnabled_withPermission_propagatesForNonCallingUser() { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true); + + backupManagerService.isBackupEnabled(mUserTwoId); + + verify(mUserTwoService).isBackupEnabled(); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testIsBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); + + backupManagerService.isBackupEnabled(mUserOneId); + + verify(mUserOneService).isBackupEnabled(); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testIsBackupEnabled_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); + + backupManagerService.isBackupEnabled(mUserTwoId); + + verify(mUserOneService, never()).isBackupEnabled(); + } + + // --------------------------------------------- + // Backup tests + // --------------------------------------------- + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testIsAppEligibleForBackup_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + + backupManagerService.isAppEligibleForBackup(TEST_PACKAGE); + + verify(mUserOneService).isAppEligibleForBackup(TEST_PACKAGE); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testIsAppEligibleForBackup_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.isAppEligibleForBackup(TEST_PACKAGE); + + verify(mUserOneService, never()).isAppEligibleForBackup(TEST_PACKAGE); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testFilterAppsEligibleForBackup_onRegisteredUser_callsMethodForUser() + throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + String[] packages = {TEST_PACKAGE}; + + backupManagerService.filterAppsEligibleForBackup(packages); + + verify(mUserOneService).filterAppsEligibleForBackup(packages); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testFilterAppsEligibleForBackup_onUnknownUser_doesNotPropagateCall() + throws Exception { + BackupManagerService backupManagerService = createService(); + String[] packages = {TEST_PACKAGE}; + + backupManagerService.filterAppsEligibleForBackup(packages); + + verify(mUserOneService, never()).filterAppsEligibleForBackup(packages); + } + + /** + * Test verifying that {@link BackupManagerService#backupNow(int)} throws a {@link + * SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission. + */ + @Test + public void testBackupNow_withoutPermission_throwsSecurityExceptionForNonCallingUser() { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); + + expectThrows(SecurityException.class, () -> backupManagerService.backupNow(mUserTwoId)); + } + + /** + * Test that the backup service does not throw a {@link SecurityException} if the caller has + * INTERACT_ACROSS_USERS_FULL permission and passes a different user id. + */ + @Test + public void testBackupNow_withPermission_propagatesForNonCallingUser() { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true); + + backupManagerService.backupNow(mUserTwoId); + + verify(mUserTwoService).backupNow(); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testBackupNow_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); + + backupManagerService.backupNow(mUserOneId); + + verify(mUserOneService).backupNow(); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testBackupNow_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); + + backupManagerService.backupNow(mUserTwoId); + + verify(mUserOneService, never()).backupNow(); + } + + /** + * Test that the backup services throws a {@link SecurityException} if the caller does not have + * INTERACT_ACROSS_USERS_FULL permission and passes a different user id. + */ + @Test + public void testRequestBackup_withoutPermission_throwsSecurityExceptionForNonCallingUser() { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); + String[] packages = {TEST_PACKAGE}; + IBackupObserver observer = mock(IBackupObserver.class); + IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class); + + expectThrows( + SecurityException.class, + () -> + backupManagerService.requestBackup( + mUserTwoId, packages, observer, monitor, 0)); + } + + /** + * Test that the backup service does not throw a {@link SecurityException} if the caller has + * INTERACT_ACROSS_USERS_FULL permission and passes a different user id. + */ + @Test + public void testRequestBackup_withPermission_propagatesForNonCallingUser() { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService); + String[] packages = {TEST_PACKAGE}; + IBackupObserver observer = mock(IBackupObserver.class); + IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true); + + backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0); + + verify(mUserTwoService).requestBackup(packages, observer, monitor, /* flags */ 0); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testRequestBackup_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + String[] packages = {TEST_PACKAGE}; + IBackupObserver observer = mock(IBackupObserver.class); + IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); + + backupManagerService.requestBackup(mUserOneId, packages, observer, monitor, /* flags */ 0); + + verify(mUserOneService).requestBackup(packages, observer, monitor, /* flags */ 0); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testRequestBackup_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + String[] packages = {TEST_PACKAGE}; + IBackupObserver observer = mock(IBackupObserver.class); + IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); + + backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0); + + verify(mUserOneService, never()).requestBackup(packages, observer, monitor, /* flags */ 0); + } + + /** + * Test verifying that {@link BackupManagerService#cancelBackups(int)} throws a {@link + * SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission. + */ + @Test + public void testCancelBackups_withoutPermission_throwsSecurityExceptionForNonCallingUser() { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); + + expectThrows(SecurityException.class, () -> backupManagerService.cancelBackups(mUserTwoId)); + } + + /** + * Test that the backup service does not throw a {@link SecurityException} if the caller has + * INTERACT_ACROSS_USERS_FULL permission and passes a different user id. + */ + @Test + public void testCancelBackups_withPermission_propagatesForNonCallingUser() { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true); + + backupManagerService.cancelBackups(mUserTwoId); + + verify(mUserTwoService).cancelBackups(); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testCancelBackups_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); + + backupManagerService.cancelBackups(mUserOneId); + + verify(mUserOneService).cancelBackups(); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testCancelBackups_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); + + backupManagerService.cancelBackups(mUserTwoId); + + verify(mUserOneService, never()).cancelBackups(); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testBeginFullBackup_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + FullBackupJob job = new FullBackupJob(); + + backupManagerService.beginFullBackup(job); + + verify(mUserOneService).beginFullBackup(job); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testBeginFullBackup_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + FullBackupJob job = new FullBackupJob(); + + backupManagerService.beginFullBackup(job); + + verify(mUserOneService, never()).beginFullBackup(job); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testEndFullBackup_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + + backupManagerService.endFullBackup(); + + verify(mUserOneService).endFullBackup(); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testEndFullBackup_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.endFullBackup(); + + verify(mUserOneService, never()).endFullBackup(); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testFullTransportBackup_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + String[] packages = {TEST_PACKAGE}; + + backupManagerService.fullTransportBackup(packages); + + verify(mUserOneService).fullTransportBackup(packages); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testFullTransportBackup_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + String[] packages = {TEST_PACKAGE}; + + backupManagerService.fullTransportBackup(packages); + + verify(mUserOneService, never()).fullTransportBackup(packages); + } + + // --------------------------------------------- + // Restore tests + // --------------------------------------------- + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testRestoreAtInstall_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + + backupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0); + + verify(mUserOneService).restoreAtInstall(TEST_PACKAGE, /* token */ 0); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testRestoreAtInstall_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0); + + verify(mUserOneService, never()).restoreAtInstall(TEST_PACKAGE, /* token */ 0); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testBeginRestoreSession_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + + backupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT); + + verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testBeginRestoreSession_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT); + + verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testGetAvailableRestoreToken_onRegisteredUser_callsMethodForUser() + throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + + backupManagerService.getAvailableRestoreToken(TEST_PACKAGE); + + verify(mUserOneService).getAvailableRestoreToken(TEST_PACKAGE); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testGetAvailableRestoreToken_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.getAvailableRestoreToken(TEST_PACKAGE); + + verify(mUserOneService, never()).getAvailableRestoreToken(TEST_PACKAGE); + } + + // --------------------------------------------- + // Adb backup/restore tests + // --------------------------------------------- + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testSetBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + + backupManagerService.setBackupPassword("currentPassword", "newPassword"); + + verify(mUserOneService).setBackupPassword("currentPassword", "newPassword"); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testSetBackupPassword_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.setBackupPassword("currentPassword", "newPassword"); + + verify(mUserOneService, never()).setBackupPassword("currentPassword", "newPassword"); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testHasBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + + backupManagerService.hasBackupPassword(); + + verify(mUserOneService).hasBackupPassword(); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testHasBackupPassword_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.hasBackupPassword(); + + verify(mUserOneService, never()).hasBackupPassword(); + } + + /** + * Test that the backup services throws a {@link SecurityException} if the caller does not have + * INTERACT_ACROSS_USERS_FULL permission and passes a different user id. + */ + @Test + public void testAdbBackup_withoutPermission_throwsSecurityExceptionForNonCallingUser() { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); + + expectThrows( + SecurityException.class, + () -> + backupManagerService.adbBackup( + mUserTwoId, + /* parcelFileDescriptor*/ null, + /* includeApks */ true, + /* includeObbs */ true, + /* includeShared */ true, + /* doWidgets */ true, + /* doAllApps */ true, + /* includeSystem */ true, + /* doCompress */ true, + /* doKeyValue */ true, + null)); + } + + /** + * Test that the backup service does not throw a {@link SecurityException} if the caller has + * INTERACT_ACROSS_USERS_FULL permission and passes a different user id. + */ + @Test + public void testAdbBackup_withPermission_propagatesForNonCallingUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService); + ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest(); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true); + + backupManagerService.adbBackup( + mUserTwoId, + parcelFileDescriptor, + /* includeApks */ true, + /* includeObbs */ true, + /* includeShared */ true, + /* doWidgets */ true, + /* doAllApps */ true, + /* includeSystem */ true, + /* doCompress */ true, + /* doKeyValue */ true, + ADB_TEST_PACKAGES); + + verify(mUserTwoService) + .adbBackup( + parcelFileDescriptor, + /* includeApks */ true, + /* includeObbs */ true, + /* includeShared */ true, + /* doWidgets */ true, + /* doAllApps */ true, + /* includeSystem */ true, + /* doCompress */ true, + /* doKeyValue */ true, + ADB_TEST_PACKAGES); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testAdbBackup_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest(); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); + + backupManagerService.adbBackup( + mUserOneId, + parcelFileDescriptor, + /* includeApks */ true, + /* includeObbs */ true, + /* includeShared */ true, + /* doWidgets */ true, + /* doAllApps */ true, + /* includeSystem */ true, + /* doCompress */ true, + /* doKeyValue */ true, + ADB_TEST_PACKAGES); + + verify(mUserOneService) + .adbBackup( + parcelFileDescriptor, + /* includeApks */ true, + /* includeObbs */ true, + /* includeShared */ true, + /* doWidgets */ true, + /* doAllApps */ true, + /* includeSystem */ true, + /* doCompress */ true, + /* doKeyValue */ true, + ADB_TEST_PACKAGES); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testAdbBackup_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest(); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); + + backupManagerService.adbBackup( + mUserTwoId, + parcelFileDescriptor, + /* includeApks */ true, + /* includeObbs */ true, + /* includeShared */ true, + /* doWidgets */ true, + /* doAllApps */ true, + /* includeSystem */ true, + /* doCompress */ true, + /* doKeyValue */ true, + ADB_TEST_PACKAGES); + + verify(mUserOneService, never()) + .adbBackup( + parcelFileDescriptor, + /* includeApks */ true, + /* includeObbs */ true, + /* includeShared */ true, + /* doWidgets */ true, + /* doAllApps */ true, + /* includeSystem */ true, + /* doCompress */ true, + /* doKeyValue */ true, + ADB_TEST_PACKAGES); + } + + /** + * Test that the backup services throws a {@link SecurityException} if the caller does not have + * INTERACT_ACROSS_USERS_FULL permission and passes a different user id. + */ + @Test + public void testAdbRestore_withoutPermission_throwsSecurityExceptionForNonCallingUser() { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); + + expectThrows( + SecurityException.class, () -> backupManagerService.adbRestore(mUserTwoId, null)); + } + + /** + * Test that the backup service does not throw a {@link SecurityException} if the caller has + * INTERACT_ACROSS_USERS_FULL permission and passes a different user id. + */ + @Test + public void testAdbRestore_withPermission_propagatesForNonCallingUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService); + ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest(); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true); + + backupManagerService.adbRestore(mUserTwoId, parcelFileDescriptor); + + verify(mUserTwoService).adbRestore(parcelFileDescriptor); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testAdbRestore_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest(); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); + + backupManagerService.adbRestore(mUserOneId, parcelFileDescriptor); + + verify(mUserOneService).adbRestore(parcelFileDescriptor); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testAdbRestore_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest(); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); + + backupManagerService.adbRestore(mUserTwoId, parcelFileDescriptor); + + verify(mUserOneService, never()).adbRestore(parcelFileDescriptor); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testAcknowledgeAdbBackupOrRestore_onRegisteredUser_callsMethodForUser() + throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class); + + backupManagerService.acknowledgeAdbBackupOrRestore( + /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer); + + verify(mUserOneService) + .acknowledgeAdbBackupOrRestore( + /* token */ 0, + /* allow */ true, + "currentPassword", + "encryptionPassword", + observer); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testAcknowledgeAdbBackupOrRestore_onUnknownUser_doesNotPropagateCall() + throws Exception { + BackupManagerService backupManagerService = createService(); + IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class); + + backupManagerService.acknowledgeAdbBackupOrRestore( + /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer); + + verify(mUserOneService, never()) + .acknowledgeAdbBackupOrRestore( + /* token */ 0, + /* allow */ true, + "currentPassword", + "encryptionPassword", + observer); + } + + // --------------------------------------------- + // Service tests + // --------------------------------------------- + + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testDump_onRegisteredUser_callsMethodForUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + File testFile = new File(mContext.getFilesDir(), "test"); + testFile.createNewFile(); + FileDescriptor fileDescriptor = new FileDescriptor(); + PrintWriter printWriter = new PrintWriter(testFile); + String[] args = {"1", "2"}; + + backupManagerService.dump(fileDescriptor, printWriter, args); + + verify(mUserOneService).dump(fileDescriptor, printWriter, args); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testDump_onUnknownUser_doesNotPropagateCall() throws Exception { + BackupManagerService backupManagerService = createService(); + File testFile = new File(mContext.getFilesDir(), "test"); + testFile.createNewFile(); + FileDescriptor fileDescriptor = new FileDescriptor(); + PrintWriter printWriter = new PrintWriter(testFile); + String[] args = {"1", "2"}; + + backupManagerService.dump(fileDescriptor, printWriter, args); + + verify(mUserOneService, never()).dump(fileDescriptor, printWriter, args); + } + + private BackupManagerService createService() { + return new BackupManagerService( + mContext, new Trampoline(mContext), startBackupThread(null)); + } + + private BackupManagerService createServiceAndRegisterUser( + int userId, UserBackupManagerService userBackupManagerService) { + BackupManagerService backupManagerService = createService(); + backupManagerService.startServiceForUser(userId, userBackupManagerService); + return backupManagerService; + } + + /** + * Sets the calling user to {@code userId} and grants the permission INTERACT_ACROSS_USERS_FULL + * to the caller if {@code shouldGrantPermission} is {@code true}, else it denies the + * permission. + */ + private void setCallerAndGrantInteractUserPermission( + @UserIdInt int userId, boolean shouldGrantPermission) { + ShadowBinder.setCallingUserHandle(UserHandle.of(userId)); + if (shouldGrantPermission) { + mShadowContext.grantPermissions(INTERACT_ACROSS_USERS_FULL); + } else { + mShadowContext.denyPermissions(INTERACT_ACROSS_USERS_FULL); + } + } + + private ParcelFileDescriptor getFileDescriptorForAdbTest() throws Exception { + File testFile = new File(mContext.getFilesDir(), "test"); + testFile.createNewFile(); + return ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE); + } +} diff --git a/services/robotests/src/com/android/server/backup/KeyValueBackupJobTest.java b/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java index 8e17209e1f50..8e17209e1f50 100644 --- a/services/robotests/src/com/android/server/backup/KeyValueBackupJobTest.java +++ b/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java index 693092da0334..693092da0334 100644 --- a/services/robotests/src/com/android/server/backup/TransportManagerTest.java +++ b/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java diff --git a/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java index efbcb960c1e9..efbcb960c1e9 100644 --- a/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java +++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java index 3f57240bc0e9..3f57240bc0e9 100644 --- a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java index 4354db72554a..4354db72554a 100644 --- a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkTest.java index 17c9a86169be..17c9a86169be 100644 --- a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java index 0bf14174e5c3..0bf14174e5c3 100644 --- a/services/robotests/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java index d0e5fb335da9..d0e5fb335da9 100644 --- a/services/robotests/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java index 2bbbf2857146..2bbbf2857146 100644 --- a/services/robotests/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java index 8e801a133909..8e801a133909 100644 --- a/services/robotests/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java index 2f872beacd17..2f872beacd17 100644 --- a/services/robotests/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java index 978bddb7301a..978bddb7301a 100644 --- a/services/robotests/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java index 19ef8fb339ba..19ef8fb339ba 100644 --- a/services/robotests/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java index 77b734785424..77b734785424 100644 --- a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java index 936b5dca033d..936b5dca033d 100644 --- a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java index 549437454e9c..549437454e9c 100644 --- a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java index 277dc372e73c..277dc372e73c 100644 --- a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java index 729580cf5101..729580cf5101 100644 --- a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java index 5342efa18a97..5342efa18a97 100644 --- a/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java index 89977f82c145..89977f82c145 100644 --- a/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java index 48216f8d7aca..48216f8d7aca 100644 --- a/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java index 49bb410ceb65..49bb410ceb65 100644 --- a/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java index 87f21bfa59c2..87f21bfa59c2 100644 --- a/services/robotests/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java index 319ec89f445e..319ec89f445e 100644 --- a/services/robotests/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java diff --git a/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java index 423512ce4b70..423512ce4b70 100644 --- a/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java +++ b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java diff --git a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java b/services/robotests/backup/src/com/android/server/backup/internal/PerformInitializeTaskTest.java index a14cc51a3ab6..a14cc51a3ab6 100644 --- a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java +++ b/services/robotests/backup/src/com/android/server/backup/internal/PerformInitializeTaskTest.java diff --git a/services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/AgentExceptionTest.java index b00b922ed3d0..b00b922ed3d0 100644 --- a/services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java +++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/AgentExceptionTest.java diff --git a/services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/BackupExceptionTest.java index d5603d64687a..d5603d64687a 100644 --- a/services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java +++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/BackupExceptionTest.java diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java index a1b8a9520524..a1b8a9520524 100644 --- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java +++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java index 099127cbeb4b..099127cbeb4b 100644 --- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java +++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java diff --git a/services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/TaskExceptionTest.java index 3698b79872b1..3698b79872b1 100644 --- a/services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.java +++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/TaskExceptionTest.java diff --git a/services/robotests/src/com/android/server/backup/remote/FutureBackupCallbackTest.java b/services/robotests/backup/src/com/android/server/backup/remote/FutureBackupCallbackTest.java index 5ac26f49abdc..5ac26f49abdc 100644 --- a/services/robotests/src/com/android/server/backup/remote/FutureBackupCallbackTest.java +++ b/services/robotests/backup/src/com/android/server/backup/remote/FutureBackupCallbackTest.java diff --git a/services/robotests/src/com/android/server/backup/remote/RemoteCallTest.java b/services/robotests/backup/src/com/android/server/backup/remote/RemoteCallTest.java index 7ec2a4e66903..7ec2a4e66903 100644 --- a/services/robotests/src/com/android/server/backup/remote/RemoteCallTest.java +++ b/services/robotests/backup/src/com/android/server/backup/remote/RemoteCallTest.java diff --git a/services/robotests/src/com/android/server/backup/remote/RemoteResultTest.java b/services/robotests/backup/src/com/android/server/backup/remote/RemoteResultTest.java index b9a77fbafe61..b9a77fbafe61 100644 --- a/services/robotests/src/com/android/server/backup/remote/RemoteResultTest.java +++ b/services/robotests/backup/src/com/android/server/backup/remote/RemoteResultTest.java diff --git a/services/robotests/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java b/services/robotests/backup/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java index 38a54dab3fb8..38a54dab3fb8 100644 --- a/services/robotests/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java +++ b/services/robotests/backup/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java diff --git a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java index 859392d80d36..859392d80d36 100644 --- a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java +++ b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java diff --git a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java index 06f6d21b9ca9..06f6d21b9ca9 100644 --- a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java +++ b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java diff --git a/services/robotests/src/com/android/server/backup/testing/CryptoTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/CryptoTestUtils.java index 0428796f51fa..0428796f51fa 100644 --- a/services/robotests/src/com/android/server/backup/testing/CryptoTestUtils.java +++ b/services/robotests/backup/src/com/android/server/backup/testing/CryptoTestUtils.java diff --git a/services/robotests/src/com/android/server/backup/testing/PackageData.java b/services/robotests/backup/src/com/android/server/backup/testing/PackageData.java index f9177a80fbfe..f9177a80fbfe 100644 --- a/services/robotests/src/com/android/server/backup/testing/PackageData.java +++ b/services/robotests/backup/src/com/android/server/backup/testing/PackageData.java diff --git a/services/robotests/src/com/android/server/backup/testing/TestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/TestUtils.java index 3fe1f3f90f2f..3fe1f3f90f2f 100644 --- a/services/robotests/src/com/android/server/backup/testing/TestUtils.java +++ b/services/robotests/backup/src/com/android/server/backup/testing/TestUtils.java diff --git a/services/robotests/src/com/android/server/backup/testing/TransportData.java b/services/robotests/backup/src/com/android/server/backup/testing/TransportData.java index 77f5d9a48c18..77f5d9a48c18 100644 --- a/services/robotests/src/com/android/server/backup/testing/TransportData.java +++ b/services/robotests/backup/src/com/android/server/backup/testing/TransportData.java diff --git a/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/TransportTestUtils.java index f6ed6307c82f..f6ed6307c82f 100644 --- a/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java +++ b/services/robotests/backup/src/com/android/server/backup/testing/TransportTestUtils.java diff --git a/services/robotests/src/com/android/server/backup/testing/Utils.java b/services/robotests/backup/src/com/android/server/backup/testing/Utils.java index b0e00a2656d0..b0e00a2656d0 100644 --- a/services/robotests/src/com/android/server/backup/testing/Utils.java +++ b/services/robotests/backup/src/com/android/server/backup/testing/Utils.java diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java b/services/robotests/backup/src/com/android/server/backup/transport/TransportClientManagerTest.java index 7dd0d927edd0..7dd0d927edd0 100644 --- a/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java +++ b/services/robotests/backup/src/com/android/server/backup/transport/TransportClientManagerTest.java diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java b/services/robotests/backup/src/com/android/server/backup/transport/TransportClientTest.java index 7281a3c87a29..7281a3c87a29 100644 --- a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java +++ b/services/robotests/backup/src/com/android/server/backup/transport/TransportClientTest.java diff --git a/services/robotests/src/com/android/server/backup/transport/TransportStatsTest.java b/services/robotests/backup/src/com/android/server/backup/transport/TransportStatsTest.java index f01a6b067c8a..f01a6b067c8a 100644 --- a/services/robotests/src/com/android/server/backup/transport/TransportStatsTest.java +++ b/services/robotests/backup/src/com/android/server/backup/transport/TransportStatsTest.java diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java deleted file mode 100644 index 58bce1cdfbf1..000000000000 --- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java +++ /dev/null @@ -1,744 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.backup; - -import static com.android.server.backup.testing.TransportData.backupTransport; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.robolectric.Shadows.shadowOf; -import static org.testng.Assert.expectThrows; - -import android.annotation.UserIdInt; -import android.app.Application; -import android.app.backup.IBackupManagerMonitor; -import android.app.backup.IBackupObserver; -import android.app.backup.IFullBackupRestoreObserver; -import android.app.backup.ISelectBackupTransportCallback; -import android.content.Context; -import android.content.Intent; -import android.os.IBinder; -import android.os.ParcelFileDescriptor; -import android.os.UserHandle; -import android.platform.test.annotations.Presubmit; - -import com.android.server.backup.testing.BackupManagerServiceTestUtils; -import com.android.server.backup.testing.TransportData; -import com.android.server.testing.shadows.ShadowBinder; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; -import org.robolectric.shadows.ShadowContextWrapper; - -import java.io.File; -import java.io.FileDescriptor; -import java.io.PrintWriter; - -/** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */ -@RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowBinder.class}) -@Presubmit -public class BackupManagerServiceTest { - private static final String TEST_PACKAGE = "package"; - private static final String TEST_TRANSPORT = "transport"; - - private static final String[] ADB_TEST_PACKAGES = {TEST_PACKAGE}; - - private static final int NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 1; - - private ShadowContextWrapper mShadowContext; - @Mock private UserBackupManagerService mUserBackupManagerService; - private BackupManagerService mBackupManagerService; - private Context mContext; - @UserIdInt private int mUserId; - - /** Initialize {@link BackupManagerService}. */ - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - Application application = RuntimeEnvironment.application; - mContext = application; - mShadowContext = shadowOf(application); - mUserId = NON_USER_SYSTEM; - mBackupManagerService = - new BackupManagerService( - application, - new Trampoline(application), - BackupManagerServiceTestUtils.startBackupThread(null)); - mBackupManagerService.setUserBackupManagerService(mUserBackupManagerService); - } - - /** - * Clean up and reset state that was created for testing {@link BackupManagerService} - * operations. - */ - @After - public void tearDown() throws Exception { - ShadowBinder.reset(); - } - - /** - * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}. - * This is specifically to prevent overloading the logs in production. - */ - @Test - public void testMoreDebug_isFalse() throws Exception { - boolean moreDebug = BackupManagerService.MORE_DEBUG; - - assertThat(moreDebug).isFalse(); - } - - // TODO(b/118520567): Change the following tests to use the per-user instance of - // UserBackupManagerService once it's implemented. Currently these tests only test the straight - // forward redirection. - - // --------------------------------------------- - // Backup agent tests - // --------------------------------------------- - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testDataChanged_callsDataChangedForUser() throws Exception { - mBackupManagerService.dataChanged(TEST_PACKAGE); - - verify(mUserBackupManagerService).dataChanged(TEST_PACKAGE); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testAgentConnected_callsAgentConnectedForUser() throws Exception { - IBinder agentBinder = mock(IBinder.class); - - mBackupManagerService.agentConnected(TEST_PACKAGE, agentBinder); - - verify(mUserBackupManagerService).agentConnected(TEST_PACKAGE, agentBinder); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testAgentDisconnected_callsAgentDisconnectedForUser() throws Exception { - mBackupManagerService.agentDisconnected(TEST_PACKAGE); - - verify(mUserBackupManagerService).agentDisconnected(TEST_PACKAGE); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testOpComplete_callsOpCompleteForUser() throws Exception { - mBackupManagerService.opComplete(/* token */ 0, /* result */ 0L); - - verify(mUserBackupManagerService).opComplete(/* token */ 0, /* result */ 0L); - } - - // --------------------------------------------- - // Transport tests - // --------------------------------------------- - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testInitializeTransports_callsInitializeTransportsForUser() throws Exception { - String[] transports = {TEST_TRANSPORT}; - - mBackupManagerService.initializeTransports(transports, /* observer */ null); - - verify(mUserBackupManagerService).initializeTransports(transports, /* observer */ null); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testClearBackupData_callsClearBackupDataForUser() throws Exception { - mBackupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE); - - verify(mUserBackupManagerService).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testGetCurrentTransport_callsGetCurrentTransportForUser() throws Exception { - mBackupManagerService.getCurrentTransport(); - - verify(mUserBackupManagerService).getCurrentTransport(); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testGetCurrentTransportComponent_callsGetCurrentTransportComponentForUser() - throws Exception { - mBackupManagerService.getCurrentTransportComponent(); - - verify(mUserBackupManagerService).getCurrentTransportComponent(); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testListAllTransports_callsListAllTransportsForUser() throws Exception { - mBackupManagerService.listAllTransports(); - - verify(mUserBackupManagerService).listAllTransports(); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testListAllTransportComponents_callsListAllTransportComponentsForUser() - throws Exception { - mBackupManagerService.listAllTransportComponents(); - - verify(mUserBackupManagerService).listAllTransportComponents(); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testGetTransportWhitelist_callsGetTransportWhitelistForUser() throws Exception { - mBackupManagerService.getTransportWhitelist(); - - verify(mUserBackupManagerService).getTransportWhitelist(); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testUpdateTransportAttributes_callsUpdateTransportAttributesForUser() - throws Exception { - TransportData transport = backupTransport(); - Intent configurationIntent = new Intent(); - Intent dataManagementIntent = new Intent(); - - mBackupManagerService.updateTransportAttributes( - transport.getTransportComponent(), - transport.transportName, - configurationIntent, - "currentDestinationString", - dataManagementIntent, - "dataManagementLabel"); - - verify(mUserBackupManagerService) - .updateTransportAttributes( - transport.getTransportComponent(), - transport.transportName, - configurationIntent, - "currentDestinationString", - dataManagementIntent, - "dataManagementLabel"); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testSelectBackupTransport_callsSelectBackupTransportForUser() throws Exception { - mBackupManagerService.selectBackupTransport(TEST_TRANSPORT); - - verify(mUserBackupManagerService).selectBackupTransport(TEST_TRANSPORT); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testSelectTransportAsync_callsSelectTransportAsyncForUser() throws Exception { - TransportData transport = backupTransport(); - ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class); - - mBackupManagerService.selectBackupTransportAsync( - transport.getTransportComponent(), callback); - - verify(mUserBackupManagerService) - .selectBackupTransportAsync(transport.getTransportComponent(), callback); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testGetConfigurationIntent_callsGetConfigurationIntentForUser() throws Exception { - mBackupManagerService.getConfigurationIntent(TEST_TRANSPORT); - - verify(mUserBackupManagerService).getConfigurationIntent(TEST_TRANSPORT); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testGetDestinationString_callsGetDestinationStringForUser() throws Exception { - mBackupManagerService.getDestinationString(TEST_TRANSPORT); - - verify(mUserBackupManagerService).getDestinationString(TEST_TRANSPORT); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testGetDataManagementIntent_callsGetDataManagementIntentForUser() throws Exception { - mBackupManagerService.getDataManagementIntent(TEST_TRANSPORT); - - verify(mUserBackupManagerService).getDataManagementIntent(TEST_TRANSPORT); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testGetDataManagementLabel_callsGetDataManagementLabelForUser() throws Exception { - mBackupManagerService.getDataManagementLabel(TEST_TRANSPORT); - - verify(mUserBackupManagerService).getDataManagementLabel(TEST_TRANSPORT); - } - - // --------------------------------------------- - // Settings tests - // --------------------------------------------- - /** - * Test verifying that {@link BackupManagerService#setBackupEnabled(int, boolean)} throws a - * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission. - */ - @Test - public void setBackupEnabled_withoutPermission_throwsSecurityException() { - mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); - - expectThrows( - SecurityException.class, - () -> mBackupManagerService.setBackupEnabled(mUserId, true)); - } - - /** - * Test verifying that {@link BackupManagerService#setBackupEnabled(int, boolean)} does not - * require the caller to have INTERACT_ACROSS_USERS_FULL permission when the calling user id is - * the same as the target user id. - */ - @Test - public void setBackupEnabled_whenCallingUserIsTargetUser_doesntNeedPermission() { - ShadowBinder.setCallingUserHandle(UserHandle.of(mUserId)); - mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); - - mBackupManagerService.setBackupEnabled(mUserId, true); - - verify(mUserBackupManagerService).setBackupEnabled(true); - } - - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void setBackupEnabled_callsSetBackupEnabledForUser() throws Exception { - mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); - - mBackupManagerService.setBackupEnabled(mUserId, true); - - verify(mUserBackupManagerService).setBackupEnabled(true); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void setAutoRestore_callsSetAutoRestoreForUser() throws Exception { - mBackupManagerService.setAutoRestore(true); - - verify(mUserBackupManagerService).setAutoRestore(true); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testSetBackupProvisioned_callsSetBackupProvisionedForUser() throws Exception { - mBackupManagerService.setBackupProvisioned(true); - - verify(mUserBackupManagerService).setBackupProvisioned(true); - } - - /** - * Test verifying that {@link BackupManagerService#isBackupEnabled(int)} throws a - * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission. - */ - @Test - public void testIsBackupEnabled_withoutPermission_throwsSecurityException() { - mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); - - expectThrows( - SecurityException.class, - () -> mBackupManagerService.isBackupEnabled(mUserId)); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testIsBackupEnabled_callsIsBackupEnabledForUser() throws Exception { - mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); - - mBackupManagerService.isBackupEnabled(mUserId); - - verify(mUserBackupManagerService).isBackupEnabled(); - } - - // --------------------------------------------- - // Backup tests - // --------------------------------------------- - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testIsAppEligibleForBackup_callsIsAppEligibleForBackupForUser() throws Exception { - mBackupManagerService.isAppEligibleForBackup(TEST_PACKAGE); - - verify(mUserBackupManagerService).isAppEligibleForBackup(TEST_PACKAGE); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testFilterAppsEligibleForBackup_callsFilterAppsEligibleForBackupForUser() - throws Exception { - String[] packages = {TEST_PACKAGE}; - - mBackupManagerService.filterAppsEligibleForBackup(packages); - - verify(mUserBackupManagerService).filterAppsEligibleForBackup(packages); - } - - /** - * Test verifying that {@link BackupManagerService#backupNow(int)} throws a - * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission. - */ - @Test - public void testBackupNow_withoutPermission_throwsSecurityException() { - mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); - - expectThrows( - SecurityException.class, - () -> mBackupManagerService.backupNow(mUserId)); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testBackupNow_callsBackupNowForUser() throws Exception { - mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); - - mBackupManagerService.backupNow(mUserId); - - verify(mUserBackupManagerService).backupNow(); - } - - /** - * Test verifying that {@link BackupManagerService#requestBackup(int, String[], IBackupObserver, - * IBackupManagerMonitor, int)} throws a {@link SecurityException} if the caller does not have - * INTERACT_ACROSS_USERS_FULL permission. - */ - @Test - public void testRequestBackup_withoutPermission_throwsSecurityException() { - mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); - String[] packages = {TEST_PACKAGE}; - IBackupObserver observer = mock(IBackupObserver.class); - IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class); - - expectThrows( - SecurityException.class, - () -> mBackupManagerService.requestBackup(mUserId, packages, observer, monitor, 0)); - } - - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testRequestBackup_callsRequestBackupForUser() throws Exception { - mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); - String[] packages = {TEST_PACKAGE}; - IBackupObserver observer = mock(IBackupObserver.class); - IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class); - - mBackupManagerService.requestBackup(mUserId, packages, observer, monitor, - /* flags */ 0); - - verify(mUserBackupManagerService).requestBackup(packages, observer, monitor, /* flags */ 0); - } - - /** - * Test verifying that {@link BackupManagerService#cancelBackups(int)} throws a - * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission. - */ - @Test - public void testCancelBackups_withoutPermission_throwsSecurityException() { - mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); - - expectThrows( - SecurityException.class, - () -> mBackupManagerService.cancelBackups(mUserId)); - } - - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testCancelBackups_callsCancelBackupsForUser() throws Exception { - mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); - - mBackupManagerService.cancelBackups(mUserId); - - verify(mUserBackupManagerService).cancelBackups(); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testBeginFullBackup_callsBeginFullBackupForUser() throws Exception { - FullBackupJob job = new FullBackupJob(); - - mBackupManagerService.beginFullBackup(job); - - verify(mUserBackupManagerService).beginFullBackup(job); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testEndFullBackup_callsEndFullBackupForUser() throws Exception { - mBackupManagerService.endFullBackup(); - - verify(mUserBackupManagerService).endFullBackup(); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testFullTransportBackup_callsFullTransportBackupForUser() throws Exception { - String[] packages = {TEST_PACKAGE}; - - mBackupManagerService.fullTransportBackup(packages); - - verify(mUserBackupManagerService).fullTransportBackup(packages); - } - - // --------------------------------------------- - // Restore tests - // --------------------------------------------- - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testRestoreAtInstall_callsRestoreAtInstallForUser() throws Exception { - mBackupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0); - - verify(mUserBackupManagerService).restoreAtInstall(TEST_PACKAGE, /* token */ 0); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testBeginRestoreSession_callsBeginRestoreSessionForUser() throws Exception { - mBackupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT); - - verify(mUserBackupManagerService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testGetAvailableRestoreToken_callsGetAvailableRestoreTokenForUser() - throws Exception { - mBackupManagerService.getAvailableRestoreToken(TEST_PACKAGE); - - verify(mUserBackupManagerService).getAvailableRestoreToken(TEST_PACKAGE); - } - - // --------------------------------------------- - // Adb backup/restore tests - // --------------------------------------------- - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testSetBackupPassword_callsSetBackupPasswordForUser() throws Exception { - mBackupManagerService.setBackupPassword("currentPassword", "newPassword"); - - verify(mUserBackupManagerService).setBackupPassword("currentPassword", "newPassword"); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testHasBackupPassword_callsHasBackupPasswordForUser() throws Exception { - mBackupManagerService.hasBackupPassword(); - - verify(mUserBackupManagerService).hasBackupPassword(); - } - - /** - * Test verifying that {@link BackupManagerService#adbBackup(ParcelFileDescriptor, int, boolean, - * boolean, boolean, boolean, boolean, boolean, boolean, boolean, String[])} throws a - * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission. - */ - @Test - public void testAdbBackup_withoutPermission_throwsSecurityException() { - mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); - - expectThrows(SecurityException.class, - () -> - mBackupManagerService.adbBackup( - /* userId */ mUserId, - /* parcelFileDescriptor*/ null, - /* includeApks */ true, - /* includeObbs */ true, - /* includeShared */ true, - /* doWidgets */ true, - /* doAllApps */ true, - /* includeSystem */ true, - /* doCompress */ true, - /* doKeyValue */ true, - null)); - - } - - /** - * Test verifying that {@link BackupManagerService#adbBackup(ParcelFileDescriptor, int, boolean, - * boolean, boolean, boolean, boolean, boolean, boolean, boolean, String[])} does not require - * the caller to have INTERACT_ACROSS_USERS_FULL permission when the calling user id is the - * same as the target user id. - */ - @Test - public void testAdbBackup_whenCallingUserIsTargetUser_doesntNeedPermission() throws Exception { - ShadowBinder.setCallingUserHandle(UserHandle.of(mUserId)); - mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); - - ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest(); - - mBackupManagerService.adbBackup( - /* userId */ mUserId, - parcelFileDescriptor, - /* includeApks */ true, - /* includeObbs */ true, - /* includeShared */ true, - /* doWidgets */ true, - /* doAllApps */ true, - /* includeSystem */ true, - /* doCompress */ true, - /* doKeyValue */ true, - ADB_TEST_PACKAGES); - - verify(mUserBackupManagerService) - .adbBackup( - parcelFileDescriptor, - /* includeApks */ true, - /* includeObbs */ true, - /* includeShared */ true, - /* doWidgets */ true, - /* doAllApps */ true, - /* includeSystem */ true, - /* doCompress */ true, - /* doKeyValue */ true, - ADB_TEST_PACKAGES); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testAdbBackup_callsAdbBackupForUser() throws Exception { - mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); - - ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest(); - - mBackupManagerService.adbBackup( - /* userId */ mUserId, - parcelFileDescriptor, - /* includeApks */ true, - /* includeObbs */ true, - /* includeShared */ true, - /* doWidgets */ true, - /* doAllApps */ true, - /* includeSystem */ true, - /* doCompress */ true, - /* doKeyValue */ true, - ADB_TEST_PACKAGES); - - verify(mUserBackupManagerService) - .adbBackup( - parcelFileDescriptor, - /* includeApks */ true, - /* includeObbs */ true, - /* includeShared */ true, - /* doWidgets */ true, - /* doAllApps */ true, - /* includeSystem */ true, - /* doCompress */ true, - /* doKeyValue */ true, - ADB_TEST_PACKAGES); - } - - /** - * Test verifying that {@link BackupManagerService#adbRestore(ParcelFileDescriptor, int)} throws - * a {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL - * permission. - */ - @Test - public void testAdbRestore_withoutPermission_throwsSecurityException() { - mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); - - expectThrows(SecurityException.class, - () -> mBackupManagerService.adbRestore(mUserId, null)); - - } - - /** - * Test verifying that {@link BackupManagerService#adbRestore(ParcelFileDescriptor, int)} does - * not require the caller to have INTERACT_ACROSS_USERS_FULL permission when the calling user id - * is the same as the target user id. - */ - @Test - public void testAdbRestore_whenCallingUserIsTargetUser_doesntNeedPermission() throws Exception { - ShadowBinder.setCallingUserHandle(UserHandle.of(mUserId)); - mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); - - ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest(); - - mBackupManagerService.adbRestore(mUserId, parcelFileDescriptor); - - verify(mUserBackupManagerService).adbRestore(parcelFileDescriptor); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testAdbRestore_callsAdbRestoreForUser() throws Exception { - mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); - - ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest(); - - mBackupManagerService.adbRestore(mUserId, parcelFileDescriptor); - - verify(mUserBackupManagerService).adbRestore(parcelFileDescriptor); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testAcknowledgeAdbBackupOrRestore_callsAcknowledgeAdbBackupOrRestoreForUser() - throws Exception { - IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class); - - mBackupManagerService.acknowledgeAdbBackupOrRestore( - /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer); - - verify(mUserBackupManagerService) - .acknowledgeAdbBackupOrRestore( - /* token */ 0, - /* allow */ true, - "currentPassword", - "encryptionPassword", - observer); - } - - // --------------------------------------------- - // Service tests - // --------------------------------------------- - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testDump_callsDumpForUser() throws Exception { - File testFile = new File(mContext.getFilesDir(), "test"); - testFile.createNewFile(); - FileDescriptor fileDescriptor = new FileDescriptor(); - PrintWriter printWriter = new PrintWriter(testFile); - String[] args = {"1", "2"}; - - mBackupManagerService.dump(fileDescriptor, printWriter, args); - - verify(mUserBackupManagerService).dump(fileDescriptor, printWriter, args); - } - - private ParcelFileDescriptor getFileDescriptorForAdbTest() throws Exception { - File testFile = new File(mContext.getFilesDir(), "test"); - testFile.createNewFile(); - return ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE); - } -} diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java index 3979a8e762d3..148faada6381 100644 --- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java @@ -25,7 +25,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; @@ -42,7 +41,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; @@ -66,7 +64,6 @@ import android.provider.Settings; import android.util.Log; import android.util.SparseArray; -import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -96,6 +93,8 @@ public class AlarmManagerServiceTest { @Mock private ContentResolver mMockResolver; @Mock + private Context mMockContext; + @Mock private IActivityManager mIActivityManager; @Mock private UsageStatsManagerInternal mUsageStatsManagerInternal; @@ -221,17 +220,16 @@ public class AlarmManagerServiceTest { .thenReturn(STANDBY_BUCKET_ACTIVE); doReturn(Looper.getMainLooper()).when(Looper::myLooper); - final Context context = spy(InstrumentationRegistry.getTargetContext()); - when(context.getContentResolver()).thenReturn(mMockResolver); - doNothing().when(mMockResolver).registerContentObserver(any(), anyBoolean(), any()); + when(mMockContext.getContentResolver()).thenReturn(mMockResolver); doReturn("min_futurity=0").when(() -> Settings.Global.getString(mMockResolver, Settings.Global.ALARM_MANAGER_CONSTANTS)); - mInjector = new Injector(context); - mService = new AlarmManagerService(context, mInjector); + mInjector = new Injector(mMockContext); + mService = new AlarmManagerService(mMockContext, mInjector); spyOn(mService); doNothing().when(mService).publishBinderService(any(), any()); mService.onStart(); mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); + spyOn(mService.mHandler); assertEquals(0, mService.mConstants.MIN_FUTURITY); assertEquals(mService.mSystemUiUid, SYSTEM_UI_UID); @@ -273,7 +271,7 @@ public class AlarmManagerServiceTest { final ArgumentCaptor<PendingIntent.OnFinished> onFinishedCaptor = ArgumentCaptor.forClass(PendingIntent.OnFinished.class); - verify(alarmPi).send(any(Context.class), eq(0), any(Intent.class), + verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class), onFinishedCaptor.capture(), any(Handler.class), isNull(), any()); verify(mWakeLock).acquire(); onFinishedCaptor.getValue().onSendFinished(alarmPi, null, 0, null, null); @@ -423,11 +421,23 @@ public class AlarmManagerServiceTest { assertNotNull(restrictedAlarms.get(TEST_CALLING_UID)); listenerArgumentCaptor.getValue().unblockAlarmsForUid(TEST_CALLING_UID); - verify(alarmPi).send(any(Context.class), eq(0), any(Intent.class), any(), + verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class), any(), any(Handler.class), isNull(), any()); assertNull(restrictedAlarms.get(TEST_CALLING_UID)); } + @Test + public void sendsTimeTickOnInteractive() { + final ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class); + // Stubbing so the handler doesn't actually run the runnable. + doReturn(true).when(mService.mHandler).post(runnableCaptor.capture()); + // change interactive state: false -> true + mService.interactiveStateChangedLocked(false); + mService.interactiveStateChangedLocked(true); + runnableCaptor.getValue().run(); + verify(mMockContext).sendBroadcastAsUser(mService.mTimeTickIntent, UserHandle.ALL); + } + @After public void tearDown() { if (mMockingSession != null) { diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index cf4d3a8070f9..1b5ba263e6dd 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -25,7 +25,6 @@ <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.BROADCAST_STICKY" /> - <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" /> <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> diff --git a/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java b/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java index d965f8a34fa4..0fd59216fa21 100644 --- a/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java +++ b/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java @@ -41,11 +41,14 @@ import java.util.Map; /** * Tests for {@link SettingsToPropertiesMapper} + * + * Build/Install/Run: + * atest FrameworksServicesTests:SettingsToPropertiesMapperTest */ @RunWith(AndroidJUnit4.class) @SmallTest public class SettingsToPropertiesMapperTest { - private static final String NAME_VALID_CHARACTERS_REGEX = "^[\\w\\.\\-@:]*$"; + private static final String NAME_VALID_CHARACTERS_REGEX = "^[\\w\\-@:]*$"; private static final String[] TEST_MAPPING = new String[] { Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS }; @@ -77,7 +80,28 @@ public class SettingsToPropertiesMapperTest { } if (!globalSetting.matches(NAME_VALID_CHARACTERS_REGEX)) { Assert.fail(globalSetting + " contains invalid characters. " - + "Only alphanumeric characters, '.', '-', '@', ':' and '_' are valid."); + + "Only alphanumeric characters, '-', '@', ':' and '_' are valid."); + } + } + } + + @Test + public void validateRegisteredDeviceConfigScopes() { + HashSet<String> hashSet = new HashSet<>(); + for (String deviceConfigScope : SettingsToPropertiesMapper.sDeviceConfigScopes) { + if (hashSet.contains(deviceConfigScope)) { + Assert.fail("deviceConfigScope " + + deviceConfigScope + + " is registered more than once in " + + "SettingsToPropertiesMapper.sDeviceConfigScopes."); + } + hashSet.add(deviceConfigScope); + if (TextUtils.isEmpty(deviceConfigScope)) { + Assert.fail("empty deviceConfigScope registered."); + } + if (!deviceConfigScope.matches(NAME_VALID_CHARACTERS_REGEX)) { + Assert.fail(deviceConfigScope + " contains invalid characters. " + + "Only alphanumeric characters, '-', '@', ':' and '_' are valid."); } } } @@ -98,8 +122,7 @@ public class SettingsToPropertiesMapperTest { Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue2"); mTestMapper.updatePropertyFromSetting( Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, - systemPropertyName, - true); + systemPropertyName); propValue = mTestMapper.systemPropertiesGet(systemPropertyName); Assert.assertEquals("testValue2", propValue); @@ -107,8 +130,7 @@ public class SettingsToPropertiesMapperTest { Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, null); mTestMapper.updatePropertyFromSetting( Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, - systemPropertyName, - true); + systemPropertyName); propValue = mTestMapper.systemPropertiesGet(systemPropertyName); Assert.assertEquals("", propValue); } diff --git a/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java b/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java new file mode 100644 index 000000000000..52f434db3be3 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.appops; + +import android.Manifest; +import android.app.AppOpsManager; +import android.app.AppOpsManager.OnOpNotedListener; +import android.content.Context; +import android.os.Process; +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; +import androidx.test.filters.SmallTest; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InOrder; + + +import static org.junit.Assert.fail; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Tests watching noted ops. + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class AppOpsNotedWatcherTest { + + private static final long NOTIFICATION_TIMEOUT_MILLIS = 5000; + + public void testWatchNotedOpsRequiresPermission() { + // Create a mock listener + final OnOpNotedListener listener = mock(OnOpNotedListener.class); + + // Try to start watching noted ops + final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class); + try { + appOpsManager.startWatchingNoted(new String[]{AppOpsManager.OPSTR_FINE_LOCATION, + AppOpsManager.OPSTR_RECORD_AUDIO}, listener); + fail("Watching noted ops shoudl require " + Manifest.permission.WATCH_APPOPS); + } catch (SecurityException expected) { + /*ignored*/ + } + } + + @Test + public void testWatchNotedOps() { + // Create a mock listener + final OnOpNotedListener listener = mock(OnOpNotedListener.class); + + // Start watching noted ops + final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class); + appOpsManager.startWatchingNoted(new String[]{AppOpsManager.OPSTR_FINE_LOCATION, + AppOpsManager.OPSTR_CAMERA}, listener); + + // Note some ops + appOpsManager.noteOp(AppOpsManager.OPSTR_FINE_LOCATION, Process.myUid(), + getContext().getPackageName()); + appOpsManager.noteOp(AppOpsManager.OPSTR_CAMERA, Process.myUid(), + getContext().getPackageName()); + + // Verify that we got called for the ops being noted + final InOrder inOrder = inOrder(listener); + inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) + .times(1)).onOpNoted(eq(AppOpsManager.OPSTR_FINE_LOCATION), + eq(Process.myUid()), eq(getContext().getPackageName()), + eq(AppOpsManager.MODE_ALLOWED)); + inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) + .times(1)).onOpNoted(eq(AppOpsManager.OPSTR_CAMERA), + eq(Process.myUid()), eq(getContext().getPackageName()), + eq(AppOpsManager.MODE_ALLOWED)); + + // Stop watching + appOpsManager.stopWatchingNoted(listener); + + // This should be the only two callbacks we got + verifyNoMoreInteractions(listener); + } + + private static Context getContext() { + return InstrumentationRegistry.getContext(); + } +}
\ No newline at end of file diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java index d7a398e50a66..ff31435162b1 100644 --- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java +++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java @@ -132,19 +132,21 @@ public class TrampolineTest { } @Test - public void startServiceForUser_whenMultiUserSettingDisabled_isIgnored() { + public void unlockUser_whenMultiUserSettingDisabled_isIgnoredForNonSystemUser() { Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0); + mTrampoline.initializeService(UserHandle.USER_SYSTEM); - mTrampoline.startServiceForUser(10); + mTrampoline.unlockUser(10); verify(mBackupManagerServiceMock, never()).startServiceForUser(10); } @Test - public void startServiceForUser_whenMultiUserSettingEnabled_callsBackupManagerService() { + public void unlockUser_whenMultiUserSettingEnabled_callsBackupManagerServiceForNonSystemUser() { Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1); + mTrampoline.initializeService(UserHandle.USER_SYSTEM); - mTrampoline.startServiceForUser(10); + mTrampoline.unlockUser(10); verify(mBackupManagerServiceMock).startServiceForUser(10); } @@ -989,6 +991,11 @@ public class TrampolineTest { return sBackupManagerServiceMock; } + @Override + protected void postToHandler(Runnable runnable) { + runnable.run(); + } + int getCreateServiceCallsCount() { return mCreateServiceCallsCount; } diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java index 7755e94369af..5df4509af885 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java @@ -17,6 +17,7 @@ package com.android.server.pm.dex; import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo; +import static com.android.server.pm.dex.PackageDexUsage.MAX_SECONDARY_FILES_PER_OWNER; import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo; import static org.junit.Assert.assertEquals; @@ -31,24 +32,28 @@ import android.os.Build; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import dalvik.system.VMRuntime; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import dalvik.system.VMRuntime; - import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @RunWith(AndroidJUnit4.class) @SmallTest public class PackageDexUsageTests { + private static final String ISA = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]); + private PackageDexUsage mPackageDexUsage; private TestData mFooBaseUser0; @@ -71,25 +76,23 @@ public class PackageDexUsageTests { String fooCodeDir = "/data/app/com.google.foo/"; String fooDataDir = "/data/user/0/com.google.foo/"; - String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]); - mFooBaseUser0 = new TestData(fooPackageName, - fooCodeDir + "base.apk", 0, isa, false, true, fooPackageName); + fooCodeDir + "base.apk", 0, ISA, false, true, fooPackageName); mFooSplit1User0 = new TestData(fooPackageName, - fooCodeDir + "split-1.apk", 0, isa, false, true, fooPackageName); + fooCodeDir + "split-1.apk", 0, ISA, false, true, fooPackageName); mFooSplit2UsedByOtherApps0 = new TestData(fooPackageName, - fooCodeDir + "split-2.apk", 0, isa, true, true, "used.by.other.com"); + fooCodeDir + "split-2.apk", 0, ISA, true, true, "used.by.other.com"); mFooSecondary1User0 = new TestData(fooPackageName, - fooDataDir + "sec-1.dex", 0, isa, false, false, fooPackageName); + fooDataDir + "sec-1.dex", 0, ISA, false, false, fooPackageName); mFooSecondary1User1 = new TestData(fooPackageName, - fooDataDir + "sec-1.dex", 1, isa, false, false, fooPackageName); + fooDataDir + "sec-1.dex", 1, ISA, false, false, fooPackageName); mFooSecondary2UsedByOtherApps0 = new TestData(fooPackageName, - fooDataDir + "sec-2.dex", 0, isa, true, false, "used.by.other.com"); + fooDataDir + "sec-2.dex", 0, ISA, true, false, "used.by.other.com"); mInvalidIsa = new TestData(fooPackageName, fooCodeDir + "base.apk", 0, "INVALID_ISA", false, true, "INALID_USER"); @@ -100,11 +103,11 @@ public class PackageDexUsageTests { String barDataDir1 = "/data/user/1/com.google.bar/"; mBarBaseUser0 = new TestData(barPackageName, - barCodeDir + "base.apk", 0, isa, false, true, barPackageName); + barCodeDir + "base.apk", 0, ISA, false, true, barPackageName); mBarSecondary1User0 = new TestData(barPackageName, - barDataDir + "sec-1.dex", 0, isa, false, false, barPackageName); + barDataDir + "sec-1.dex", 0, ISA, false, false, barPackageName); mBarSecondary2User1 = new TestData(barPackageName, - barDataDir1 + "sec-2.dex", 1, isa, false, false, barPackageName); + barDataDir1 + "sec-2.dex", 1, ISA, false, false, barPackageName); } @Test @@ -183,6 +186,25 @@ public class PackageDexUsageTests { } @Test + public void testRecordTooManySecondaries() { + int tooManyFiles = MAX_SECONDARY_FILES_PER_OWNER + 1; + List<TestData> expectedSecondaries = new ArrayList<>(); + for (int i = 1; i <= tooManyFiles; i++) { + String fooPackageName = "com.google.foo"; + TestData testData = new TestData(fooPackageName, + "/data/user/0/" + fooPackageName + "/sec-" + i + "1.dex", 0, ISA, false, false, + fooPackageName); + if (i < tooManyFiles) { + assertTrue("Adding " + testData.mDexFile, record(testData)); + expectedSecondaries.add(testData); + } else { + assertFalse("Adding " + testData.mDexFile, record(testData)); + } + assertPackageDexUsage(mPackageDexUsage, null, null, expectedSecondaries); + } + } + + @Test public void testMultiplePackages() { assertTrue(record(mFooBaseUser0)); assertTrue(record(mFooSecondary1User0)); @@ -540,7 +562,14 @@ public class PackageDexUsageTests { private void assertPackageDexUsage(PackageDexUsage packageDexUsage, Set<String> users, TestData primary, TestData... secondaries) { - String packageName = primary == null ? secondaries[0].mPackageName : primary.mPackageName; + assertPackageDexUsage(packageDexUsage, users, primary, Arrays.asList(secondaries)); + } + + private void assertPackageDexUsage(PackageDexUsage packageDexUsage, Set<String> users, + TestData primary, List<TestData> secondaries) { + String packageName = primary == null + ? secondaries.get(0).mPackageName + : primary.mPackageName; boolean primaryUsedByOtherApps = primary != null && primary.mUsedByOtherApps; PackageUseInfo pInfo = packageDexUsage.getPackageUseInfo(packageName); @@ -554,7 +583,7 @@ public class PackageDexUsageTests { } Map<String, DexUseInfo> dexUseInfoMap = pInfo.getDexUseInfoMap(); - assertEquals(secondaries.length, dexUseInfoMap.size()); + assertEquals(secondaries.size(), dexUseInfoMap.size()); // Check dex use info for (TestData testData : secondaries) { 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 e9889948c341..bf4b52eb72aa 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java @@ -52,7 +52,6 @@ import android.platform.test.annotations.Presubmit; import android.provider.Settings; import android.view.Surface; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import com.android.internal.util.test.FakeSettingsProvider; @@ -81,7 +80,6 @@ import java.util.concurrent.TimeUnit; */ @SmallTest @Presubmit -@FlakyTest(detail = "Confirm stable in post-submit before removing") public class DisplayRotationTests { private static final long UI_HANDLER_WAIT_TIMEOUT_MS = 50; diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java index f3a125bf79e4..86353643c128 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java @@ -40,7 +40,6 @@ import android.platform.test.annotations.Presubmit; import android.view.DisplayInfo; import androidx.test.InstrumentationRegistry; -import androidx.test.filters.FlakyTest; import androidx.test.filters.MediumTest; import com.android.server.LocalServices; @@ -65,7 +64,6 @@ import java.util.function.Predicate; */ @MediumTest @Presubmit -@FlakyTest(detail = "Confirm stable in post-submit before removing") public class LaunchParamsPersisterTests extends ActivityTestsBase { private static final int TEST_USER_ID = 3; private static final int ALTERNATIVE_USER_ID = 0; diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java index 0bd681bd69a2..7186e22cefef 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java @@ -47,7 +47,6 @@ import android.platform.test.annotations.Presubmit; import android.view.Display; import android.view.Gravity; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import com.android.server.wm.LaunchParamsController.LaunchParams; @@ -64,7 +63,6 @@ import java.util.Locale; * atest WmTests:TaskLaunchParamsModifierTests */ @SmallTest -@FlakyTest(detail = "Confirm stable in post-submit before removing") @Presubmit public class TaskLaunchParamsModifierTests extends ActivityTestsBase { diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java index ec475bf26bb3..eddf8f9b7254 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java +++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java @@ -98,14 +98,14 @@ final class UsageStatsXmlV1 { stats.mLastTimeVisible = statsOut.beginTime + XmlUtils.readLongAttribute( parser, LAST_TIME_VISIBLE_ATTR); } catch (IOException e) { - Log.e(TAG, "Failed to parse mLastTimeVisible", e); + Log.i(TAG, "Failed to parse mLastTimeVisible"); } try { stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + XmlUtils.readLongAttribute( parser, LAST_TIME_SERVICE_USED_ATTR); } catch (IOException e) { - Log.e(TAG, "Failed to parse mLastTimeForegroundServiceUsed", e); + Log.i(TAG, "Failed to parse mLastTimeForegroundServiceUsed"); } stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR); @@ -113,14 +113,14 @@ final class UsageStatsXmlV1 { try { stats.mTotalTimeVisible = XmlUtils.readLongAttribute(parser, TOTAL_TIME_VISIBLE_ATTR); } catch (IOException e) { - Log.e(TAG, "Failed to parse mTotalTimeVisible", e); + Log.i(TAG, "Failed to parse mTotalTimeVisible"); } try { stats.mTotalTimeForegroundServiceUsed = XmlUtils.readLongAttribute(parser, TOTAL_TIME_SERVICE_USED_ATTR); } catch (IOException e) { - Log.e(TAG, "Failed to parse mTotalTimeForegroundServiceUsed", e); + Log.i(TAG, "Failed to parse mTotalTimeForegroundServiceUsed"); } stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 99ad1f4d6b50..bbf3d45d7c99 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -356,7 +356,12 @@ public class VoiceInteractionManagerService extends SystemService { } // No voice interactor, we'll just set up a simple recognizer. - curRecognizer = findAvailRecognizer(null, userHandle); + initSimpleRecognizer(curInteractorInfo, userHandle); + } + + public void initSimpleRecognizer(VoiceInteractionServiceInfo curInteractorInfo, + int userHandle) { + ComponentName curRecognizer = findAvailRecognizer(null, userHandle); if (curRecognizer != null) { if (curInteractorInfo == null) { setCurInteractor(null, userHandle); @@ -1236,34 +1241,46 @@ public class VoiceInteractionManagerService extends SystemService { int userHandle = UserHandle.getUserId(uid); ComponentName curInteractor = getCurInteractor(userHandle); ComponentName curRecognizer = getCurRecognizer(userHandle); - boolean hit = false; + boolean hitInt = false; + boolean hitRec = false; for (String pkg : packages) { if (curInteractor != null && pkg.equals(curInteractor.getPackageName())) { - hit = true; + hitInt = true; break; } else if (curRecognizer != null && pkg.equals(curRecognizer.getPackageName())) { - hit = true; + hitRec = true; break; } } - if (hit && doit) { - // The user is force stopping our current interactor/recognizer. + if (hitInt && doit) { + // The user is force stopping our current interactor. // Clear the current settings and restore default state. synchronized (VoiceInteractionManagerServiceStub.this) { + Slog.i(TAG, "Force stopping current voice interactor: " + + getCurInteractor(userHandle)); unloadAllKeyphraseModels(); if (mImpl != null) { mImpl.shutdownLocked(); setImplLocked(null); } + setCurInteractor(null, userHandle); setCurRecognizer(null, userHandle); resetCurAssistant(userHandle); initForUser(userHandle); switchImplementationIfNeededLocked(true); } + } else if (hitRec && doit) { + // We are just force-stopping the current recognizer, which is not + // also the current interactor. + synchronized (VoiceInteractionManagerServiceStub.this) { + Slog.i(TAG, "Force stopping current voice recognizer: " + + getCurRecognizer(userHandle)); + initSimpleRecognizer(null, userHandle); + } } - return hit; + return hitInt || hitRec; } @Override diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc index 94879d0bbcc4..a78f7d53d135 100644 --- a/startop/view_compiler/dex_builder.cc +++ b/startop/view_compiler/dex_builder.cc @@ -79,6 +79,9 @@ std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode) { case Instruction::Op::kNew: out << "kNew"; return out; + case Instruction::Op::kCheckCast: + out << "kCheckCast"; + return out; } } @@ -329,6 +332,8 @@ void MethodBuilder::EncodeInstruction(const Instruction& instruction) { return EncodeBranch(art::Instruction::IF_NEZ, instruction); case Instruction::Op::kNew: return EncodeNew(instruction); + case Instruction::Op::kCheckCast: + return EncodeCast(instruction); } } @@ -422,6 +427,18 @@ void MethodBuilder::EncodeNew(const Instruction& instruction) { Encode21c(::art::Instruction::NEW_INSTANCE, RegisterValue(*instruction.dest()), type.value()); } +void MethodBuilder::EncodeCast(const Instruction& instruction) { + DCHECK_EQ(Instruction::Op::kCheckCast, instruction.opcode()); + DCHECK(instruction.dest().has_value()); + DCHECK(instruction.dest()->is_variable()); + DCHECK_EQ(1, instruction.args().size()); + + const Value& type = instruction.args()[0]; + DCHECK_LT(RegisterValue(*instruction.dest()), 256); + DCHECK(type.is_type()); + Encode21c(::art::Instruction::CHECK_CAST, RegisterValue(*instruction.dest()), type.value()); +} + size_t MethodBuilder::RegisterValue(const Value& value) const { if (value.is_register()) { return value.value(); diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h index 45596acfdc44..06059c8c6e56 100644 --- a/startop/view_compiler/dex_builder.h +++ b/startop/view_compiler/dex_builder.h @@ -142,17 +142,18 @@ class Instruction { // The operation performed by this instruction. These are virtual instructions that do not // correspond exactly to DEX instructions. enum class Op { - kReturn, - kReturnObject, - kMove, - kInvokeVirtual, - kInvokeDirect, - kInvokeStatic, - kInvokeInterface, kBindLabel, kBranchEqz, kBranchNEqz, - kNew + kCheckCast, + kInvokeDirect, + kInvokeInterface, + kInvokeStatic, + kInvokeVirtual, + kMove, + kNew, + kReturn, + kReturnObject, }; //////////////////////// @@ -168,6 +169,13 @@ class Instruction { static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest, T... args) { return Instruction{opcode, /*method_id=*/0, /*result_is_object=*/false, dest, args...}; } + + // A cast instruction. Basically, `(type)val` + static inline Instruction Cast(Value val, Value type) { + DCHECK(type.is_type()); + return OpWithArgs(Op::kCheckCast, val, type); + } + // For method calls. template <typename... T> static inline Instruction InvokeVirtual(size_t method_id, std::optional<const Value> dest, @@ -302,6 +310,7 @@ class MethodBuilder { void EncodeInvoke(const Instruction& instruction, ::art::Instruction::Code opcode); void EncodeBranch(art::Instruction::Code op, const Instruction& instruction); void EncodeNew(const Instruction& instruction); + void EncodeCast(const Instruction& instruction); // Low-level instruction format encoding. See // https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java index 1508ad9ee56b..42d4161ee81e 100644 --- a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java +++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java @@ -20,6 +20,7 @@ import com.google.common.io.ByteStreams; import dalvik.system.InMemoryDexClassLoader; import dalvik.system.PathClassLoader; import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.ByteBuffer; import org.junit.Assert; @@ -151,4 +152,23 @@ public class DexBuilderTest { Method method = clazz.getMethod("invokeVirtualReturnObject", String.class, int.class); Assert.assertEquals("bc", method.invoke(null, "abc", 1)); } + + @Test + public void castObjectToString() throws Exception { + ClassLoader loader = loadDexFile("simple.dex"); + Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); + Method method = clazz.getMethod("castObjectToString", Object.class); + Assert.assertEquals("abc", method.invoke(null, "abc")); + boolean castFailed = false; + try { + method.invoke(null, 5); + } catch (InvocationTargetException e) { + if (e.getCause() instanceof ClassCastException) { + castFailed = true; + } else { + throw e; + } + } + Assert.assertTrue(castFailed); + } } diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc index 2781aa55d1df..f62ec5dde85e 100644 --- a/startop/view_compiler/dex_testcase_generator.cc +++ b/startop/view_compiler/dex_testcase_generator.cc @@ -269,6 +269,19 @@ void GenerateSimpleTestCases(const string& outdir) { method.Encode(); }(invokeVirtualReturnObject); + // Make sure we can cast objects + // String castObjectToString(Object o) { return (String)o; } + MethodBuilder castObjectToString{cbuilder.CreateMethod( + "castObjectToString", + Prototype{string_type, TypeDescriptor::FromClassname("java.lang.Object")})}; + [&](MethodBuilder& method) { + const ir::Type* type_def = dex_file.GetOrAddType(string_type.descriptor()); + method.AddInstruction( + Instruction::Cast(Value::Parameter(0), Value::Type(type_def->orig_index))); + method.BuildReturn(Value::Parameter(0), /*is_object=*/true); + method.Encode(); + }(castObjectToString); + slicer::MemView image{dex_file.CreateImage()}; std::ofstream out_file(outdir + "/simple.dex"); out_file.write(image.ptr<const char>(), image.size()); diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java index c6887ab93109..f53cb8224706 100644 --- a/telephony/java/android/telephony/DisconnectCause.java +++ b/telephony/java/android/telephony/DisconnectCause.java @@ -16,12 +16,16 @@ package android.telephony; +import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; /** - * Contains disconnect call causes generated by the framework and the RIL. + * Describes the cause of a disconnected call. Those disconnect causes can be converted into a more + * generic {@link android.telecom.DisconnectCause} object. + * * @hide */ +@SystemApi public class DisconnectCause { /** The disconnect cause is not valid (Not received a disconnect cause) */ @@ -101,8 +105,8 @@ public class DisconnectCause { /** Unknown error or not specified */ public static final int ERROR_UNSPECIFIED = 36; /** - * Only emergency numbers are allowed, but we tried to dial - * a non-emergency number. + * Only emergency numbers are allowed, but we tried to dial a non-emergency number. + * @hide */ // TODO: This should be the same as NOT_EMERGENCY public static final int EMERGENCY_ONLY = 37; @@ -115,8 +119,7 @@ public class DisconnectCause { */ public static final int DIALED_MMI = 39; /** - * We tried to call a voicemail: URI but the device has no - * voicemail number configured. + * We tried to call a voicemail: URI but the device has no voicemail number configured. */ public static final int VOICEMAIL_NUMBER_MISSING = 40; /** @@ -129,6 +132,8 @@ public class DisconnectCause { * needs to be triggered by a *disconnect* event, rather than when * the InCallScreen first comes to the foreground. For now we use * the needToShowCallLostDialog field for this (see below.) + * + * @hide */ public static final int CDMA_CALL_LOST = 41; /** @@ -169,62 +174,52 @@ public class DisconnectCause { /** * Stk Call Control modified DIAL request to USSD request. - * {@hide} */ public static final int DIAL_MODIFIED_TO_USSD = 46; /** * Stk Call Control modified DIAL request to SS request. - * {@hide} */ public static final int DIAL_MODIFIED_TO_SS = 47; /** * Stk Call Control modified DIAL request to DIAL with modified data. - * {@hide} */ public static final int DIAL_MODIFIED_TO_DIAL = 48; /** * The call was terminated because CDMA phone service and roaming have already been activated. - * {@hide} */ public static final int CDMA_ALREADY_ACTIVATED = 49; /** * The call was terminated because it is not possible to place a video call while TTY is * enabled. - * {@hide} */ public static final int VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED = 50; /** * The call was terminated because it was pulled to another device. - * {@hide} */ public static final int CALL_PULLED = 51; /** * The call was terminated because it was answered on another device. - * {@hide} */ public static final int ANSWERED_ELSEWHERE = 52; /** * The call was terminated because the maximum allowable number of calls has been reached. - * {@hide} */ public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 53; /** * The call was terminated because cellular data has been disabled. * Used when in a video call and the user disables cellular data via the settings. - * {@hide} */ public static final int DATA_DISABLED = 54; /** * The call was terminated because the data policy has disabled cellular data. * Used when in a video call and the user has exceeded the device data limit. - * {@hide} */ public static final int DATA_LIMIT_REACHED = 55; @@ -237,7 +232,6 @@ public class DisconnectCause { /** * The network does not accept the emergency call request because IMEI was used as * identification and this cability is not supported by the network. - * {@hide} */ public static final int IMEI_NOT_ACCEPTED = 58; @@ -249,7 +243,6 @@ public class DisconnectCause { /** * The call has failed because of access class barring. - * {@hide} */ public static final int IMS_ACCESS_BLOCKED = 60; @@ -265,51 +258,43 @@ public class DisconnectCause { /** * Emergency call failed with a temporary fail cause and can be redialed on this slot. - * {@hide} */ public static final int EMERGENCY_TEMP_FAILURE = 63; /** * Emergency call failed with a permanent fail cause and should not be redialed on this - * slot. - * {@hide} + * slot. */ public static final int EMERGENCY_PERM_FAILURE = 64; /** * This cause is used to report a normal event only when no other cause in the normal class * applies. - * {@hide} */ public static final int NORMAL_UNSPECIFIED = 65; /** * Stk Call Control modified DIAL request to video DIAL request. - * {@hide} */ public static final int DIAL_MODIFIED_TO_DIAL_VIDEO = 66; /** * Stk Call Control modified Video DIAL request to SS request. - * {@hide} */ public static final int DIAL_VIDEO_MODIFIED_TO_SS = 67; /** * Stk Call Control modified Video DIAL request to USSD request. - * {@hide} */ public static final int DIAL_VIDEO_MODIFIED_TO_USSD = 68; /** * Stk Call Control modified Video DIAL request to DIAL request. - * {@hide} */ public static final int DIAL_VIDEO_MODIFIED_TO_DIAL = 69; /** * Stk Call Control modified Video DIAL request to Video DIAL request. - * {@hide} */ public static final int DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 70; @@ -359,7 +344,10 @@ public class DisconnectCause { // Do nothing. } - /** Returns descriptive string for the specified disconnect cause. */ + /** + * Returns descriptive string for the specified disconnect cause. + * @hide + */ @UnsupportedAppUsage public static String toString(int cause) { switch (cause) { diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java index 0df0dafbe1dd..9317aa73ffc2 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/telephony/java/android/telephony/PhoneStateListener.java @@ -173,14 +173,14 @@ public class PhoneStateListener { public static final int LISTEN_CELL_INFO = 0x00000400; /** - * Listen for precise changes and fails to the device calls (cellular). + * Listen for {@link PreciseCallState.State} of ringing, background and foreground calls. * {@more} * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE * READ_PRECISE_PHONE_STATE} * * @hide */ - @UnsupportedAppUsage + @SystemApi public static final int LISTEN_PRECISE_CALL_STATE = 0x00000800; /** @@ -320,6 +320,18 @@ public class PhoneStateListener { */ public static final int LISTEN_EMERGENCY_NUMBER_LIST = 0x01000000; + /** + * Listen for call disconnect causes which contains {@link DisconnectCause} and + * {@link PreciseDisconnectCause}. + * {@more} + * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE + * READ_PRECISE_PHONE_STATE} + * + * @hide + */ + @SystemApi + public static final int LISTEN_CALL_DISCONNECT_CAUSES = 0x02000000; + /* * Subscription used to listen to the phone state changes * @hide @@ -530,15 +542,27 @@ public class PhoneStateListener { /** * Callback invoked when precise device call state changes. - * + * @param callState {@link PreciseCallState} * @hide */ - @UnsupportedAppUsage + @SystemApi public void onPreciseCallStateChanged(PreciseCallState callState) { // default implementation empty } /** + * Callback invoked when call disconnect cause changes. + * @param disconnectCause {@link DisconnectCause}. + * @param preciseDisconnectCause {@link PreciseDisconnectCause}. + * + * @hide + */ + @SystemApi + public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) { + // default implementation empty + } + + /** * Callback invoked when data connection state changes with precise information. * * @hide @@ -799,6 +823,15 @@ public class PhoneStateListener { () -> mExecutor.execute(() -> psl.onPreciseCallStateChanged(callState))); } + public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) { + PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); + if (psl == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> psl.onCallDisconnectCauseChanged( + disconnectCause, preciseDisconnectCause))); + } + public void onPreciseDataConnectionStateChanged( PreciseDataConnectionState dataConnectionState) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); diff --git a/telephony/java/android/telephony/PreciseCallState.java b/telephony/java/android/telephony/PreciseCallState.java index ed5c26ac5cf2..59f3e1f0e7f7 100644 --- a/telephony/java/android/telephony/PreciseCallState.java +++ b/telephony/java/android/telephony/PreciseCallState.java @@ -16,29 +16,51 @@ package android.telephony; +import android.annotation.IntDef; +import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.telephony.DisconnectCause; import android.telephony.PreciseDisconnectCause; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + /** - * Contains precise call state and call fail causes generated by the - * framework and the RIL. + * Contains precise call states. * * The following call information is included in returned PreciseCallState: * * <ul> - * <li>Ringing call state. - * <li>Foreground call state. - * <li>Background call state. - * <li>Disconnect cause; generated by the framework. - * <li>Precise disconnect cause; generated by the RIL. + * <li>Precise ringing call state. + * <li>Precise foreground call state. + * <li>Precise background call state. * </ul> * + * @see android.telephony.TelephonyManager.CallState which contains generic call states. + * * @hide */ -public class PreciseCallState implements Parcelable { +@SystemApi +public final class PreciseCallState implements Parcelable { + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"PRECISE_CALL_STATE_"}, + value = { + PRECISE_CALL_STATE_NOT_VALID, + PRECISE_CALL_STATE_IDLE, + PRECISE_CALL_STATE_ACTIVE, + PRECISE_CALL_STATE_HOLDING, + PRECISE_CALL_STATE_DIALING, + PRECISE_CALL_STATE_ALERTING, + PRECISE_CALL_STATE_INCOMING, + PRECISE_CALL_STATE_WAITING, + PRECISE_CALL_STATE_DISCONNECTED, + PRECISE_CALL_STATE_DISCONNECTING}) + public @interface State {} /** Call state is not valid (Not received a call state). */ public static final int PRECISE_CALL_STATE_NOT_VALID = -1; @@ -61,9 +83,9 @@ public class PreciseCallState implements Parcelable { /** Call state: Disconnecting. */ public static final int PRECISE_CALL_STATE_DISCONNECTING = 8; - private int mRingingCallState = PRECISE_CALL_STATE_NOT_VALID; - private int mForegroundCallState = PRECISE_CALL_STATE_NOT_VALID; - private int mBackgroundCallState = PRECISE_CALL_STATE_NOT_VALID; + private @State int mRingingCallState = PRECISE_CALL_STATE_NOT_VALID; + private @State int mForegroundCallState = PRECISE_CALL_STATE_NOT_VALID; + private @State int mBackgroundCallState = PRECISE_CALL_STATE_NOT_VALID; private int mDisconnectCause = DisconnectCause.NOT_VALID; private int mPreciseDisconnectCause = PreciseDisconnectCause.NOT_VALID; @@ -73,8 +95,9 @@ public class PreciseCallState implements Parcelable { * @hide */ @UnsupportedAppUsage - public PreciseCallState(int ringingCall, int foregroundCall, int backgroundCall, - int disconnectCause, int preciseDisconnectCause) { + public PreciseCallState(@State int ringingCall, @State int foregroundCall, + @State int backgroundCall, int disconnectCause, + int preciseDisconnectCause) { mRingingCallState = ringingCall; mForegroundCallState = foregroundCall; mBackgroundCallState = backgroundCall; @@ -92,6 +115,8 @@ public class PreciseCallState implements Parcelable { /** * Construct a PreciseCallState object from the given parcel. + * + * @hide */ private PreciseCallState(Parcel in) { mRingingCallState = in.readInt(); @@ -102,59 +127,23 @@ public class PreciseCallState implements Parcelable { } /** - * Get precise ringing call state - * - * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID - * @see PreciseCallState#PRECISE_CALL_STATE_IDLE - * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE - * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING - * @see PreciseCallState#PRECISE_CALL_STATE_DIALING - * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING - * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING - * @see PreciseCallState#PRECISE_CALL_STATE_WAITING - * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED - * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING + * Returns the precise ringing call state. */ - @UnsupportedAppUsage - public int getRingingCallState() { + public @State int getRingingCallState() { return mRingingCallState; } /** - * Get precise foreground call state - * - * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID - * @see PreciseCallState#PRECISE_CALL_STATE_IDLE - * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE - * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING - * @see PreciseCallState#PRECISE_CALL_STATE_DIALING - * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING - * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING - * @see PreciseCallState#PRECISE_CALL_STATE_WAITING - * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED - * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING + * Returns the precise foreground call state. */ - @UnsupportedAppUsage - public int getForegroundCallState() { + public @State int getForegroundCallState() { return mForegroundCallState; } /** - * Get precise background call state - * - * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID - * @see PreciseCallState#PRECISE_CALL_STATE_IDLE - * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE - * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING - * @see PreciseCallState#PRECISE_CALL_STATE_DIALING - * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING - * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING - * @see PreciseCallState#PRECISE_CALL_STATE_WAITING - * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED - * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING + * Returns the precise background call state. */ - @UnsupportedAppUsage - public int getBackgroundCallState() { + public @State int getBackgroundCallState() { return mBackgroundCallState; } @@ -199,6 +188,11 @@ public class PreciseCallState implements Parcelable { * @see DisconnectCause#CDMA_NOT_EMERGENCY * @see DisconnectCause#CDMA_ACCESS_BLOCKED * @see DisconnectCause#ERROR_UNSPECIFIED + * + * TODO: remove disconnect cause from preciseCallState as there is no link between random + * connection disconnect cause with foreground, background or ringing call. + * + * @hide */ @UnsupportedAppUsage public int getDisconnectCause() { @@ -238,6 +232,11 @@ public class PreciseCallState implements Parcelable { * @see PreciseDisconnectCause#CDMA_NOT_EMERGENCY * @see PreciseDisconnectCause#CDMA_ACCESS_BLOCKED * @see PreciseDisconnectCause#ERROR_UNSPECIFIED + * + * TODO: remove precise disconnect cause from preciseCallState as there is no link between + * random connection disconnect cause with foreground, background or ringing call. + * + * @hide */ @UnsupportedAppUsage public int getPreciseDisconnectCause() { @@ -272,14 +271,8 @@ public class PreciseCallState implements Parcelable { @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + mRingingCallState; - result = prime * result + mForegroundCallState; - result = prime * result + mBackgroundCallState; - result = prime * result + mDisconnectCause; - result = prime * result + mPreciseDisconnectCause; - return result; + return Objects.hash(mRingingCallState, mForegroundCallState, mForegroundCallState, + mDisconnectCause, mPreciseDisconnectCause); } @Override diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java index 2acaf34dbb30..af88748af9e6 100644 --- a/telephony/java/android/telephony/PreciseDisconnectCause.java +++ b/telephony/java/android/telephony/PreciseDisconnectCause.java @@ -16,279 +16,329 @@ package android.telephony; +import android.annotation.SystemApi; + /** - * Contains precise disconnect call causes generated by the - * framework and the RIL. - * + * Contains precise disconnect call causes generated by the framework and the RIL. * @hide */ +@SystemApi public class PreciseDisconnectCause { - /** The disconnect cause is not valid (Not received a disconnect cause)*/ + /** The disconnect cause is not valid (Not received a disconnect cause).*/ public static final int NOT_VALID = -1; - /** No disconnect cause provided. Generally a local disconnect or an incoming missed call */ + /** No disconnect cause provided. Generally a local disconnect or an incoming missed call. */ public static final int NO_DISCONNECT_CAUSE_AVAILABLE = 0; /** * The destination cannot be reached because the number, although valid, - * is not currently assigned + * is not currently assigned. */ public static final int UNOBTAINABLE_NUMBER = 1; - /** The user cannot be reached because the network through which the call has been - * routed does not serve the destination desired + /** + * The user cannot be reached because the network through which the call has been routed does + * not serve the destination desired. */ public static final int NO_ROUTE_TO_DESTINATION = 3; - /** The channel most recently identified is not acceptable to the sending entity for - * use in this call + /** + * The channel most recently identified is not acceptable to the sending entity for use in this + * call. */ public static final int CHANNEL_UNACCEPTABLE = 6; - /** The MS has tried to access a service that the MS's network operator or service - * provider is not prepared to allow + /** + * The mobile station (MS) has tried to access a service that the MS's network operator or + * service provider is not prepared to allow. */ public static final int OPERATOR_DETERMINED_BARRING = 8; - /** One of the users involved in the call has requested that the call is cleared */ + /** One of the users involved in the call has requested that the call is cleared. */ public static final int NORMAL = 16; - /** The called user is unable to accept another call */ + /** The called user is unable to accept another call. */ public static final int BUSY = 17; - /** The user does not respond to a call establishment message with either an alerting - * or connect indication within the prescribed period of time allocated + /** + * The user does not respond to a call establishment message with either an alerting or connect + * indication within the prescribed period of time allocated. */ public static final int NO_USER_RESPONDING = 18; - /** The user has provided an alerting indication but has not provided a connect - * indication within a prescribed period of time + /** + * The user has provided an alerting indication but has not provided a connect indication + * within a prescribed period of time. */ public static final int NO_ANSWER_FROM_USER = 19; - /** The equipment sending this cause does not wish to accept this call */ + /** The equipment sending this cause does not wish to accept this call. */ public static final int CALL_REJECTED = 21; - /** The called number is no longer assigned */ + /** The called number is no longer assigned. */ public static final int NUMBER_CHANGED = 22; - /** This cause is returned to the network when a mobile station clears an active - * call which is being pre-empted by another call with higher precedence + /** + * This cause is returned to the network when a mobile station clears an active call which is + * being pre-empted by another call with higher precedence. */ public static final int PREEMPTION = 25; - /** The destination indicated by the mobile station cannot be reached because - * the interface to the destination is not functioning correctly + /** + * The destination indicated by the mobile station cannot be reached because the interface to + * the destination is not functioning correctly. */ public static final int DESTINATION_OUT_OF_ORDER = 27; - /** The called party number is not a valid format or is not complete */ + /** The called party number is not a valid format or is not complete. */ public static final int INVALID_NUMBER_FORMAT = 28; - /** The facility requested by user can not be provided by the network */ + /** The facility requested by user can not be provided by the network. */ public static final int FACILITY_REJECTED = 29; - /** Provided in response to a STATUS ENQUIRY message */ + /** Provided in response to a STATUS ENQUIRY message. */ public static final int STATUS_ENQUIRY = 30; - /** Reports a normal disconnect only when no other normal cause applies */ + /** Reports a normal disconnect only when no other normal cause applies. */ public static final int NORMAL_UNSPECIFIED = 31; - /** There is no channel presently available to handle the call */ + /** There is no channel presently available to handle the call. */ public static final int NO_CIRCUIT_AVAIL = 34; - /** The network is not functioning correctly and that the condition is likely - * to last a relatively long period of time + /** + * The network is not functioning correctly and that the condition is likely to last a + * relatively long period of time. */ public static final int NETWORK_OUT_OF_ORDER = 38; /** - * The network is not functioning correctly and the condition is not likely to last - * a long period of time + * The network is not functioning correctly and the condition is not likely to last a long + * period of time. */ public static final int TEMPORARY_FAILURE = 41; - /** The switching equipment is experiencing a period of high traffic */ + /** The switching equipment is experiencing a period of high traffic. */ public static final int SWITCHING_CONGESTION = 42; - /** The network could not deliver access information to the remote user as requested */ + /** The network could not deliver access information to the remote user as requested. */ public static final int ACCESS_INFORMATION_DISCARDED = 43; - /** The channel cannot be provided */ + /** The channel cannot be provided. */ public static final int CHANNEL_NOT_AVAIL = 44; - /** This cause is used to report a resource unavailable event only when no other - * cause in the resource unavailable class applies + /** + * This cause is used to report a resource unavailable event only when no other cause in the + * resource unavailable class applies. */ public static final int RESOURCES_UNAVAILABLE_OR_UNSPECIFIED = 47; - /** The requested quality of service (ITU-T X.213) cannot be provided */ + /** The requested quality of service (ITU-T X.213) cannot be provided. */ public static final int QOS_NOT_AVAIL = 49; - /** The facility could not be provided by the network because the user has no - * complete subscription + /** + * The facility could not be provided by the network because the user has no complete + * subscription. */ public static final int REQUESTED_FACILITY_NOT_SUBSCRIBED = 50; - /** Incoming calls are not allowed within this CUG */ + /** Incoming calls are not allowed within this calling user group (CUG). */ public static final int INCOMING_CALLS_BARRED_WITHIN_CUG = 55; - /** The mobile station is not authorized to use bearer capability requested */ + /** The mobile station is not authorized to use bearer capability requested. */ public static final int BEARER_CAPABILITY_NOT_AUTHORIZED = 57; - /** The requested bearer capability is not available at this time */ + /** The requested bearer capability is not available at this time. */ public static final int BEARER_NOT_AVAIL = 58; - /** The service option is not availble at this time */ + /** The service option is not availble at this time. */ public static final int SERVICE_OPTION_NOT_AVAILABLE = 63; - /** The equipment sending this cause does not support the bearer capability requested */ + /** The equipment sending this cause does not support the bearer capability requested. */ public static final int BEARER_SERVICE_NOT_IMPLEMENTED = 65; - /** The call clearing is due to ACM being greater than or equal to ACMmax */ + /** The call clearing is due to ACM being greater than or equal to ACMmax. */ public static final int ACM_LIMIT_EXCEEDED = 68; - /** The equipment sending this cause does not support the requested facility */ + /** The equipment sending this cause does not support the requested facility. */ public static final int REQUESTED_FACILITY_NOT_IMPLEMENTED = 69; - /** The equipment sending this cause only supports the restricted version of - * the requested bearer capability + /** + * The equipment sending this cause only supports the restricted version of the requested bearer + * capability. */ public static final int ONLY_DIGITAL_INFORMATION_BEARER_AVAILABLE = 70; - /** The service requested is not implemented at network */ + /** The service requested is not implemented at network. */ public static final int SERVICE_OR_OPTION_NOT_IMPLEMENTED = 79; - /** The equipment sending this cause has received a message with a transaction identifier - * which is not currently in use on the MS-network interface + /** + * The equipment sending this cause has received a message with a transaction identifier + * which is not currently in use on the mobile station network interface. */ public static final int INVALID_TRANSACTION_IDENTIFIER = 81; - /** The called user for the incoming CUG call is not a member of the specified CUG */ + /** + * The called user for the incoming CUG call is not a member of the specified calling user + * group (CUG). + */ public static final int USER_NOT_MEMBER_OF_CUG = 87; - /** The equipment sending this cause has received a request which can't be accomodated */ + /** The equipment sending this cause has received a request which can't be accomodated. */ public static final int INCOMPATIBLE_DESTINATION = 88; - /** This cause is used to report receipt of a message with semantically incorrect contents */ + /** This cause is used to report receipt of a message with semantically incorrect contents. */ public static final int SEMANTICALLY_INCORRECT_MESSAGE = 95; - /** The equipment sending this cause has received a message with a non-semantical - * mandatory IE error + /** + * The equipment sending this cause has received a message with a non-semantical mandatory + * information element (IE) error. */ public static final int INVALID_MANDATORY_INFORMATION = 96; - /** This is sent in response to a message which is not defined, or defined but not - * implemented by the equipment sending this cause + /** + * This is sent in response to a message which is not defined, or defined but not implemented + * by the equipment sending this cause. */ public static final int MESSAGE_TYPE_NON_IMPLEMENTED = 97; - /** The equipment sending this cause has received a message not compatible with the - * protocol state + /** + * The equipment sending this cause has received a message not compatible with the protocol + * state. */ public static final int MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 98; - /** The equipment sending this cause has received a message which includes information - * elements not recognized because its identifier is not defined or it is defined but not - * implemented by the equipment sending the cause + /** + * The equipment sending this cause has received a message which includes information + * elements not recognized because its identifier is not defined or it is defined but not + * implemented by the equipment sending the cause. */ public static final int INFORMATION_ELEMENT_NON_EXISTENT = 99; - /** The equipment sending this cause has received a message with conditional IE errors */ + /** The equipment sending this cause has received a message with conditional IE errors. */ public static final int CONDITIONAL_IE_ERROR = 100; - /** The message has been received which is incompatible with the protocol state */ + /** The message has been received which is incompatible with the protocol state. */ public static final int MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 101; - /** The procedure has been initiated by the expiry of a timer in association with - * 3GPP TS 24.008 error handling procedures + /** + * The procedure has been initiated by the expiry of a timer in association with + * 3GPP TS 24.008 error handling procedures. */ public static final int RECOVERY_ON_TIMER_EXPIRED = 102; - /** This protocol error event is reported only when no other cause in the protocol - * error class applies + /** + * This protocol error event is reported only when no other cause in the protocol error class + * applies. */ public static final int PROTOCOL_ERROR_UNSPECIFIED = 111; - /** interworking with a network which does not provide causes for actions it takes - * thus, the precise cause for a message which is being sent cannot be ascertained + /** + * Interworking with a network which does not provide causes for actions it takes thus, the + * precise cause for a message which is being sent cannot be ascertained. */ public static final int INTERWORKING_UNSPECIFIED = 127; - /** The call is restricted */ + /** The call is restricted. */ public static final int CALL_BARRED = 240; - /** The call is blocked by the Fixed Dialing Number list */ + /** The call is blocked by the Fixed Dialing Number list. */ public static final int FDN_BLOCKED = 241; - /** The given IMSI is not known at the VLR */ - /** TS 24.008 cause 4 */ + /** The given IMSI is not known at the Visitor Location Register (VLR) TS 24.008 cause . */ public static final int IMSI_UNKNOWN_IN_VLR = 242; /** * The network does not accept emergency call establishment using an IMEI or not accept attach - * procedure for emergency services using an IMEI + * procedure for emergency services using an IMEI. */ public static final int IMEI_NOT_ACCEPTED = 243; - /** The call cannot be established because RADIO is OFF */ + /** The call cannot be established because RADIO is OFF. */ public static final int RADIO_OFF = 247; - /** The call cannot be established because of no cell coverage */ + /** The call cannot be established because of no cell coverage. */ public static final int OUT_OF_SRV = 248; - /** The call cannot be established because of no valid SIM */ + /** The call cannot be established because of no valid SIM. */ public static final int NO_VALID_SIM = 249; - /** The call is dropped or failed internally by modem */ + /** The call is dropped or failed internally by modem. */ public static final int RADIO_INTERNAL_ERROR = 250; - /** Call failed because of UE timer expired while waiting for a response from network */ + /** Call failed because of UE timer expired while waiting for a response from network. */ public static final int NETWORK_RESP_TIMEOUT = 251; - /** Call failed because of a network reject */ + /** Call failed because of a network reject. */ public static final int NETWORK_REJECT = 252; - /** Call failed because of radio access failure. ex. RACH failure */ + /** Call failed because of radio access failure. ex. RACH failure. */ public static final int RADIO_ACCESS_FAILURE = 253; - /** Call failed/dropped because of a RLF */ + /** Call failed/dropped because of a Radio Link Failure (RLF). */ public static final int RADIO_LINK_FAILURE = 254; - /** Call failed/dropped because of radio link lost */ + /** Call failed/dropped because of radio link lost. */ public static final int RADIO_LINK_LOST = 255; - /** Call failed because of a radio uplink issue */ + /** Call failed because of a radio uplink issue. */ public static final int RADIO_UPLINK_FAILURE = 256; - /** Call failed because of a RRC connection setup failure */ + /** Call failed because of a RRC (Radio Resource Control) connection setup failure. */ public static final int RADIO_SETUP_FAILURE = 257; - /** Call failed/dropped because of RRC connection release from NW */ + /** Call failed/dropped because of RRC (Radio Resource Control) connection release from NW. */ public static final int RADIO_RELEASE_NORMAL = 258; - /** Call failed/dropped because of RRC abnormally released by modem/network */ + /** + * Call failed/dropped because of RRC (Radio Resource Control) abnormally released by + * modem/network. + */ public static final int RADIO_RELEASE_ABNORMAL = 259; - /** Call setup failed because of access class barring */ + /** Call setup failed because of access class barring. */ public static final int ACCESS_CLASS_BLOCKED = 260; - /** Call failed/dropped because of a network detach */ + /** Call failed/dropped because of a network detach. */ public static final int NETWORK_DETACH = 261; - /** MS is locked until next power cycle */ + /** Mobile station (MS) is locked until next power cycle. */ public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 1000; - /** Drop call*/ + /** Drop call. */ public static final int CDMA_DROP = 1001; - /** INTERCEPT order received, MS state idle entered */ + /** INTERCEPT order received, Mobile station (MS) state idle entered. */ public static final int CDMA_INTERCEPT = 1002; - /** MS has been redirected, call is cancelled */ + /** Mobile station (MS) has been redirected, call is cancelled. */ public static final int CDMA_REORDER = 1003; - /** Service option rejection */ + /** Service option rejection. */ public static final int CDMA_SO_REJECT = 1004; - /** Requested service is rejected, retry delay is set */ + /** Requested service is rejected, retry delay is set. */ public static final int CDMA_RETRY_ORDER = 1005; - /** Unable to obtain access to the CDMA system */ + /** Unable to obtain access to the CDMA system. */ public static final int CDMA_ACCESS_FAILURE = 1006; - /** Not a preempted call */ + /** Not a preempted call. */ public static final int CDMA_PREEMPTED = 1007; - /** Not an emergency call */ + /** Not an emergency call. */ public static final int CDMA_NOT_EMERGENCY = 1008; - /** Access Blocked by CDMA network */ + /** Access Blocked by CDMA network. */ public static final int CDMA_ACCESS_BLOCKED = 1009; /** Mapped from ImsReasonInfo */ + // TODO: remove ImsReasonInfo from preciseDisconnectCause /* The passed argument is an invalid */ + /** @hide */ public static final int LOCAL_ILLEGAL_ARGUMENT = 1200; // The operation is invoked in invalid call state + /** @hide */ public static final int LOCAL_ILLEGAL_STATE = 1201; // IMS service internal error + /** @hide */ public static final int LOCAL_INTERNAL_ERROR = 1202; // IMS service goes down (service connection is lost) + /** @hide */ public static final int LOCAL_IMS_SERVICE_DOWN = 1203; // No pending incoming call exists + /** @hide */ public static final int LOCAL_NO_PENDING_CALL = 1204; // Service unavailable; by power off + /** @hide */ public static final int LOCAL_POWER_OFF = 1205; // Service unavailable; by low battery + /** @hide */ public static final int LOCAL_LOW_BATTERY = 1206; // Service unavailable; by out of service (data service state) + /** @hide */ public static final int LOCAL_NETWORK_NO_SERVICE = 1207; /* Service unavailable; by no LTE coverage * (VoLTE is not supported even though IMS is registered) */ + /** @hide */ public static final int LOCAL_NETWORK_NO_LTE_COVERAGE = 1208; /** Service unavailable; by located in roaming area */ + /** @hide */ public static final int LOCAL_NETWORK_ROAMING = 1209; /** Service unavailable; by IP changed */ + /** @hide */ public static final int LOCAL_NETWORK_IP_CHANGED = 1210; /** Service unavailable; other */ + /** @hide */ public static final int LOCAL_SERVICE_UNAVAILABLE = 1211; /* Service unavailable; IMS connection is lost (IMS is not registered) */ + /** @hide */ public static final int LOCAL_NOT_REGISTERED = 1212; /** Max call exceeded */ + /** @hide */ public static final int LOCAL_MAX_CALL_EXCEEDED = 1213; /** Call decline */ + /** @hide */ public static final int LOCAL_CALL_DECLINE = 1214; /** SRVCC is in progress */ + /** @hide */ public static final int LOCAL_CALL_VCC_ON_PROGRESSING = 1215; /** Resource reservation is failed (QoS precondition) */ + /** @hide */ public static final int LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 1216; /** Retry CS call; VoLTE service can't be provided by the network or remote end * Resolve the extra code(EXTRA_CODE_CALL_RETRY_*) if the below code is set + * @hide */ public static final int LOCAL_CALL_CS_RETRY_REQUIRED = 1217; /** Retry VoLTE call; VoLTE service can't be provided by the network temporarily */ + /** @hide */ public static final int LOCAL_CALL_VOLTE_RETRY_REQUIRED = 1218; /** IMS call is already terminated (in TERMINATED state) */ + /** @hide */ public static final int LOCAL_CALL_TERMINATED = 1219; /** Handover not feasible */ + /** @hide */ public static final int LOCAL_HO_NOT_FEASIBLE = 1220; /** 1xx waiting timer is expired after sending INVITE request (MO only) */ + /** @hide */ public static final int TIMEOUT_1XX_WAITING = 1221; /** User no answer during call setup operation (MO/MT) * MO : 200 OK to INVITE request is not received, * MT : No action from user after alerting the call + * @hide */ public static final int TIMEOUT_NO_ANSWER = 1222; /** User no answer during call update operation (MO/MT) * MO : 200 OK to re-INVITE request is not received, * MT : No action from user after alerting the call + * @hide */ public static final int TIMEOUT_NO_ANSWER_CALL_UPDATE = 1223; @@ -296,102 +346,142 @@ public class PreciseDisconnectCause { * STATUSCODE (SIP response code) (IMS -> Telephony) */ /** SIP request is redirected */ + /** @hide */ public static final int SIP_REDIRECTED = 1300; /** 4xx responses */ /** 400 : Bad Request */ + /** @hide */ public static final int SIP_BAD_REQUEST = 1310; /** 403 : Forbidden */ + /** @hide */ public static final int SIP_FORBIDDEN = 1311; /** 404 : Not Found */ + /** @hide */ public static final int SIP_NOT_FOUND = 1312; /** 415 : Unsupported Media Type * 416 : Unsupported URI Scheme * 420 : Bad Extension */ + /** @hide */ public static final int SIP_NOT_SUPPORTED = 1313; /** 408 : Request Timeout */ + /** @hide */ public static final int SIP_REQUEST_TIMEOUT = 1314; /** 480 : Temporarily Unavailable */ + /** @hide */ public static final int SIP_TEMPRARILY_UNAVAILABLE = 1315; /** 484 : Address Incomplete */ + /** @hide */ public static final int SIP_BAD_ADDRESS = 1316; /** 486 : Busy Here * 600 : Busy Everywhere */ + /** @hide */ public static final int SIP_BUSY = 1317; /** 487 : Request Terminated */ + /** @hide */ public static final int SIP_REQUEST_CANCELLED = 1318; /** 406 : Not Acceptable * 488 : Not Acceptable Here * 606 : Not Acceptable */ + /** @hide */ public static final int SIP_NOT_ACCEPTABLE = 1319; /** 410 : Gone * 604 : Does Not Exist Anywhere */ + /** @hide */ public static final int SIP_NOT_REACHABLE = 1320; /** Others */ + /** @hide */ public static final int SIP_CLIENT_ERROR = 1321; /** 481 : Transaction Does Not Exist */ + /** @hide */ public static final int SIP_TRANSACTION_DOES_NOT_EXIST = 1322; /** 5xx responses * 501 : Server Internal Error */ + /** @hide */ public static final int SIP_SERVER_INTERNAL_ERROR = 1330; /** 503 : Service Unavailable */ + /** @hide */ public static final int SIP_SERVICE_UNAVAILABLE = 1331; /** 504 : Server Time-out */ + /** @hide */ public static final int SIP_SERVER_TIMEOUT = 1332; /** Others */ + /** @hide */ public static final int SIP_SERVER_ERROR = 1333; /** 6xx responses * 603 : Decline */ + /** @hide */ public static final int SIP_USER_REJECTED = 1340; /** Others */ + /** @hide */ public static final int SIP_GLOBAL_ERROR = 1341; /** Emergency failure */ + /** @hide */ public static final int EMERGENCY_TEMP_FAILURE = 1342; + /** @hide */ public static final int EMERGENCY_PERM_FAILURE = 1343; /** Media resource initialization failed */ + /** @hide */ public static final int MEDIA_INIT_FAILED = 1400; /** RTP timeout (no audio / video traffic in the session) */ + /** @hide */ public static final int MEDIA_NO_DATA = 1401; /** Media is not supported; so dropped the call */ + /** @hide */ public static final int MEDIA_NOT_ACCEPTABLE = 1402; /** Unknown media related errors */ + /** @hide */ public static final int MEDIA_UNSPECIFIED = 1403; /** User triggers the call end */ + /** @hide */ public static final int USER_TERMINATED = 1500; /** No action while an incoming call is ringing */ + /** @hide */ public static final int USER_NOANSWER = 1501; /** User ignores an incoming call */ + /** @hide */ public static final int USER_IGNORE = 1502; /** User declines an incoming call */ + /** @hide */ public static final int USER_DECLINE = 1503; /** Device declines/ends a call due to low battery */ + /** @hide */ public static final int LOW_BATTERY = 1504; /** Device declines call due to blacklisted call ID */ + /** @hide */ public static final int BLACKLISTED_CALL_ID = 1505; /** The call is terminated by the network or remote user */ + /** @hide */ public static final int USER_TERMINATED_BY_REMOTE = 1510; /** * UT */ + /** @hide */ public static final int UT_NOT_SUPPORTED = 1800; + /** @hide */ public static final int UT_SERVICE_UNAVAILABLE = 1801; + /** @hide */ public static final int UT_OPERATION_NOT_ALLOWED = 1802; + /** @hide */ public static final int UT_NETWORK_ERROR = 1803; + /** @hide */ public static final int UT_CB_PASSWORD_MISMATCH = 1804; /** * ECBM + * @hide */ public static final int ECBM_NOT_SUPPORTED = 1900; /** * Fail code used to indicate that Multi-endpoint is not supported by the Ims framework. + * @hide */ public static final int MULTIENDPOINT_NOT_SUPPORTED = 1901; @@ -405,56 +495,68 @@ public class PreciseDisconnectCause { * active wifi call and at the edge of coverage and there is no qualified LTE network available * to handover the call to. We get a handover NOT_TRIGERRED message from the modem. This error * code is received as part of the handover message. + * @hide */ public static final int CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 2000; /** * MT call has ended due to a release from the network * because the call was answered elsewhere + * @hide */ public static final int ANSWERED_ELSEWHERE = 2100; /** * For MultiEndpoint - Call Pull request has failed + * @hide */ public static final int CALL_PULL_OUT_OF_SYNC = 2101; /** * For MultiEndpoint - Call has been pulled from primary to secondary + * @hide */ public static final int CALL_PULLED = 2102; /** * Supplementary services (HOLD/RESUME) failure error codes. * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision. + * @hide */ public static final int SUPP_SVC_FAILED = 2300; + /** @hide */ public static final int SUPP_SVC_CANCELLED = 2301; + /** @hide */ public static final int SUPP_SVC_REINVITE_COLLISION = 2302; /** * DPD Procedure received no response or send failed + * @hide */ public static final int IWLAN_DPD_FAILURE = 2400; /** * Establishment of the ePDG Tunnel Failed + * @hide */ public static final int EPDG_TUNNEL_ESTABLISH_FAILURE = 2500; /** * Re-keying of the ePDG Tunnel Failed; may not always result in teardown + * @hide */ public static final int EPDG_TUNNEL_REKEY_FAILURE = 2501; /** * Connection to the packet gateway is lost + * @hide */ public static final int EPDG_TUNNEL_LOST_CONNECTION = 2502; /** * The maximum number of calls allowed has been reached. Used in a multi-endpoint scenario * where the number of calls across all connected devices has reached the maximum. + * @hide */ public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 2503; @@ -462,21 +564,25 @@ public class PreciseDisconnectCause { * Similar to {@link #CODE_LOCAL_CALL_DECLINE}, except indicates that a remote device has * declined the call. Used in a multi-endpoint scenario where a remote device declined an * incoming call. + * @hide */ public static final int REMOTE_CALL_DECLINE = 2504; /** * Indicates the call was disconnected due to the user reaching their data limit. + * @hide */ public static final int DATA_LIMIT_REACHED = 2505; /** * Indicates the call was disconnected due to the user disabling cellular data. + * @hide */ public static final int DATA_DISABLED = 2506; /** * Indicates a call was disconnected due to loss of wifi signal. + * @hide */ public static final int WIFI_LOST = 2507; @@ -499,7 +605,7 @@ public class PreciseDisconnectCause { public static final int OEM_CAUSE_14 = 0xf00e; public static final int OEM_CAUSE_15 = 0xf00f; - /** Disconnected due to unspecified reasons */ + /** Disconnected due to unspecified reasons. */ public static final int ERROR_UNSPECIFIED = 0xffff; /** Private constructor to avoid class instantiation. */ diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java index a7e8e8a95ea2..a1e8b199d2a0 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -138,10 +138,15 @@ public class SubscriptionInfo implements Parcelable { private UiccAccessRule[] mAccessRules; /** - * The ID of the SIM card. It is the ICCID of the active profile for a UICC card and the EID - * for an eUICC card. + * The string ID of the SIM card. It is the ICCID of the active profile for a UICC card and the + * EID for an eUICC card. */ - private String mCardId; + private String mCardString; + + /** + * The card ID of the SIM card. This maps uniquely to the card string. + */ + private int mCardId; /** * Whether the subscription is opportunistic. @@ -174,9 +179,9 @@ public class SubscriptionInfo implements Parcelable { public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName, CharSequence carrierName, int nameSource, int iconTint, String number, int roaming, Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded, - @Nullable UiccAccessRule[] accessRules, String cardId) { + @Nullable UiccAccessRule[] accessRules, String cardString) { this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number, - roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardId, + roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString, false, null, true, TelephonyManager.UNKNOWN_CARRIER_ID); } @@ -186,20 +191,22 @@ public class SubscriptionInfo implements Parcelable { public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName, CharSequence carrierName, int nameSource, int iconTint, String number, int roaming, Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded, - @Nullable UiccAccessRule[] accessRules, String cardId, boolean isOpportunistic, + @Nullable UiccAccessRule[] accessRules, String cardString, boolean isOpportunistic, @Nullable String groupUUID, boolean isMetered, int carrierId) { this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number, - roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardId, + roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString, -1, isOpportunistic, groupUUID, isMetered, false, carrierId); } + /** * @hide */ public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName, CharSequence carrierName, int nameSource, int iconTint, String number, int roaming, Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded, - @Nullable UiccAccessRule[] accessRules, String cardId, boolean isOpportunistic, - @Nullable String groupUUID, boolean isMetered, boolean isGroupDisabled, int carrierid) { + @Nullable UiccAccessRule[] accessRules, String cardString, int cardId, + boolean isOpportunistic, @Nullable String groupUUID, boolean isMetered, + boolean isGroupDisabled, int carrierid) { this.mId = id; this.mIccId = iccId; this.mSimSlotIndex = simSlotIndex; @@ -215,6 +222,7 @@ public class SubscriptionInfo implements Parcelable { this.mCountryIso = countryIso; this.mIsEmbedded = isEmbedded; this.mAccessRules = accessRules; + this.mCardString = cardString; this.mCardId = cardId; this.mIsOpportunistic = isOpportunistic; this.mGroupUUID = groupUUID; @@ -523,10 +531,21 @@ public class SubscriptionInfo implements Parcelable { } /** - * @return the ID of the SIM card which contains the subscription. + * @return the card string of the SIM card which contains the subscription. The card string is + * the ICCID for UICCs or the EID for eUICCs. + * @hide + * //TODO rename usages in LPA: UiccSlotUtil.java, UiccSlotsManager.java, UiccSlotInfoTest.java + */ + public String getCardString() { + return this.mCardString; + } + + /** + * @return the cardId of the SIM card which contains the subscription. * @hide */ - public String getCardId() { + @SystemApi + public int getCardId() { return this.mCardId; } @@ -564,7 +583,8 @@ public class SubscriptionInfo implements Parcelable { Bitmap iconBitmap = Bitmap.CREATOR.createFromParcel(source); boolean isEmbedded = source.readBoolean(); UiccAccessRule[] accessRules = source.createTypedArray(UiccAccessRule.CREATOR); - String cardId = source.readString(); + String cardString = source.readString(); + int cardId = source.readInt(); boolean isOpportunistic = source.readBoolean(); String groupUUID = source.readString(); boolean isMetered = source.readBoolean(); @@ -573,8 +593,8 @@ public class SubscriptionInfo implements Parcelable { return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso, - isEmbedded, accessRules, cardId, isOpportunistic, groupUUID, isMetered, - isGroupDisabled, carrierid); + isEmbedded, accessRules, cardString, cardId, isOpportunistic, groupUUID, + isMetered, isGroupDisabled, carrierid); } @Override @@ -600,7 +620,8 @@ public class SubscriptionInfo implements Parcelable { mIconBitmap.writeToParcel(dest, flags); dest.writeBoolean(mIsEmbedded); dest.writeTypedArray(mAccessRules, flags); - dest.writeString(mCardId); + dest.writeString(mCardString); + dest.writeInt(mCardId); dest.writeBoolean(mIsOpportunistic); dest.writeString(mGroupUUID); dest.writeBoolean(mIsMetered); @@ -631,7 +652,7 @@ public class SubscriptionInfo implements Parcelable { @Override public String toString() { String iccIdToPrint = givePrintableIccid(mIccId); - String cardIdToPrint = givePrintableIccid(mCardId); + String cardStringToPrint = givePrintableIccid(mCardString); return "{id=" + mId + ", iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex + " carrierId=" + mCarrierId + " displayName=" + mDisplayName + " carrierName=" + mCarrierName + " nameSource=" + mNameSource @@ -639,17 +660,17 @@ public class SubscriptionInfo implements Parcelable { + " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc + " mnc " + mMnc + "mCountryIso=" + mCountryIso + " isEmbedded " + mIsEmbedded + " accessRules " + Arrays.toString(mAccessRules) - + " cardId=" + cardIdToPrint + " isOpportunistic " + mIsOpportunistic - + " mGroupUUID=" + mGroupUUID + " isMetered=" + mIsMetered - + " mIsGroupDisabled=" + mIsGroupDisabled + "}"; + + " cardString=" + cardStringToPrint + " cardId=" + mCardId + + " isOpportunistic " + mIsOpportunistic + " mGroupUUID=" + mGroupUUID + + " isMetered=" + mIsMetered + " mIsGroupDisabled=" + mIsGroupDisabled + "}"; } @Override public int hashCode() { return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded, mIsOpportunistic, mGroupUUID, mIsMetered, mIccId, mNumber, mMcc, mMnc, - mCountryIso, mCardId, mDisplayName, mCarrierName, mAccessRules, mIsGroupDisabled, - mCarrierId); + mCountryIso, mCardString, mCardId, mDisplayName, mCarrierName, mAccessRules, + mIsGroupDisabled, mCarrierId); } @Override @@ -680,6 +701,7 @@ public class SubscriptionInfo implements Parcelable { && Objects.equals(mMcc, toCompare.mMcc) && Objects.equals(mMnc, toCompare.mMnc) && Objects.equals(mCountryIso, toCompare.mCountryIso) + && Objects.equals(mCardString, toCompare.mCardString) && Objects.equals(mCardId, toCompare.mCardId) && TextUtils.equals(mDisplayName, toCompare.mDisplayName) && TextUtils.equals(mCarrierName, toCompare.mCarrierName) diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index eaff50a522ea..b61e99bc6f0d 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1198,7 +1198,8 @@ public class SubscriptionManager { } /** - * Request a refresh of the platform cache of profile information. + * Request a refresh of the platform cache of profile information for the eUICC which + * corresponds to the card ID returned by {@link TelephonyManager#getCardIdForDefaultEuicc()}. * * <p>Should be called by the EuiccService implementation whenever this information changes due * to an operation done outside the scope of a request initiated by the platform to the @@ -1206,17 +1207,50 @@ public class SubscriptionManager { * were made through the EuiccService. * * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. + * + * @see {@link TelephonyManager#getCardIdForDefaultEuicc()} for more information on the card ID. + * * @hide */ @SystemApi public void requestEmbeddedSubscriptionInfoListRefresh() { + int cardId = TelephonyManager.from(mContext).getCardIdForDefaultEuicc(); try { ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); if (iSub != null) { - iSub.requestEmbeddedSubscriptionInfoListRefresh(); + iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId); } } catch (RemoteException ex) { - // ignore it + logd("requestEmbeddedSubscriptionInfoListFresh for card = " + cardId + " failed."); + } + } + + /** + * Request a refresh of the platform cache of profile information for the eUICC with the given + * {@code cardId}. + * + * <p>Should be called by the EuiccService implementation whenever this information changes due + * to an operation done outside the scope of a request initiated by the platform to the + * EuiccService. There is no need to refresh for downloads, deletes, or other operations that + * were made through the EuiccService. + * + * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. + * + * @param cardId the card ID of the eUICC. + * + * @see {@link TelephonyManager#getCardIdForDefaultEuicc()} for more information on the card ID. + * + * @hide + */ + @SystemApi + public void requestEmbeddedSubscriptionInfoListRefresh(int cardId) { + try { + ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + if (iSub != null) { + iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId); + } + } catch (RemoteException ex) { + logd("requestEmbeddedSubscriptionInfoListFresh for card = " + cardId + " failed."); } } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index e0632b1e0392..c7df36b62a79 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -625,8 +625,6 @@ public class TelephonyManager { * The {@link #EXTRA_RINGING_CALL_STATE} extra indicates the ringing call state. * The {@link #EXTRA_FOREGROUND_CALL_STATE} extra indicates the foreground call state. * The {@link #EXTRA_BACKGROUND_CALL_STATE} extra indicates the background call state. - * The {@link #EXTRA_DISCONNECT_CAUSE} extra indicates the disconnect cause. - * The {@link #EXTRA_PRECISE_DISCONNECT_CAUSE} extra indicates the precise disconnect cause. * * <p class="note"> * Requires the READ_PRECISE_PHONE_STATE permission. @@ -634,12 +632,10 @@ public class TelephonyManager { * @see #EXTRA_RINGING_CALL_STATE * @see #EXTRA_FOREGROUND_CALL_STATE * @see #EXTRA_BACKGROUND_CALL_STATE - * @see #EXTRA_DISCONNECT_CAUSE - * @see #EXTRA_PRECISE_DISCONNECT_CAUSE * * <p class="note"> * Requires the READ_PRECISE_PHONE_STATE permission. - * + * @deprecated use {@link PhoneStateListener#LISTEN_PRECISE_CALL_STATE} instead * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @@ -647,8 +643,28 @@ public class TelephonyManager { "android.intent.action.PRECISE_CALL_STATE"; /** - * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast - * for an integer containing the state of the current ringing call. + * Broadcast intent action indicating that call disconnect cause has changed. + * + * <p> + * The {@link #EXTRA_DISCONNECT_CAUSE} extra indicates the disconnect cause. + * The {@link #EXTRA_PRECISE_DISCONNECT_CAUSE} extra indicates the precise disconnect cause. + * + * <p class="note"> + * Requires the READ_PRECISE_PHONE_STATE permission. + * + * @see #EXTRA_DISCONNECT_CAUSE + * @see #EXTRA_PRECISE_DISCONNECT_CAUSE + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CALL_DISCONNECT_CAUSE_CHANGED = + "android.intent.action.CALL_DISCONNECT_CAUSE"; + + /** + * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and + * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer + * containing the state of the current ringing call. * * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID * @see PreciseCallState#PRECISE_CALL_STATE_IDLE @@ -670,8 +686,9 @@ public class TelephonyManager { public static final String EXTRA_RINGING_CALL_STATE = "ringing_state"; /** - * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast - * for an integer containing the state of the current foreground call. + * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and + * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer + * containing the state of the current foreground call. * * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID * @see PreciseCallState#PRECISE_CALL_STATE_IDLE @@ -693,8 +710,9 @@ public class TelephonyManager { public static final String EXTRA_FOREGROUND_CALL_STATE = "foreground_state"; /** - * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast - * for an integer containing the state of the current background call. + * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and + * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer + * containing the state of the current background call. * * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID * @see PreciseCallState#PRECISE_CALL_STATE_IDLE @@ -716,8 +734,9 @@ public class TelephonyManager { public static final String EXTRA_BACKGROUND_CALL_STATE = "background_state"; /** - * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast - * for an integer containing the disconnect cause. + * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and + * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer + * containing the disconnect cause. * * @see DisconnectCause * @@ -730,8 +749,9 @@ public class TelephonyManager { public static final String EXTRA_DISCONNECT_CAUSE = "disconnect_cause"; /** - * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast - * for an integer containing the disconnect cause provided by the RIL. + * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and + * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer + * containing the disconnect cause provided by the RIL. * * @see PreciseDisconnectCause * diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl index 78fc0bc487bf..00cf9c3577ec 100644 --- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -55,5 +55,6 @@ oneway interface IPhoneStateListener { void onPreferredDataSubIdChanged(in int subId); void onRadioPowerStateChanged(in int state); void onEmergencyNumberListChanged(in Map emergencyNumberList); + void onCallDisconnectCauseChanged(in int disconnectCause, in int preciseDisconnectCause); } diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl index 65eedb83c4cc..d169b7d04f5c 100755 --- a/telephony/java/com/android/internal/telephony/ISub.aidl +++ b/telephony/java/com/android/internal/telephony/ISub.aidl @@ -104,7 +104,7 @@ interface ISub { /** * @see android.telephony.SubscriptionManager#requestEmbeddedSubscriptionInfoListRefresh */ - oneway void requestEmbeddedSubscriptionInfoListRefresh(); + oneway void requestEmbeddedSubscriptionInfoListRefresh(int cardId); /** * Add a new SubscriptionInfo to subinfo database if needed diff --git a/tests/net/java/android/net/dhcp/DhcpServerTest.java b/tests/net/java/android/net/dhcp/DhcpServerTest.java index df34c7310b63..ab9bd84b05cb 100644 --- a/tests/net/java/android/net/dhcp/DhcpServerTest.java +++ b/tests/net/java/android/net/dhcp/DhcpServerTest.java @@ -25,7 +25,6 @@ import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -48,7 +47,6 @@ import android.net.dhcp.DhcpLeaseRepository.InvalidAddressException; import android.net.dhcp.DhcpLeaseRepository.OutOfAddressesException; import android.net.dhcp.DhcpServer.Clock; import android.net.dhcp.DhcpServer.Dependencies; -import android.net.util.InterfaceParams; import android.net.util.SharedLog; import android.os.test.TestLooper; import android.support.test.filters.SmallTest; @@ -74,9 +72,6 @@ import java.util.Set; public class DhcpServerTest { private static final String PROP_DEXMAKER_SHARE_CLASSLOADER = "dexmaker.share_classloader"; private static final String TEST_IFACE = "testiface"; - private static final MacAddress TEST_IFACE_MAC = MacAddress.fromString("11:22:33:44:55:66"); - private static final InterfaceParams TEST_IFACEPARAMS = - new InterfaceParams(TEST_IFACE, 1, TEST_IFACE_MAC); private static final Inet4Address TEST_SERVER_ADDR = parseAddr("192.168.0.2"); private static final LinkAddress TEST_SERVER_LINKADDR = new LinkAddress(TEST_SERVER_ADDR, 20); @@ -149,7 +144,7 @@ public class DhcpServerTest { .build(); mLooper = new TestLooper(); - mServer = new DhcpServer(mLooper.getLooper(), TEST_IFACEPARAMS, servingParams, + mServer = new DhcpServer(mLooper.getLooper(), TEST_IFACE, servingParams, new SharedLog(DhcpServerTest.class.getSimpleName()), mDeps); mServer.start(); diff --git a/tests/net/java/android/net/ip/IpServerTest.java b/tests/net/java/android/net/ip/IpServerTest.java index cff0b5469d47..2c675c68a076 100644 --- a/tests/net/java/android/net/ip/IpServerTest.java +++ b/tests/net/java/android/net/ip/IpServerTest.java @@ -404,7 +404,7 @@ public class IpServerTest { private void assertDhcpStarted(IpPrefix expectedPrefix) { verify(mDependencies, times(1)).makeDhcpServer( - eq(mLooper.getLooper()), eq(TEST_IFACE_PARAMS), any(), eq(mSharedLog)); + eq(mLooper.getLooper()), eq(IFACE_NAME), any(), eq(mSharedLog)); verify(mDhcpServer, times(1)).start(); final DhcpServingParams params = mDhcpParamsCaptor.getValue(); // Last address byte is random diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java index bca9be772704..e6b43d286a3d 100644 --- a/tests/net/java/com/android/server/connectivity/TetheringTest.java +++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java @@ -240,7 +240,7 @@ public class TetheringTest { } @Override - public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface, + public DhcpServer makeDhcpServer(Looper looper, String ifName, DhcpServingParams params, SharedLog log) { return mDhcpServer; } diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index 95877045072b..c91134c167ed 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -78,7 +78,7 @@ static uint32_t ParseFormatType(const StringPiece& piece) { static uint32_t ParseFormatAttribute(const StringPiece& str) { uint32_t mask = 0; - for (StringPiece part : util::Tokenize(str, '|')) { + for (const StringPiece& part : util::Tokenize(str, '|')) { StringPiece trimmed_part = util::TrimWhitespace(part); uint32_t type = ParseFormatType(trimmed_part); if (type == 0) { diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp index da22e885b917..c6f91527c91c 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -362,7 +362,7 @@ std::unique_ptr<BinaryPrimitive> TryParseFlagSymbol(const Attribute* flag_attr, return util::make_unique<BinaryPrimitive>(flags); } - for (StringPiece part : util::Tokenize(str, '|')) { + for (const StringPiece& part : util::Tokenize(str, '|')) { StringPiece trimmed_part = util::TrimWhitespace(part); bool flag_set = false; diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp index fc9514a691d2..f63a0745690b 100644 --- a/tools/aapt2/cmd/Compile.cpp +++ b/tools/aapt2/cmd/Compile.cpp @@ -433,7 +433,7 @@ static bool CompileXml(IAaptContext* context, const CompileOptions& options, } Printer r_txt_printer(&fout_text); - for (const auto res : xmlres->file.exported_symbols) { + for (const auto& res : xmlres->file.exported_symbols) { r_txt_printer.Print("default int id "); r_txt_printer.Println(res.name.entry); } diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp index 8d91b0098c1f..a4610b2575b9 100644 --- a/tools/aapt2/java/AnnotationProcessor.cpp +++ b/tools/aapt2/java/AnnotationProcessor.cpp @@ -113,7 +113,7 @@ void AnnotationProcessor::AppendNewLine() { void AnnotationProcessor::Print(Printer* printer) const { if (has_comments_) { std::string result = comment_.str(); - for (StringPiece line : util::Tokenize(result, '\n')) { + for (const StringPiece& line : util::Tokenize(result, '\n')) { printer->Println(line); } printer->Println(" */"); diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp index 73105e16559b..5cfbbf2485e0 100644 --- a/tools/aapt2/util/Files.cpp +++ b/tools/aapt2/util/Files.cpp @@ -182,7 +182,7 @@ void AppendPath(std::string* base, StringPiece part) { std::string PackageToPath(const StringPiece& package) { std::string out_path; - for (StringPiece part : util::Tokenize(package, '.')) { + for (const StringPiece& part : util::Tokenize(package, '.')) { AppendPath(&out_path, part); } return out_path; diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index a8e82b3d1bc6..0362a1b491ea 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -197,5 +197,7 @@ interface IWifiManager int removeNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName); String[] getFactoryMacAddresses(); + + void setDeviceMobilityState(int state); } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 8cb20638dd42..a7c2ff0f875c 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -4476,4 +4476,69 @@ public class WifiManager { throw e.rethrowFromSystemServer(); } } + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"DEVICE_MOBILITY_STATE_"}, value = { + DEVICE_MOBILITY_STATE_UNKNOWN, + DEVICE_MOBILITY_STATE_HIGH_MVMT, + DEVICE_MOBILITY_STATE_LOW_MVMT, + DEVICE_MOBILITY_STATE_STATIONARY}) + public @interface DeviceMobilityState {} + + /** + * Unknown device mobility state + * + * @see #setDeviceMobilityState(int) + * + * @hide + */ + @SystemApi + public static final int DEVICE_MOBILITY_STATE_UNKNOWN = 0; + + /** + * High movement device mobility state + * + * @see #setDeviceMobilityState(int) + * + * @hide + */ + @SystemApi + public static final int DEVICE_MOBILITY_STATE_HIGH_MVMT = 1; + + /** + * Low movement device mobility state + * + * @see #setDeviceMobilityState(int) + * + * @hide + */ + @SystemApi + public static final int DEVICE_MOBILITY_STATE_LOW_MVMT = 2; + + /** + * Stationary device mobility state + * + * @see #setDeviceMobilityState(int) + * + * @hide + */ + @SystemApi + public static final int DEVICE_MOBILITY_STATE_STATIONARY = 3; + + /** + * Updates the device mobility state. Wifi uses this information to adjust the interval between + * Wifi scans in order to balance power consumption with scan accuracy. + * @param state the updated device mobility state + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) + public void setDeviceMobilityState(@DeviceMobilityState int state) { + try { + mService.setDeviceMobilityState(state); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } |