diff options
284 files changed, 7502 insertions, 2135 deletions
diff --git a/api/current.txt b/api/current.txt index 96485a81fffb..bf3a1f42a535 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11302,6 +11302,7 @@ package android.content.pm { method public void updateSessionAppLabel(int, java.lang.CharSequence); field public static final java.lang.String ACTION_SESSION_COMMITTED = "android.content.pm.action.SESSION_COMMITTED"; field public static final java.lang.String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS"; + field public static final java.lang.String ACTION_SESSION_UPDATED = "android.content.pm.action.SESSION_UPDATED"; field public static final java.lang.String EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME"; field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME"; field public static final java.lang.String EXTRA_SESSION = "android.content.pm.extra.SESSION"; @@ -14134,8 +14135,11 @@ package android.graphics { public final class Insets { method public static android.graphics.Insets add(android.graphics.Insets, android.graphics.Insets); + method public static android.graphics.Insets max(android.graphics.Insets, android.graphics.Insets); + method public static android.graphics.Insets min(android.graphics.Insets, android.graphics.Insets); method public static android.graphics.Insets of(int, int, int, int); method public static android.graphics.Insets of(android.graphics.Rect); + method public static android.graphics.Insets subtract(android.graphics.Insets, android.graphics.Insets); field public static final android.graphics.Insets NONE; field public final int bottom; field public final int left; @@ -23379,12 +23383,13 @@ package android.media { method public android.media.AudioPresentation.Builder setProgramId(int); } - public class AudioRecord implements android.media.AudioRouting { + public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting { ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException; method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler); method public deprecated void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler); method protected void finalize(); method public java.util.List<android.media.MicrophoneInfo> getActiveMicrophones() throws java.io.IOException; + method public android.media.AudioRecordingConfiguration getActiveRecordingConfiguration(); method public int getAudioFormat(); method public int getAudioSessionId(); method public int getAudioSource(); @@ -23409,6 +23414,7 @@ package android.media { method public int read(float[], int, int, int); method public int read(java.nio.ByteBuffer, int); method public int read(java.nio.ByteBuffer, int, int); + method public void registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback); method public void release(); method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener); method public deprecated void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener); @@ -23420,6 +23426,7 @@ package android.media { method public void startRecording() throws java.lang.IllegalStateException; method public void startRecording(android.media.MediaSyncEvent) throws java.lang.IllegalStateException; method public void stop() throws java.lang.IllegalStateException; + method public void unregisterAudioRecordingCallback(android.media.AudioManager.AudioRecordingCallback); field public static final int ERROR = -1; // 0xffffffff field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa @@ -23474,6 +23481,12 @@ package android.media { field public static final android.os.Parcelable.Creator<android.media.AudioRecordingConfiguration> CREATOR; } + public abstract interface AudioRecordingMonitor { + method public abstract android.media.AudioRecordingConfiguration getActiveRecordingConfiguration(); + method public abstract void registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback); + method public abstract void unregisterAudioRecordingCallback(android.media.AudioManager.AudioRecordingCallback); + } + public abstract interface AudioRouting { method public abstract void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler); method public abstract android.media.AudioDeviceInfo getPreferredDevice(); @@ -25475,11 +25488,12 @@ package android.media { field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1 } - public class MediaRecorder implements android.media.AudioRouting { + public class MediaRecorder implements android.media.AudioRecordingMonitor android.media.AudioRouting { ctor public MediaRecorder(); method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler); method protected void finalize(); method public java.util.List<android.media.MicrophoneInfo> getActiveMicrophones() throws java.io.IOException; + method public android.media.AudioRecordingConfiguration getActiveRecordingConfiguration(); method public static final int getAudioSourceMax(); method public int getMaxAmplitude() throws java.lang.IllegalStateException; method public android.os.PersistableBundle getMetrics(); @@ -25488,6 +25502,7 @@ package android.media { method public android.view.Surface getSurface(); method public void pause() throws java.lang.IllegalStateException; method public void prepare() throws java.io.IOException, java.lang.IllegalStateException; + method public void registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback); method public void release(); method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener); method public void reset(); @@ -25523,6 +25538,7 @@ package android.media { method public void setVideoSource(int) throws java.lang.IllegalStateException; method public void start() throws java.lang.IllegalStateException; method public void stop() throws java.lang.IllegalStateException; + method public void unregisterAudioRecordingCallback(android.media.AudioManager.AudioRecordingCallback); field public static final int MEDIA_ERROR_SERVER_DIED = 100; // 0x64 field public static final int MEDIA_RECORDER_ERROR_UNKNOWN = 1; // 0x1 field public static final int MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800; // 0x320 @@ -28228,7 +28244,7 @@ package android.net { method public void addDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener); method public boolean bindProcessToNetwork(android.net.Network); method public android.net.Network getActiveNetwork(); - method public android.net.NetworkInfo getActiveNetworkInfo(); + method public deprecated android.net.NetworkInfo getActiveNetworkInfo(); method public deprecated android.net.NetworkInfo[] getAllNetworkInfo(); method public android.net.Network[] getAllNetworks(); method public deprecated boolean getBackgroundDataSetting(); @@ -28239,7 +28255,7 @@ package android.net { method public int getMultipathPreference(android.net.Network); method public android.net.NetworkCapabilities getNetworkCapabilities(android.net.Network); method public deprecated android.net.NetworkInfo getNetworkInfo(int); - method public android.net.NetworkInfo getNetworkInfo(android.net.Network); + method public deprecated android.net.NetworkInfo getNetworkInfo(android.net.Network); method public deprecated int getNetworkPreference(); method public byte[] getNetworkWatchlistConfigHash(); method public static deprecated android.net.Network getProcessDefaultNetwork(); @@ -28273,14 +28289,14 @@ package android.net { field public static final deprecated int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1 field public static final java.lang.String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL"; field public static final java.lang.String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL"; - field public static final java.lang.String EXTRA_EXTRA_INFO = "extraInfo"; - field public static final java.lang.String EXTRA_IS_FAILOVER = "isFailover"; + field public static final deprecated java.lang.String EXTRA_EXTRA_INFO = "extraInfo"; + field public static final deprecated java.lang.String EXTRA_IS_FAILOVER = "isFailover"; field public static final java.lang.String EXTRA_NETWORK = "android.net.extra.NETWORK"; field public static final deprecated java.lang.String EXTRA_NETWORK_INFO = "networkInfo"; field public static final java.lang.String EXTRA_NETWORK_REQUEST = "android.net.extra.NETWORK_REQUEST"; field public static final java.lang.String EXTRA_NETWORK_TYPE = "networkType"; field public static final java.lang.String EXTRA_NO_CONNECTIVITY = "noConnectivity"; - field public static final java.lang.String EXTRA_OTHER_NETWORK_INFO = "otherNetwork"; + field public static final deprecated java.lang.String EXTRA_OTHER_NETWORK_INFO = "otherNetwork"; field public static final java.lang.String EXTRA_REASON = "reason"; field public static final int MULTIPATH_PREFERENCE_HANDOVER = 1; // 0x1 field public static final int MULTIPATH_PREFERENCE_PERFORMANCE = 4; // 0x4 @@ -28432,6 +28448,7 @@ package android.net { method public android.net.ProxyInfo getHttpProxy(); method public java.lang.String getInterfaceName(); method public java.util.List<android.net.LinkAddress> getLinkAddresses(); + method public int getMtu(); method public java.lang.String getPrivateDnsServerName(); method public java.util.List<android.net.RouteInfo> getRoutes(); method public boolean isPrivateDnsActive(); @@ -28580,10 +28597,10 @@ package android.net { field public static final int TRANSPORT_WIFI_AWARE = 5; // 0x5 } - public class NetworkInfo implements android.os.Parcelable { + public deprecated class NetworkInfo implements android.os.Parcelable { method public int describeContents(); method public deprecated android.net.NetworkInfo.DetailedState getDetailedState(); - method public java.lang.String getExtraInfo(); + method public deprecated java.lang.String getExtraInfo(); method public deprecated java.lang.String getReason(); method public deprecated android.net.NetworkInfo.State getState(); method public deprecated int getSubtype(); @@ -28599,7 +28616,7 @@ package android.net { field public static final android.os.Parcelable.Creator<android.net.NetworkInfo> CREATOR; } - public static final class NetworkInfo.DetailedState extends java.lang.Enum { + public static final deprecated class NetworkInfo.DetailedState extends java.lang.Enum { method public static android.net.NetworkInfo.DetailedState valueOf(java.lang.String); method public static final android.net.NetworkInfo.DetailedState[] values(); enum_constant public static final android.net.NetworkInfo.DetailedState AUTHENTICATING; @@ -28617,7 +28634,7 @@ package android.net { enum_constant public static final android.net.NetworkInfo.DetailedState VERIFYING_POOR_LINK; } - public static final class NetworkInfo.State extends java.lang.Enum { + public static final deprecated class NetworkInfo.State extends java.lang.Enum { method public static android.net.NetworkInfo.State valueOf(java.lang.String); method public static final android.net.NetworkInfo.State[] values(); enum_constant public static final android.net.NetworkInfo.State CONNECTED; @@ -38682,22 +38699,23 @@ package android.provider { field public static final android.net.Uri CONTENT_URI; field public static final java.lang.String CURRENT = "current"; field public static final java.lang.String DEFAULT_SORT_ORDER = "name ASC"; - field public static final java.lang.String MCC = "mcc"; + field public static final deprecated java.lang.String MCC = "mcc"; field public static final java.lang.String MMSC = "mmsc"; field public static final java.lang.String MMSPORT = "mmsport"; field public static final java.lang.String MMSPROXY = "mmsproxy"; - field public static final java.lang.String MNC = "mnc"; - field public static final java.lang.String MVNO_MATCH_DATA = "mvno_match_data"; - field public static final java.lang.String MVNO_TYPE = "mvno_type"; + field public static final deprecated java.lang.String MNC = "mnc"; + field public static final deprecated java.lang.String MVNO_MATCH_DATA = "mvno_match_data"; + field public static final deprecated java.lang.String MVNO_TYPE = "mvno_type"; field public static final java.lang.String NAME = "name"; field public static final java.lang.String NETWORK_TYPE_BITMASK = "network_type_bitmask"; - field public static final java.lang.String NUMERIC = "numeric"; + field public static final deprecated java.lang.String NUMERIC = "numeric"; field public static final java.lang.String PASSWORD = "password"; field public static final java.lang.String PORT = "port"; field public static final java.lang.String PROTOCOL = "protocol"; field public static final java.lang.String PROXY = "proxy"; field public static final java.lang.String ROAMING_PROTOCOL = "roaming_protocol"; field public static final java.lang.String SERVER = "server"; + field public static final android.net.Uri SIM_APN_URI; field public static final java.lang.String SUBSCRIPTION_ID = "sub_id"; field public static final java.lang.String TYPE = "type"; field public static final java.lang.String USER = "user"; @@ -52228,7 +52246,6 @@ package android.view.contentcapture { } public final class ContentCaptureManager { - method public android.view.contentcapture.ContentCaptureSession createContentCaptureSession(android.view.contentcapture.ContentCaptureContext); method public android.content.ComponentName getServiceComponentName(); method public boolean isContentCaptureEnabled(); method public void removeUserData(android.view.contentcapture.UserDataRemovalRequest); @@ -52237,6 +52254,7 @@ package android.view.contentcapture { public abstract class ContentCaptureSession implements java.lang.AutoCloseable { method public void close(); + method public final android.view.contentcapture.ContentCaptureSession createContentCaptureSession(android.view.contentcapture.ContentCaptureContext); method public final void destroy(); method public final android.view.contentcapture.ContentCaptureSessionId getContentCaptureSessionId(); method public final void notifyViewAppeared(android.view.ViewStructure); diff --git a/api/system-current.txt b/api/system-current.txt index 751ef34f0ae1..d98139933588 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -2577,6 +2577,81 @@ package android.location { method public void onLocationBatch(java.util.List<android.location.Location>); } + public final class GnssMeasurementCorrections implements android.os.Parcelable { + method public int describeContents(); + method public double getAltitudeMeters(); + method public double getLatitudeDegrees(); + method public double getLongitudeDegrees(); + method public java.util.List<android.location.GnssSingleSatCorrection> getSingleSatCorrectionList(); + method public long getToaGpsNanosecondsOfWeek(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.location.GnssMeasurementCorrections> CREATOR; + } + + public static class GnssMeasurementCorrections.Builder { + ctor public GnssMeasurementCorrections.Builder(); + method public android.location.GnssMeasurementCorrections build(); + method public android.location.GnssMeasurementCorrections.Builder setAltitudeMeters(double); + method public android.location.GnssMeasurementCorrections.Builder setLatitudeDegrees(double); + method public android.location.GnssMeasurementCorrections.Builder setLongitudeDegrees(double); + method public android.location.GnssMeasurementCorrections.Builder setSingleSatCorrectionList(java.util.List<android.location.GnssSingleSatCorrection>); + method public android.location.GnssMeasurementCorrections.Builder setToaGpsNanosecondsOfWeek(long); + } + + public final class GnssReflectingPlane implements android.os.Parcelable { + method public int describeContents(); + method public double getAltitudeMeters(); + method public double getAzimuthDegrees(); + method public double getLatitudeDegrees(); + method public double getLongitudeDegrees(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.location.GnssReflectingPlane> CREATOR; + } + + public static class GnssReflectingPlane.Builder { + ctor public GnssReflectingPlane.Builder(); + method public android.location.GnssReflectingPlane build(); + method public android.location.GnssReflectingPlane.Builder setAltitudeMeters(double); + method public android.location.GnssReflectingPlane.Builder setAzimuthDegrees(double); + method public android.location.GnssReflectingPlane.Builder setLatitudeDegrees(double); + method public android.location.GnssReflectingPlane.Builder setLongitudeDegrees(double); + } + + public final class GnssSingleSatCorrection implements android.os.Parcelable { + method public int describeContents(); + method public float getCarrierFrequencyHz(); + method public int getConstellationType(); + method public float getExcessPathLengthMeters(); + method public float getExcessPathLengthUncertaintyMeters(); + method public android.location.GnssReflectingPlane getReflectingPlane(); + method public int getSatId(); + method public int getSingleSatCorrectionFlags(); + method public boolean hasExcessPathLength(); + method public boolean hasExcessPathLengthUncertainty(); + method public boolean hasReflectingPlane(); + method public boolean hasSatelliteLineOfSight(); + method public boolean isSatelliteLineOfSight(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.location.GnssSingleSatCorrection> CREATOR; + field public static final int HAS_EXCESS_PATH_LENGTH_MASK = 2; // 0x2 + field public static final int HAS_EXCESS_PATH_LENGTH_UNC_MASK = 4; // 0x4 + field public static final int HAS_REFLECTING_PLANE_MASK = 8; // 0x8 + field public static final int HAS_SAT_IS_LOS_MASK = 1; // 0x1 + } + + public static class GnssSingleSatCorrection.Builder { + ctor public GnssSingleSatCorrection.Builder(); + method public android.location.GnssSingleSatCorrection build(); + method public android.location.GnssSingleSatCorrection.Builder setCarrierFrequencyHz(float); + method public android.location.GnssSingleSatCorrection.Builder setConstellationType(int); + method public android.location.GnssSingleSatCorrection.Builder setExcessPathLengthMeters(float); + method public android.location.GnssSingleSatCorrection.Builder setExcessPathLengthUncertaintyMeters(float); + method public android.location.GnssSingleSatCorrection.Builder setReflectingPlane(android.location.GnssReflectingPlane); + method public android.location.GnssSingleSatCorrection.Builder setSatId(int); + method public android.location.GnssSingleSatCorrection.Builder setSatIsLos(boolean); + method public android.location.GnssSingleSatCorrection.Builder setSingleSatCorrectionFlags(int); + } + public class GpsClock implements android.os.Parcelable { method public int describeContents(); method public double getBiasInNs(); @@ -2813,8 +2888,10 @@ package android.location { method public deprecated boolean addGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener); method public void flushGnssBatch(); method public int getGnssBatchSize(); + method public int getGnssCapabilities(); method public java.lang.String getLocationControllerExtraPackage(); method public java.lang.String getNetworkProviderPackage(); + method public void injectGnssMeasurementCorrections(android.location.GnssMeasurementCorrections); method public boolean isLocationControllerExtraPackageEnabled(); method public boolean isLocationEnabledForUser(android.os.UserHandle); method public boolean isProviderEnabledForUser(java.lang.String, android.os.UserHandle); @@ -2953,7 +3030,7 @@ package android.media { field public static final int PLAYER_TYPE_UNKNOWN = -1; // 0xffffffff } - public class AudioRecord implements android.media.AudioRouting { + public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting { ctor public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException; } @@ -3441,7 +3518,20 @@ package android.net { ctor public LinkAddress(java.lang.String); } + public final class LinkProperties implements android.os.Parcelable { + ctor public LinkProperties(); + method public boolean addRoute(android.net.RouteInfo); + method public void clear(); + method public void setDnsServers(java.util.Collection<java.net.InetAddress>); + method public void setDomains(java.lang.String); + method public void setHttpProxy(android.net.ProxyInfo); + method public void setInterfaceName(java.lang.String); + method public void setLinkAddresses(java.util.Collection<android.net.LinkAddress>); + method public void setMtu(int); + } + public final class NetworkCapabilities implements android.os.Parcelable { + method public int getSignalStrength(); field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16 } @@ -3461,6 +3551,10 @@ package android.net { method public abstract void onRequestScores(android.net.NetworkKey[]); } + public static class NetworkRequest.Builder { + method public android.net.NetworkRequest.Builder setSignalStrength(int); + } + public class NetworkScoreManager { method public boolean clearScores() throws java.lang.SecurityException; method public void disableScoring() throws java.lang.SecurityException; @@ -5085,6 +5179,16 @@ package android.service.contentcapture { package android.service.euicc { + public final class DownloadSubscriptionResult implements android.os.Parcelable { + ctor public DownloadSubscriptionResult(int, int, int); + method public int describeContents(); + method public int getCardId(); + method public int getResolvableErrors(); + method public int getResult(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.service.euicc.DownloadSubscriptionResult> CREATOR; + } + public final class EuiccProfileInfo implements android.os.Parcelable { method public int describeContents(); method public android.service.carrier.CarrierIdentifier getCarrierIdentifier(); @@ -5138,7 +5242,8 @@ package android.service.euicc { ctor public EuiccService(); method public android.os.IBinder onBind(android.content.Intent); method public abstract int onDeleteSubscription(int, java.lang.String); - method public abstract int onDownloadSubscription(int, android.telephony.euicc.DownloadableSubscription, boolean, boolean); + method public abstract android.service.euicc.DownloadSubscriptionResult onDownloadSubscription(int, android.telephony.euicc.DownloadableSubscription, boolean, boolean, android.os.Bundle); + method public deprecated int onDownloadSubscription(int, android.telephony.euicc.DownloadableSubscription, boolean, boolean); method public abstract int onEraseSubscriptions(int); method public abstract android.service.euicc.GetDefaultDownloadableSubscriptionListResult onGetDefaultDownloadableSubscriptionList(int, boolean); method public abstract android.service.euicc.GetDownloadableSubscriptionMetadataResult onGetDownloadableSubscriptionMetadata(int, android.telephony.euicc.DownloadableSubscription, boolean); @@ -5153,19 +5258,25 @@ package android.service.euicc { field public static final java.lang.String ACTION_BIND_CARRIER_PROVISIONING_SERVICE = "android.service.euicc.action.BIND_CARRIER_PROVISIONING_SERVICE"; field public static final java.lang.String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS = "android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS"; field public static final java.lang.String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.service.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION"; - field public static final java.lang.String ACTION_RESOLVE_CONFIRMATION_CODE = "android.service.euicc.action.RESOLVE_CONFIRMATION_CODE"; + field public static final deprecated java.lang.String ACTION_RESOLVE_CONFIRMATION_CODE = "android.service.euicc.action.RESOLVE_CONFIRMATION_CODE"; field public static final java.lang.String ACTION_RESOLVE_DEACTIVATE_SIM = "android.service.euicc.action.RESOLVE_DEACTIVATE_SIM"; field public static final java.lang.String ACTION_RESOLVE_NO_PRIVILEGES = "android.service.euicc.action.RESOLVE_NO_PRIVILEGES"; + field public static final java.lang.String ACTION_RESOLVE_RESOLVABLE_ERRORS = "android.service.euicc.action.RESOLVE_RESOLVABLE_ERRORS"; field public static final java.lang.String CATEGORY_EUICC_UI = "android.service.euicc.category.EUICC_UI"; field public static final java.lang.String EUICC_SERVICE_INTERFACE = "android.service.euicc.EuiccService"; + field public static final java.lang.String EXTRA_RESOLUTION_ALLOW_POLICY_RULES = "android.service.euicc.extra.RESOLUTION_ALLOW_POLICY_RULES"; field public static final java.lang.String EXTRA_RESOLUTION_CALLING_PACKAGE = "android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE"; field public static final java.lang.String EXTRA_RESOLUTION_CONFIRMATION_CODE = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE"; field public static final java.lang.String EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED"; field public static final java.lang.String EXTRA_RESOLUTION_CONSENT = "android.service.euicc.extra.RESOLUTION_CONSENT"; + field public static final java.lang.String EXTRA_RESOLVABLE_ERRORS = "android.service.euicc.extra.RESOLVABLE_ERRORS"; + field public static final int RESOLVABLE_ERROR_CONFIRMATION_CODE = 1; // 0x1 + field public static final int RESOLVABLE_ERROR_POLICY_RULES = 2; // 0x2 field public static final int RESULT_FIRST_USER = 1; // 0x1 field public static final int RESULT_MUST_DEACTIVATE_SIM = -1; // 0xffffffff - field public static final int RESULT_NEED_CONFIRMATION_CODE = -2; // 0xfffffffe + field public static final deprecated int RESULT_NEED_CONFIRMATION_CODE = -2; // 0xfffffffe field public static final int RESULT_OK = 0; // 0x0 + field public static final int RESULT_RESOLVABLE_ERRORS = -2; // 0xfffffffe } public static abstract class EuiccService.OtaStatusChangedCallback { @@ -6464,12 +6575,17 @@ package android.telephony.euicc { method public void getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent); method public int getOtaStatus(); field public static final java.lang.String ACTION_OTA_STATUS_CHANGED = "android.telephony.euicc.action.OTA_STATUS_CHANGED"; + field public static final java.lang.String ACTION_PROFILE_SELECTION = "android.telephony.euicc.action.PROFILE_SELECTION"; field public static final java.lang.String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION"; + field public static final int EUICC_ACTIVATION_TYPE_BACKUP = 2; // 0x2 + field public static final int EUICC_ACTIVATION_TYPE_DEFAULT = 1; // 0x1 + field public static final int EUICC_ACTIVATION_TYPE_TRANSFER = 3; // 0x3 field public static final int EUICC_OTA_FAILED = 2; // 0x2 field public static final int EUICC_OTA_IN_PROGRESS = 1; // 0x1 field public static final int EUICC_OTA_NOT_NEEDED = 4; // 0x4 field public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5; // 0x5 field public static final int EUICC_OTA_SUCCEEDED = 3; // 0x3 + field public static final java.lang.String EXTRA_ACTIVATION_TYPE = "android.telephony.euicc.extra.ACTIVATION_TYPE"; field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS"; field public static final java.lang.String EXTRA_FORCE_PROVISION = "android.telephony.euicc.extra.FORCE_PROVISION"; } @@ -7553,6 +7669,7 @@ package android.view.contentcapture { method public int getDisplayId(); method public android.os.Bundle getExtras(); method public int getFlags(); + method public android.view.contentcapture.ContentCaptureSessionId getParentSessionId(); method public int getTaskId(); method public android.net.Uri getUri(); field public static final int FLAG_DISABLED_BY_APP = 1; // 0x1 diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp index d7922bc08cea..fe7099b2f3ea 100644 --- a/cmds/idmap2/Android.bp +++ b/cmds/idmap2/Android.bp @@ -12,14 +12,26 @@ // See the License for the specific language governing permissions and // limitations under the License. -cc_library { - name: "libidmap2", - host_supported: true, +cc_defaults { + name: "idmap2_defaults", tidy: true, + tidy_checks: [ + "android-*", + "misc-*", + "modernize-*", + "readability-*", + ], tidy_flags: [ "-system-headers", -// b/120024673 "-warnings-as-errors=*", ], +} + +cc_library { + name: "libidmap2", + defaults: [ + "idmap2_defaults", + ], + host_supported: true, srcs: [ "libidmap2/BinaryStreamVisitor.cpp", "libidmap2/CommandLineOptions.cpp", @@ -60,12 +72,13 @@ cc_library { cc_test { name: "idmap2_tests", - host_supported: true, - tidy: true, - tidy_flags: [ - "-system-headers", -// b/120024673 "-warnings-as-errors=*", + defaults: [ + "idmap2_defaults", + ], + tidy_checks: [ + "-readability-magic-numbers", ], + host_supported: true, srcs: [ "tests/BinaryStreamVisitorTests.cpp", "tests/CommandLineOptionsTests.cpp", @@ -114,12 +127,10 @@ cc_test { cc_binary { name: "idmap2", - host_supported: true, - tidy: true, - tidy_flags: [ - "-system-headers", -// b/120024673 "-warnings-as-errors=*", + defaults: [ + "idmap2_defaults", ], + host_supported: true, srcs: [ "idmap2/Create.cpp", "idmap2/Dump.cpp", @@ -156,19 +167,11 @@ cc_binary { cc_binary { name: "idmap2d", - host_supported: false, - tidy: true, - tidy_checks: [ - // remove google-default-arguments or clang-tidy will complain about - // the auto-generated file IIdmap2.cpp - "-google-default-arguments", - ], - tidy_flags: [ - "-system-headers", -// b/120024673 "-warnings-as-errors=*", + defaults: [ + "idmap2_defaults", ], + host_supported: false, srcs: [ - ":idmap2_aidl", "idmap2d/Idmap2Service.cpp", "idmap2d/Main.cpp", ], @@ -181,9 +184,30 @@ cc_binary { "libutils", "libziparchive", ], + static_libs: [ + "libidmap2daidl", + ], init_rc: ["idmap2d/idmap2d.rc"], } +cc_library_static { + name: "libidmap2daidl", + defaults: [ + "idmap2_defaults", + ], + tidy: false, + host_supported: false, + srcs: [ + ":idmap2_aidl", + ], + shared_libs: [ + "libbase", + ], + aidl: { + export_aidl_headers: true, + }, +} + filegroup { name: "idmap2_aidl", srcs: [ diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp index 291eaeb9c211..b07567331e85 100644 --- a/cmds/idmap2/idmap2/Create.cpp +++ b/cmds/idmap2/idmap2/Create.cpp @@ -32,9 +32,12 @@ using android::ApkAssets; using android::idmap2::BinaryStreamVisitor; using android::idmap2::CommandLineOptions; using android::idmap2::Idmap; +using android::idmap2::utils::kIdmapFilePermissionMask; bool Create(const std::vector<std::string>& args, std::ostream& out_error) { - std::string target_apk_path, overlay_apk_path, idmap_path; + std::string target_apk_path; + std::string overlay_apk_path; + std::string idmap_path; const CommandLineOptions opts = CommandLineOptions("idmap2 create") @@ -68,7 +71,7 @@ bool Create(const std::vector<std::string>& args, std::ostream& out_error) { return false; } - umask(0133); // u=rw,g=r,o=r + umask(kIdmapFilePermissionMask); std::ofstream fout(idmap_path); if (fout.fail()) { out_error << "failed to open idmap path " << idmap_path << std::endl; diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp index 8d0cee5b938d..cfb5dd5b30a9 100644 --- a/cmds/idmap2/idmap2/Lookup.cpp +++ b/cmds/idmap2/idmap2/Lookup.cpp @@ -63,10 +63,12 @@ namespace { Result<ResourceId> WARN_UNUSED ParseResReference(const AssetManager2& am, const std::string& res, const std::string& fallback_package) { + static constexpr const int kBaseHex = 16; + // first, try to parse as a hex number char* endptr = nullptr; ResourceId resid; - resid = strtol(res.c_str(), &endptr, 16); + resid = strtol(res.c_str(), &endptr, kBaseHex); if (*endptr == '\0') { return {resid}; } @@ -155,7 +157,9 @@ Result<std::string> GetTargetPackageNameFromManifest(const std::string& apk_path bool Lookup(const std::vector<std::string>& args, std::ostream& out_error) { std::vector<std::string> idmap_paths; - std::string config_str, resid_str; + std::string config_str; + std::string resid_str; + const CommandLineOptions opts = CommandLineOptions("idmap2 lookup") .MandatoryOption("--idmap-path", "input: path to idmap file to load", &idmap_paths) diff --git a/cmds/idmap2/idmap2/Main.cpp b/cmds/idmap2/idmap2/Main.cpp index 5d9ea778915a..4012555235a8 100644 --- a/cmds/idmap2/idmap2/Main.cpp +++ b/cmds/idmap2/idmap2/Main.cpp @@ -29,10 +29,12 @@ using android::idmap2::CommandLineOptions; -typedef std::map<std::string, std::function<int(const std::vector<std::string>&, std::ostream&)>> +typedef std::map<std::string, std::function<bool(const std::vector<std::string>&, std::ostream&)>> NameToFunctionMap; -static void PrintUsage(const NameToFunctionMap& commands, std::ostream& out) { +namespace { + +void PrintUsage(const NameToFunctionMap& commands, std::ostream& out) { out << "usage: idmap2 ["; for (auto iter = commands.cbegin(); iter != commands.cend(); iter++) { if (iter != commands.cbegin()) { @@ -43,6 +45,8 @@ static void PrintUsage(const NameToFunctionMap& commands, std::ostream& out) { out << "]" << std::endl; } +} // namespace + int main(int argc, char** argv) { const NameToFunctionMap commands = { {"create", Create}, {"dump", Dump}, {"lookup", Lookup}, {"scan", Scan}, {"verify", Verify}, diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp index 00c49e38e8b2..ef560d1ee710 100644 --- a/cmds/idmap2/idmap2/Scan.cpp +++ b/cmds/idmap2/idmap2/Scan.cpp @@ -44,7 +44,7 @@ std::unique_ptr<std::vector<std::string>> FindApkFiles(const std::vector<std::st const auto predicate = [](unsigned char type, const std::string& path) -> bool { static constexpr size_t kExtLen = 4; // strlen(".apk") return type == DT_REG && path.size() > kExtLen && - !path.compare(path.size() - kExtLen, kExtLen, ".apk"); + path.compare(path.size() - kExtLen, kExtLen, ".apk") == 0; }; // pass apk paths through a set to filter out duplicates std::set<std::string> paths; @@ -63,7 +63,9 @@ std::unique_ptr<std::vector<std::string>> FindApkFiles(const std::vector<std::st bool Scan(const std::vector<std::string>& args, std::ostream& out_error) { std::vector<std::string> input_directories; - std::string target_package_name, target_apk_path, output_directory; + std::string target_package_name; + std::string target_apk_path; + std::string output_directory; bool recursive = false; const CommandLineOptions opts = @@ -112,7 +114,7 @@ bool Scan(const std::vector<std::string>& args, std::ostream& out_error) { } auto iter = tag->find("isStatic"); - if (iter == tag->end() || std::stoul(iter->second) == 0u) { + if (iter == tag->end() || std::stoul(iter->second) == 0U) { continue; } diff --git a/cmds/idmap2/idmap2/Verify.cpp b/cmds/idmap2/idmap2/Verify.cpp index b5fa438b5b9f..4d4a0e769174 100644 --- a/cmds/idmap2/idmap2/Verify.cpp +++ b/cmds/idmap2/idmap2/Verify.cpp @@ -27,6 +27,7 @@ using android::idmap2::IdmapHeader; bool Verify(const std::vector<std::string>& args, std::ostream& out_error) { std::string idmap_path; + const CommandLineOptions opts = CommandLineOptions("idmap2 verify") .MandatoryOption("--idmap-path", "input: path to idmap file to verify", &idmap_path); diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp index 86b00f1d6e95..7b16093434fb 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.cpp +++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp @@ -39,10 +39,11 @@ using android::binder::Status; using android::idmap2::BinaryStreamVisitor; using android::idmap2::Idmap; using android::idmap2::IdmapHeader; +using android::idmap2::utils::kIdmapFilePermissionMask; namespace { -static constexpr const char* kIdmapCacheDir = "/data/resource-cache"; +constexpr const char* kIdmapCacheDir = "/data/resource-cache"; Status ok() { return Status::ok(); @@ -69,13 +70,12 @@ Status Idmap2Service::removeIdmap(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); - if (unlink(idmap_path.c_str()) == 0) { - *_aidl_return = true; - return ok(); - } else { + if (unlink(idmap_path.c_str()) != 0) { *_aidl_return = false; return error("failed to unlink " + idmap_path + ": " + strerror(errno)); } + *_aidl_return = true; + return ok(); } Status Idmap2Service::verifyIdmap(const std::string& overlay_apk_path, @@ -119,7 +119,7 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path, return error(err.str()); } - umask(0133); // u=rw,g=r,o=r + umask(kIdmapFilePermissionMask); const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path); std::ofstream fout(idmap_path); if (fout.fail()) { diff --git a/cmds/idmap2/idmap2d/Main.cpp b/cmds/idmap2/idmap2d/Main.cpp index d64a87b8ee15..4393dcc130ec 100644 --- a/cmds/idmap2/idmap2d/Main.cpp +++ b/cmds/idmap2/idmap2d/Main.cpp @@ -37,7 +37,7 @@ using android::status_t; using android::os::Idmap2Service; int main(int argc ATTRIBUTE_UNUSED, char** argv ATTRIBUTE_UNUSED) { - IPCThreadState::self()->disableBackgroundScheduling(true); + IPCThreadState::disableBackgroundScheduling(true); status_t ret = BinderService<Idmap2Service>::publish(); if (ret != android::OK) { return EXIT_FAILURE; diff --git a/cmds/idmap2/include/idmap2/FileUtils.h b/cmds/idmap2/include/idmap2/FileUtils.h index 05c6d31d395d..84cc69a95ac5 100644 --- a/cmds/idmap2/include/idmap2/FileUtils.h +++ b/cmds/idmap2/include/idmap2/FileUtils.h @@ -25,6 +25,9 @@ namespace android { namespace idmap2 { namespace utils { + +constexpr const mode_t kIdmapFilePermissionMask = 0133; // u=rw,g=r,o=r + typedef std::function<bool(unsigned char type /* DT_* from dirent.h */, const std::string& path)> FindFilesPredicate; std::unique_ptr<std::vector<std::string>> FindFiles(const std::string& root, bool recurse, diff --git a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp index 29969a23250b..b7765bc3d836 100644 --- a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp +++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp @@ -64,15 +64,15 @@ void BinaryStreamVisitor::visit(const IdmapData::Header& header) { Write16(header.GetTypeCount()); } -void BinaryStreamVisitor::visit(const IdmapData::TypeEntry& te) { - const uint16_t entryCount = te.GetEntryCount(); +void BinaryStreamVisitor::visit(const IdmapData::TypeEntry& type_entry) { + const uint16_t entryCount = type_entry.GetEntryCount(); - Write16(te.GetTargetTypeId()); - Write16(te.GetOverlayTypeId()); + Write16(type_entry.GetTargetTypeId()); + Write16(type_entry.GetOverlayTypeId()); Write16(entryCount); - Write16(te.GetEntryOffset()); + Write16(type_entry.GetEntryOffset()); for (uint16_t i = 0; i < entryCount; i++) { - EntryId entry_id = te.GetEntry(i); + EntryId entry_id = type_entry.GetEntry(i); Write32(entry_id != kNoEntry ? static_cast<uint32_t>(entry_id) : kPadding); } } diff --git a/cmds/idmap2/libidmap2/FileUtils.cpp b/cmds/idmap2/libidmap2/FileUtils.cpp index 4ac4c04d0bfc..88d40d1f523d 100644 --- a/cmds/idmap2/libidmap2/FileUtils.cpp +++ b/cmds/idmap2/libidmap2/FileUtils.cpp @@ -34,12 +34,12 @@ namespace utils { std::unique_ptr<std::vector<std::string>> FindFiles(const std::string& root, bool recurse, const FindFilesPredicate& predicate) { DIR* dir = opendir(root.c_str()); - if (!dir) { + if (dir == nullptr) { return nullptr; } std::unique_ptr<std::vector<std::string>> vector(new std::vector<std::string>()); struct dirent* dirent; - while ((dirent = readdir(dir))) { + while ((dirent = readdir(dir)) != nullptr) { const std::string path = root + "/" + dirent->d_name; if (predicate(dirent->d_type, path)) { vector->push_back(path); @@ -68,8 +68,10 @@ std::unique_ptr<std::string> ReadFile(const std::string& path) { } std::unique_ptr<std::string> ReadFile(int fd) { + static constexpr const size_t kBufSize = 1024; + std::unique_ptr<std::string> str(new std::string()); - char buf[1024]; + char buf[kBufSize]; ssize_t r; while ((r = read(fd, buf, sizeof(buf))) > 0) { str->append(buf, r); diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp index 1ef326793cb4..5822745a95c3 100644 --- a/cmds/idmap2/libidmap2/Idmap.cpp +++ b/cmds/idmap2/libidmap2/Idmap.cpp @@ -39,24 +39,32 @@ namespace android { namespace idmap2 { +namespace { + #define EXTRACT_TYPE(resid) ((0x00ff0000 & (resid)) >> 16) #define EXTRACT_ENTRY(resid) (0x0000ffff & (resid)) -struct MatchingResources { +class MatchingResources { + public: void Add(ResourceId target_resid, ResourceId overlay_resid) { TypeId target_typeid = EXTRACT_TYPE(target_resid); - if (map.find(target_typeid) == map.end()) { - map.emplace(target_typeid, std::set<std::pair<ResourceId, ResourceId>>()); + if (map_.find(target_typeid) == map_.end()) { + map_.emplace(target_typeid, std::set<std::pair<ResourceId, ResourceId>>()); } - map[target_typeid].insert(std::make_pair(target_resid, overlay_resid)); + map_[target_typeid].insert(std::make_pair(target_resid, overlay_resid)); + } + + inline const std::map<TypeId, std::set<std::pair<ResourceId, ResourceId>>>& Map() const { + return map_; } + private: // target type id -> set { pair { overlay entry id, overlay entry id } } - std::map<TypeId, std::set<std::pair<ResourceId, ResourceId>>> map; + std::map<TypeId, std::set<std::pair<ResourceId, ResourceId>>> map_; }; -static bool WARN_UNUSED Read16(std::istream& stream, uint16_t* out) { +bool WARN_UNUSED Read16(std::istream& stream, uint16_t* out) { uint16_t value; if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint16_t))) { *out = dtohl(value); @@ -65,7 +73,7 @@ static bool WARN_UNUSED Read16(std::istream& stream, uint16_t* out) { return false; } -static bool WARN_UNUSED Read32(std::istream& stream, uint32_t* out) { +bool WARN_UNUSED Read32(std::istream& stream, uint32_t* out) { uint32_t value; if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint32_t))) { *out = dtohl(value); @@ -75,7 +83,7 @@ static bool WARN_UNUSED Read32(std::istream& stream, uint32_t* out) { } // a string is encoded as a kIdmapStringLength char array; the array is always null-terminated -static bool WARN_UNUSED ReadString(std::istream& stream, char out[kIdmapStringLength]) { +bool WARN_UNUSED ReadString(std::istream& stream, char out[kIdmapStringLength]) { char buf[kIdmapStringLength]; memset(buf, 0, sizeof(buf)); if (!stream.read(buf, sizeof(buf))) { @@ -88,7 +96,7 @@ static bool WARN_UNUSED ReadString(std::istream& stream, char out[kIdmapStringLe return true; } -static ResourceId NameToResid(const AssetManager2& am, const std::string& name) { +ResourceId NameToResid(const AssetManager2& am, const std::string& name) { return am.GetResourceId(name); } @@ -101,7 +109,7 @@ static ResourceId NameToResid(const AssetManager2& am, const std::string& name) // relying on a hard-coded index. This however requires storing the package name in the idmap // header, which in turn requires incrementing the idmap version. Because the initial version of // idmap2 is compatible with idmap, this will have to wait for now. -static const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) { +const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) { const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc.GetPackages(); if (packages.empty()) { return nullptr; @@ -110,6 +118,8 @@ static const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) { return loaded_arsc.GetPackageById(id); } +} // namespace + std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& stream) { std::unique_ptr<IdmapHeader> idmap_header(new IdmapHeader()); @@ -196,8 +206,9 @@ std::unique_ptr<const IdmapData::Header> IdmapData::Header::FromBinaryStream(std std::unique_ptr<const IdmapData::TypeEntry> IdmapData::TypeEntry::FromBinaryStream( std::istream& stream) { std::unique_ptr<IdmapData::TypeEntry> data(new IdmapData::TypeEntry()); - - uint16_t target_type16, overlay_type16, entry_count; + uint16_t target_type16; + uint16_t overlay_type16; + uint16_t entry_count; if (!Read16(stream, &target_type16) || !Read16(stream, &overlay_type16) || !Read16(stream, &entry_count) || !Read16(stream, &data->entry_offset_)) { return nullptr; @@ -282,25 +293,25 @@ std::unique_ptr<const Idmap> Idmap::FromApkAssets(const std::string& target_apk_ } const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc(); - if (!target_arsc) { + if (target_arsc == nullptr) { out_error << "error: failed to load target resources.arsc" << std::endl; return nullptr; } const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc(); - if (!overlay_arsc) { + if (overlay_arsc == nullptr) { out_error << "error: failed to load overlay resources.arsc" << std::endl; return nullptr; } const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc); - if (!target_pkg) { + if (target_pkg == nullptr) { out_error << "error: failed to load target package from resources.arsc" << std::endl; return nullptr; } const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc); - if (!overlay_pkg) { + if (overlay_pkg == nullptr) { out_error << "error: failed to load overlay package from resources.arsc" << std::endl; return nullptr; } @@ -375,8 +386,8 @@ std::unique_ptr<const Idmap> Idmap::FromApkAssets(const std::string& target_apk_ // encode idmap data std::unique_ptr<IdmapData> data(new IdmapData()); - const auto types_end = matching_resources.map.cend(); - for (auto ti = matching_resources.map.cbegin(); ti != types_end; ++ti) { + const auto types_end = matching_resources.Map().cend(); + for (auto ti = matching_resources.Map().cbegin(); ti != types_end; ++ti) { auto ei = ti->second.cbegin(); std::unique_ptr<IdmapData::TypeEntry> type(new IdmapData::TypeEntry()); type->target_type_id_ = EXTRACT_TYPE(ei->first); diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp index fb3bc5ba9ae8..b36df24a0da7 100644 --- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp +++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp @@ -49,17 +49,18 @@ void PrettyPrintVisitor::visit(const IdmapData::Header& header ATTRIBUTE_UNUSED) last_seen_package_id_ = header.GetTargetPackageId(); } -void PrettyPrintVisitor::visit(const IdmapData::TypeEntry& te) { +void PrettyPrintVisitor::visit(const IdmapData::TypeEntry& type_entry) { const bool target_package_loaded = !target_am_.GetApkAssets().empty(); - for (uint16_t i = 0; i < te.GetEntryCount(); i++) { - const EntryId entry = te.GetEntry(i); + for (uint16_t i = 0; i < type_entry.GetEntryCount(); i++) { + const EntryId entry = type_entry.GetEntry(i); if (entry == kNoEntry) { continue; } 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); + RESID(last_seen_package_id_, type_entry.GetTargetTypeId(), type_entry.GetEntryOffset() + i); + const ResourceId overlay_resid = + RESID(last_seen_package_id_, type_entry.GetOverlayTypeId(), entry); stream_ << base::StringPrintf("0x%08x -> 0x%08x", target_resid, overlay_resid); if (target_package_loaded) { diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp index 7c24445ef902..a6bf5fb6e687 100644 --- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp +++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp @@ -59,22 +59,23 @@ void RawPrintVisitor::visit(const IdmapData::Header& header) { last_seen_package_id_ = header.GetTargetPackageId(); } -void RawPrintVisitor::visit(const IdmapData::TypeEntry& te) { +void RawPrintVisitor::visit(const IdmapData::TypeEntry& type_entry) { const bool target_package_loaded = !target_am_.GetApkAssets().empty(); - print(static_cast<uint16_t>(te.GetTargetTypeId()), "target type"); - print(static_cast<uint16_t>(te.GetOverlayTypeId()), "overlay type"); - print(static_cast<uint16_t>(te.GetEntryCount()), "entry count"); - print(static_cast<uint16_t>(te.GetEntryOffset()), "entry offset"); + print(static_cast<uint16_t>(type_entry.GetTargetTypeId()), "target type"); + print(static_cast<uint16_t>(type_entry.GetOverlayTypeId()), "overlay type"); + print(static_cast<uint16_t>(type_entry.GetEntryCount()), "entry count"); + print(static_cast<uint16_t>(type_entry.GetEntryOffset()), "entry offset"); - for (uint16_t i = 0; i < te.GetEntryCount(); i++) { - const EntryId entry = te.GetEntry(i); + for (uint16_t i = 0; i < type_entry.GetEntryCount(); i++) { + const EntryId entry = type_entry.GetEntry(i); if (entry == kNoEntry) { print(kPadding, "no entry"); } else { - 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); + const ResourceId target_resid = RESID(last_seen_package_id_, type_entry.GetTargetTypeId(), + type_entry.GetEntryOffset() + i); + const ResourceId overlay_resid = + RESID(last_seen_package_id_, type_entry.GetOverlayTypeId(), entry); Result<std::string> name; if (target_package_loaded) { name = utils::ResToTypeEntryName(target_am_, target_resid); diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp index 8b552dcc1265..3b9dbe97e894 100644 --- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp +++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp @@ -29,7 +29,6 @@ #include "TestHelpers.h" -using ::testing::IsNull; using ::testing::NotNull; namespace android { @@ -52,14 +51,14 @@ TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) { ASSERT_EQ(idmap1->GetHeader()->GetTargetCrc(), idmap2->GetHeader()->GetTargetCrc()); ASSERT_EQ(idmap1->GetHeader()->GetTargetPath(), idmap2->GetHeader()->GetTargetPath()); - ASSERT_EQ(idmap1->GetData().size(), 1u); + ASSERT_EQ(idmap1->GetData().size(), 1U); ASSERT_EQ(idmap1->GetData().size(), idmap2->GetData().size()); const auto& data1 = idmap1->GetData()[0]; const auto& data2 = idmap2->GetData()[0]; ASSERT_EQ(data1->GetHeader()->GetTargetPackageId(), data2->GetHeader()->GetTargetPackageId()); - ASSERT_EQ(data1->GetTypeEntries().size(), 2u); + ASSERT_EQ(data1->GetTypeEntries().size(), 2U); ASSERT_EQ(data1->GetTypeEntries().size(), data2->GetTypeEntries().size()); ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(0), data2->GetTypeEntries()[0]->GetEntry(0)); ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(1), data2->GetTypeEntries()[0]->GetEntry(1)); diff --git a/cmds/idmap2/tests/CommandLineOptionsTests.cpp b/cmds/idmap2/tests/CommandLineOptionsTests.cpp index b04b25660ee4..243d23a0f2db 100644 --- a/cmds/idmap2/tests/CommandLineOptionsTests.cpp +++ b/cmds/idmap2/tests/CommandLineOptionsTests.cpp @@ -38,14 +38,12 @@ #include "TestHelpers.h" -using ::testing::NotNull; - namespace android { namespace idmap2 { TEST(CommandLineOptionsTests, Flag) { - bool foo = true, bar = false; - + bool foo = true; + bool bar = false; CommandLineOptions opts = CommandLineOptions("test").OptionalFlag("--foo", "", &foo).OptionalFlag("--bar", "", &bar); @@ -63,7 +61,8 @@ TEST(CommandLineOptionsTests, Flag) { } TEST(CommandLineOptionsTests, MandatoryOption) { - std::string foo, bar; + std::string foo; + std::string bar; CommandLineOptions opts = CommandLineOptions("test") .MandatoryOption("--foo", "", &foo) .MandatoryOption("--bar", "", &bar); @@ -92,13 +91,14 @@ TEST(CommandLineOptionsTests, MandatoryOptionMultipleArgsAndExpectedOnceOrMore) std::ostream fakeStdErr(nullptr); bool success = opts.Parse({"--foo", "FOO", "--foo", "BAR"}, fakeStdErr); ASSERT_TRUE(success); - ASSERT_EQ(args.size(), 2u); + ASSERT_EQ(args.size(), 2U); ASSERT_EQ(args[0], "FOO"); ASSERT_EQ(args[1], "BAR"); } TEST(CommandLineOptionsTests, OptionalOption) { - std::string foo, bar; + std::string foo; + std::string bar; CommandLineOptions opts = CommandLineOptions("test") .OptionalOption("--foo", "", &foo) .OptionalOption("--bar", "", &bar); @@ -123,7 +123,8 @@ TEST(CommandLineOptionsTests, OptionalOption) { } TEST(CommandLineOptionsTests, CornerCases) { - std::string foo, bar; + std::string foo; + std::string bar; bool baz = false; CommandLineOptions opts = CommandLineOptions("test") .MandatoryOption("--foo", "", &foo) @@ -150,7 +151,7 @@ TEST(CommandLineOptionsTests, ConvertArgvToVector) { nullptr, }; std::unique_ptr<std::vector<std::string>> v = CommandLineOptions::ConvertArgvToVector(3, argv); - ASSERT_EQ(v->size(), 2ul); + ASSERT_EQ(v->size(), 2UL); ASSERT_EQ((*v)[0], "--foo"); ASSERT_EQ((*v)[1], "FOO"); } @@ -161,12 +162,16 @@ TEST(CommandLineOptionsTests, ConvertArgvToVectorNoArgs) { nullptr, }; std::unique_ptr<std::vector<std::string>> v = CommandLineOptions::ConvertArgvToVector(1, argv); - ASSERT_EQ(v->size(), 0ul); + ASSERT_EQ(v->size(), 0UL); } TEST(CommandLineOptionsTests, Usage) { - std::string arg1, arg2, arg3, arg4; - bool arg5 = false, arg6 = false; + std::string arg1; + std::string arg2; + std::string arg3; + std::string arg4; + bool arg5 = false; + bool arg6 = false; std::vector<std::string> arg7; CommandLineOptions opts = CommandLineOptions("test") .MandatoryOption("--aa", "description-aa", &arg1) diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp index 0c6439ab8c0c..6584ee32a509 100644 --- a/cmds/idmap2/tests/FileUtilsTests.cpp +++ b/cmds/idmap2/tests/FileUtilsTests.cpp @@ -39,7 +39,7 @@ TEST(FileUtilsTests, FindFilesFindEverythingNonRecursive) { [](unsigned char type ATTRIBUTE_UNUSED, const std::string& path ATTRIBUTE_UNUSED) -> bool { return true; }); ASSERT_THAT(v, NotNull()); - ASSERT_EQ(v->size(), 4u); + ASSERT_EQ(v->size(), 4U); ASSERT_EQ( std::set<std::string>(v->begin(), v->end()), std::set<std::string>({root + "/.", root + "/..", root + "/overlay", root + "/target"})); @@ -48,10 +48,10 @@ TEST(FileUtilsTests, FindFilesFindEverythingNonRecursive) { TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) { const auto& root = GetTestDataPath(); auto v = utils::FindFiles(root, true, [](unsigned char type, const std::string& path) -> bool { - return type == DT_REG && path.size() > 4 && !path.compare(path.size() - 4, 4, ".apk"); + return type == DT_REG && path.size() > 4 && path.compare(path.size() - 4, 4, ".apk") == 0; }); ASSERT_THAT(v, NotNull()); - ASSERT_EQ(v->size(), 4u); + ASSERT_EQ(v->size(), 4U); ASSERT_EQ(std::set<std::string>(v->begin(), v->end()), std::set<std::string>({root + "/target/target.apk", root + "/overlay/overlay.apk", root + "/overlay/overlay-static-1.apk", diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp index 5c4e8576985b..255f3c102b85 100644 --- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp +++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp @@ -51,15 +51,17 @@ namespace idmap2 { class Idmap2BinaryTests : public Idmap2Tests {}; -static void AssertIdmap(const Idmap& idmap, const std::string& target_apk_path, - const std::string& overlay_apk_path) { +namespace { + +void AssertIdmap(const Idmap& idmap, const std::string& target_apk_path, + const std::string& overlay_apk_path) { // check that the idmap file looks reasonable (IdmapTests is responsible for // more in-depth verification) ASSERT_EQ(idmap.GetHeader()->GetMagic(), kIdmapMagic); ASSERT_EQ(idmap.GetHeader()->GetVersion(), kIdmapCurrentVersion); ASSERT_EQ(idmap.GetHeader()->GetTargetPath(), target_apk_path); ASSERT_EQ(idmap.GetHeader()->GetOverlayPath(), overlay_apk_path); - ASSERT_EQ(idmap.GetData().size(), 1u); + ASSERT_EQ(idmap.GetData().size(), 1U); } #define ASSERT_IDMAP(idmap_ref, target_apk_path, overlay_apk_path) \ @@ -67,6 +69,8 @@ static void AssertIdmap(const Idmap& idmap, const std::string& target_apk_path, ASSERT_NO_FATAL_FAILURE(AssertIdmap(idmap_ref, target_apk_path, overlay_apk_path)); \ } while (0) +} // namespace + TEST_F(Idmap2BinaryTests, Create) { // clang-format off auto result = ExecuteBinary({"idmap2", diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp index 0379aa491682..dc80e0e145ac 100644 --- a/cmds/idmap2/tests/IdmapTests.cpp +++ b/cmds/idmap2/tests/IdmapTests.cpp @@ -50,10 +50,10 @@ TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) { std::istringstream stream(raw); std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream); ASSERT_THAT(header, NotNull()); - ASSERT_EQ(header->GetMagic(), 0x504d4449u); - ASSERT_EQ(header->GetVersion(), 0x01u); - ASSERT_EQ(header->GetTargetCrc(), 0x1234u); - ASSERT_EQ(header->GetOverlayCrc(), 0x5678u); + ASSERT_EQ(header->GetMagic(), 0x504d4449U); + ASSERT_EQ(header->GetVersion(), 0x01U); + ASSERT_EQ(header->GetTargetCrc(), 0x1234U); + ASSERT_EQ(header->GetOverlayCrc(), 0x5678U); ASSERT_EQ(header->GetTargetPath().to_string(), "target.apk"); ASSERT_EQ(header->GetOverlayPath().to_string(), "overlay.apk"); } @@ -77,8 +77,8 @@ TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) { std::unique_ptr<const IdmapData::Header> header = IdmapData::Header::FromBinaryStream(stream); ASSERT_THAT(header, NotNull()); - ASSERT_EQ(header->GetTargetPackageId(), 0x7fu); - ASSERT_EQ(header->GetTypeCount(), 2u); + ASSERT_EQ(header->GetTargetPackageId(), 0x7fU); + ASSERT_EQ(header->GetTypeCount(), 2U); } TEST(IdmapTests, CreateIdmapDataResourceTypeFromBinaryStream) { @@ -89,11 +89,11 @@ TEST(IdmapTests, CreateIdmapDataResourceTypeFromBinaryStream) { std::unique_ptr<const IdmapData::TypeEntry> data = IdmapData::TypeEntry::FromBinaryStream(stream); ASSERT_THAT(data, NotNull()); - ASSERT_EQ(data->GetTargetTypeId(), 0x02u); - ASSERT_EQ(data->GetOverlayTypeId(), 0x02u); - ASSERT_EQ(data->GetEntryCount(), 1u); - ASSERT_EQ(data->GetEntryOffset(), 0u); - ASSERT_EQ(data->GetEntry(0), 0u); + ASSERT_EQ(data->GetTargetTypeId(), 0x02U); + ASSERT_EQ(data->GetOverlayTypeId(), 0x02U); + ASSERT_EQ(data->GetEntryCount(), 1U); + ASSERT_EQ(data->GetEntryOffset(), 0U); + ASSERT_EQ(data->GetEntry(0), 0U); } TEST(IdmapTests, CreateIdmapDataFromBinaryStream) { @@ -104,24 +104,24 @@ TEST(IdmapTests, CreateIdmapDataFromBinaryStream) { std::unique_ptr<const IdmapData> data = IdmapData::FromBinaryStream(stream); ASSERT_THAT(data, NotNull()); - ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fu); - ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2u); + ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU); + ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2U); const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries(); - ASSERT_EQ(types.size(), 2u); - - ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02u); - ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02u); - ASSERT_EQ(types[0]->GetEntryCount(), 1u); - ASSERT_EQ(types[0]->GetEntryOffset(), 0u); - ASSERT_EQ(types[0]->GetEntry(0), 0x0000u); - - ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03u); - ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03u); - ASSERT_EQ(types[1]->GetEntryCount(), 3u); - ASSERT_EQ(types[1]->GetEntryOffset(), 3u); - ASSERT_EQ(types[1]->GetEntry(0), 0x0000u); + ASSERT_EQ(types.size(), 2U); + + ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U); + ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02U); + ASSERT_EQ(types[0]->GetEntryCount(), 1U); + ASSERT_EQ(types[0]->GetEntryOffset(), 0U); + ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); + + ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03U); + ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03U); + ASSERT_EQ(types[1]->GetEntryCount(), 3U); + ASSERT_EQ(types[1]->GetEntryOffset(), 3U); + ASSERT_EQ(types[1]->GetEntry(0), 0x0000U); ASSERT_EQ(types[1]->GetEntry(1), kNoEntry); - ASSERT_EQ(types[1]->GetEntry(2), 0x0001u); + ASSERT_EQ(types[1]->GetEntry(2), 0x0001U); } TEST(IdmapTests, CreateIdmapFromBinaryStream) { @@ -133,35 +133,35 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) { ASSERT_THAT(idmap, NotNull()); ASSERT_THAT(idmap->GetHeader(), NotNull()); - ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449u); - ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01u); - ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234u); - ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678u); + ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U); + ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U); + ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U); + ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U); ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), "target.apk"); ASSERT_EQ(idmap->GetHeader()->GetOverlayPath().to_string(), "overlay.apk"); const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData(); - ASSERT_EQ(dataBlocks.size(), 1u); + ASSERT_EQ(dataBlocks.size(), 1U); const std::unique_ptr<const IdmapData>& data = dataBlocks[0]; - ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fu); - ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2u); + ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU); + ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2U); const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries(); - ASSERT_EQ(types.size(), 2u); - - ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02u); - ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02u); - ASSERT_EQ(types[0]->GetEntryCount(), 1u); - ASSERT_EQ(types[0]->GetEntryOffset(), 0u); - ASSERT_EQ(types[0]->GetEntry(0), 0x0000u); - - ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03u); - ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03u); - ASSERT_EQ(types[1]->GetEntryCount(), 3u); - ASSERT_EQ(types[1]->GetEntryOffset(), 3u); - ASSERT_EQ(types[1]->GetEntry(0), 0x0000u); + ASSERT_EQ(types.size(), 2U); + + ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U); + ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02U); + ASSERT_EQ(types[0]->GetEntryCount(), 1U); + ASSERT_EQ(types[0]->GetEntryOffset(), 0U); + ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); + + ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03U); + ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03U); + ASSERT_EQ(types[1]->GetEntryCount(), 3U); + ASSERT_EQ(types[1]->GetEntryOffset(), 3U); + ASSERT_EQ(types[1]->GetEntry(0), 0x0000U); ASSERT_EQ(types[1]->GetEntry(1), kNoEntry); - ASSERT_EQ(types[1]->GetEntry(2), 0x0001u); + ASSERT_EQ(types[1]->GetEntry(2), 0x0001U); } TEST(IdmapTests, GracefullyFailToCreateIdmapFromCorruptBinaryStream) { @@ -189,8 +189,8 @@ TEST(IdmapTests, CreateIdmapFromApkAssets) { ASSERT_THAT(idmap, NotNull()); ASSERT_THAT(idmap->GetHeader(), NotNull()); - ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449u); - ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01u); + ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U); + ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U); ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xf5ad1d1d); ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xd470336b); ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path); @@ -198,30 +198,30 @@ TEST(IdmapTests, CreateIdmapFromApkAssets) { ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path); const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData(); - ASSERT_EQ(dataBlocks.size(), 1u); + ASSERT_EQ(dataBlocks.size(), 1U); const std::unique_ptr<const IdmapData>& data = dataBlocks[0]; - ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fu); - ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2u); + ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU); + ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2U); const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries(); - ASSERT_EQ(types.size(), 2u); - - ASSERT_EQ(types[0]->GetTargetTypeId(), 0x01u); - ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01u); - ASSERT_EQ(types[0]->GetEntryCount(), 1u); - ASSERT_EQ(types[0]->GetEntryOffset(), 0u); - ASSERT_EQ(types[0]->GetEntry(0), 0x0000u); - - ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02u); - ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02u); - ASSERT_EQ(types[1]->GetEntryCount(), 4u); - ASSERT_EQ(types[1]->GetEntryOffset(), 3u); - ASSERT_EQ(types[1]->GetEntry(0), 0x0000u); + ASSERT_EQ(types.size(), 2U); + + ASSERT_EQ(types[0]->GetTargetTypeId(), 0x01U); + ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U); + ASSERT_EQ(types[0]->GetEntryCount(), 1U); + ASSERT_EQ(types[0]->GetEntryOffset(), 0U); + ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); + + ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02U); + ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02U); + ASSERT_EQ(types[1]->GetEntryCount(), 4U); + ASSERT_EQ(types[1]->GetEntryOffset(), 3U); + ASSERT_EQ(types[1]->GetEntry(0), 0x0000U); ASSERT_EQ(types[1]->GetEntry(1), kNoEntry); - ASSERT_EQ(types[1]->GetEntry(2), 0x0001u); - ASSERT_EQ(types[1]->GetEntry(3), 0x0002u); + ASSERT_EQ(types[1]->GetEntry(2), 0x0001U); + ASSERT_EQ(types[1]->GetEntry(3), 0x0002U); } TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) { diff --git a/cmds/idmap2/tests/Main.cpp b/cmds/idmap2/tests/Main.cpp index f2469eaf57cc..0f683ffd8eef 100644 --- a/cmds/idmap2/tests/Main.cpp +++ b/cmds/idmap2/tests/Main.cpp @@ -25,7 +25,7 @@ namespace android { namespace idmap2 { -const std::string GetTestDataPath() { +std::string GetTestDataPath() { return base::GetExecutableDirectory() + "/tests/data"; } diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp index da9779211f81..0c4f493e9f6c 100644 --- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp @@ -29,7 +29,6 @@ #include "TestHelpers.h" -using ::testing::IsNull; using ::testing::NotNull; using android::ApkAssets; diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp index c28ce2e02ea9..6285f217b21e 100644 --- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp @@ -27,7 +27,6 @@ #include "TestHelpers.h" -using ::testing::IsNull; using ::testing::NotNull; namespace android { diff --git a/cmds/idmap2/tests/ResourceUtilsTests.cpp b/cmds/idmap2/tests/ResourceUtilsTests.cpp index 7f60d7529a61..c8578d3b340a 100644 --- a/cmds/idmap2/tests/ResourceUtilsTests.cpp +++ b/cmds/idmap2/tests/ResourceUtilsTests.cpp @@ -52,13 +52,13 @@ class ResourceUtilsTests : public Idmap2Tests { }; TEST_F(ResourceUtilsTests, ResToTypeEntryName) { - Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000u); + Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000U); ASSERT_TRUE(name); ASSERT_EQ(*name, "integer/int1"); } TEST_F(ResourceUtilsTests, ResToTypeEntryNameNoSuchResourceId) { - Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f123456u); + Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f123456U); ASSERT_FALSE(name); } diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h index 18dc541021c1..356db7af1385 100644 --- a/cmds/idmap2/tests/TestHelpers.h +++ b/cmds/idmap2/tests/TestHelpers.h @@ -117,7 +117,7 @@ const unsigned char idmap_raw_data[] = { const unsigned int idmap_raw_data_len = 565; -const std::string GetTestDataPath(); +std::string GetTestDataPath(); class Idmap2Tests : public testing::Test { protected: diff --git a/cmds/idmap2/tests/XmlTests.cpp b/cmds/idmap2/tests/XmlTests.cpp index 97ff03e0f9e3..40758b42e1ff 100644 --- a/cmds/idmap2/tests/XmlTests.cpp +++ b/cmds/idmap2/tests/XmlTests.cpp @@ -58,11 +58,11 @@ TEST(XmlTests, FindTag) { auto attrs = xml->FindTag("c"); ASSERT_THAT(attrs, NotNull()); - ASSERT_EQ(attrs->size(), 4u); + ASSERT_EQ(attrs->size(), 4U); ASSERT_EQ(attrs->at("type_string"), "fortytwo"); ASSERT_EQ(std::stoi(attrs->at("type_int_dec")), 42); ASSERT_EQ(std::stoi(attrs->at("type_int_hex")), 42); - ASSERT_NE(std::stoul(attrs->at("type_int_boolean")), 0u); + ASSERT_NE(std::stoul(attrs->at("type_int_boolean")), 0U); auto fail = xml->FindTag("does-not-exist"); ASSERT_THAT(fail, IsNull()); diff --git a/cmds/incident_helper/src/TextParserBase.h b/cmds/incident_helper/src/TextParserBase.h index 784c181d9741..a6074e71c035 100644 --- a/cmds/incident_helper/src/TextParserBase.h +++ b/cmds/incident_helper/src/TextParserBase.h @@ -30,7 +30,7 @@ class TextParserBase { public: String8 name; - TextParserBase(String8 name) : name(name) {}; + explicit TextParserBase(String8 name) : name(name) {}; virtual ~TextParserBase() {}; virtual status_t Parse(const int in, const int out) const = 0; diff --git a/cmds/incident_helper/src/ih_util.h b/cmds/incident_helper/src/ih_util.h index c02a3493724a..09dc8e6fdbfc 100644 --- a/cmds/incident_helper/src/ih_util.h +++ b/cmds/incident_helper/src/ih_util.h @@ -109,7 +109,7 @@ double toDouble(const std::string& s); class Reader { public: - Reader(const int fd); + explicit Reader(const int fd); ~Reader(); bool readLine(std::string* line); @@ -162,7 +162,7 @@ private: class Message { public: - Message(Table* table); + explicit Message(Table* table); ~Message(); // Reconstructs the typical proto message by adding its message fields. diff --git a/cmds/incidentd/src/IncidentService.h b/cmds/incidentd/src/IncidentService.h index 6252ad295224..c63a183b70b9 100644 --- a/cmds/incidentd/src/IncidentService.h +++ b/cmds/incidentd/src/IncidentService.h @@ -97,7 +97,7 @@ private: // ================================================================================ class IncidentService : public BnIncidentManager { public: - IncidentService(const sp<Looper>& handlerLooper); + explicit IncidentService(const sp<Looper>& handlerLooper); virtual ~IncidentService(); virtual Status reportIncident(const IncidentReportArgs& args); diff --git a/cmds/incidentd/src/Privacy.h b/cmds/incidentd/src/Privacy.h index a3df4903de45..a0159d9a8649 100644 --- a/cmds/incidentd/src/Privacy.h +++ b/cmds/incidentd/src/Privacy.h @@ -83,7 +83,7 @@ public: static PrivacySpec new_spec(int dest); private: - PrivacySpec(uint8_t dest) : dest(dest) {} + explicit PrivacySpec(uint8_t dest) : dest(dest) {} }; } // namespace incidentd diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h index 45fd9443e9d0..2a3abd7b4d97 100644 --- a/cmds/incidentd/src/Reporter.h +++ b/cmds/incidentd/src/Reporter.h @@ -89,7 +89,7 @@ public: ReportRequestSet batch; Reporter(); // PROD must use this constructor. - Reporter(const char* directory); // For testing purpose only. + explicit Reporter(const char* directory); // For testing purpose only. virtual ~Reporter(); // Run the report as described in the batch and args parameters. diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp index 10d226822331..32ec1ba90cda 100644 --- a/cmds/incidentd/src/Section.cpp +++ b/cmds/incidentd/src/Section.cpp @@ -415,7 +415,7 @@ struct WorkerThreadData : public virtual RefBase { bool workerDone; status_t workerError; - WorkerThreadData(const WorkerThreadSection* section); + explicit WorkerThreadData(const WorkerThreadSection* section); virtual ~WorkerThreadData(); }; diff --git a/cmds/statsd/src/anomaly/AlarmMonitor.h b/cmds/statsd/src/anomaly/AlarmMonitor.h index 3badb1faece6..bca858e67f13 100644 --- a/cmds/statsd/src/anomaly/AlarmMonitor.h +++ b/cmds/statsd/src/anomaly/AlarmMonitor.h @@ -42,7 +42,7 @@ namespace statsd { * Timestamps are in seconds since epoch in a uint32, so will fail in year 2106. */ struct InternalAlarm : public RefBase { - InternalAlarm(uint32_t timestampSec) : timestampSec(timestampSec) { + explicit InternalAlarm(uint32_t timestampSec) : timestampSec(timestampSec) { } const uint32_t timestampSec; diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h index a6f88af4b4d6..2c8814772839 100644 --- a/cmds/statsd/src/condition/ConditionWizard.h +++ b/cmds/statsd/src/condition/ConditionWizard.h @@ -29,7 +29,7 @@ namespace statsd { class ConditionWizard : public virtual android::RefBase { public: ConditionWizard(){}; // for testing - ConditionWizard(std::vector<sp<ConditionTracker>>& conditionTrackers) + explicit ConditionWizard(std::vector<sp<ConditionTracker>>& conditionTrackers) : mAllConditions(conditionTrackers){}; virtual ~ConditionWizard(){}; diff --git a/cmds/statsd/src/config/ConfigKey.h b/cmds/statsd/src/config/ConfigKey.h index dc79519d1f34..4cc9393fbd02 100644 --- a/cmds/statsd/src/config/ConfigKey.h +++ b/cmds/statsd/src/config/ConfigKey.h @@ -33,7 +33,7 @@ using std::string; class ConfigKey { public: ConfigKey(); - explicit ConfigKey(const ConfigKey& that); + ConfigKey(const ConfigKey& that); ConfigKey(int uid, const int64_t& id); ~ConfigKey(); diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.h b/cmds/statsd/src/external/ResourceHealthManagerPuller.h index 9b238eaf5f83..ba6e6c33b1a5 100644 --- a/cmds/statsd/src/external/ResourceHealthManagerPuller.h +++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.h @@ -28,7 +28,7 @@ namespace statsd { */ class ResourceHealthManagerPuller : public StatsPuller { public: - ResourceHealthManagerPuller(int tagId); + explicit ResourceHealthManagerPuller(int tagId); bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override; }; diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.h b/cmds/statsd/src/external/StatsCompanionServicePuller.h index 0a49732fe120..a16baf025a34 100644 --- a/cmds/statsd/src/external/StatsCompanionServicePuller.h +++ b/cmds/statsd/src/external/StatsCompanionServicePuller.h @@ -25,7 +25,7 @@ namespace statsd { class StatsCompanionServicePuller : public StatsPuller { public: - StatsCompanionServicePuller(int tagId); + explicit StatsCompanionServicePuller(int tagId); bool PullInternal(vector<std::shared_ptr<LogEvent> >* data) override; void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) override; diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h index cafd7979601a..f8ecb87eafd4 100644 --- a/cmds/statsd/src/external/StatsPuller.h +++ b/cmds/statsd/src/external/StatsPuller.h @@ -33,7 +33,7 @@ namespace statsd { class StatsPuller : public virtual RefBase { public: - StatsPuller(const int tagId); + explicit StatsPuller(const int tagId); virtual ~StatsPuller() {} diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index 5408d17b02b2..4e37e9b004ca 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -212,7 +212,7 @@ private: * Don't copy, it's slower. If we really need this we can add it but let's try to * avoid it. */ - explicit LogEvent(const LogEvent&); + LogEvent(const LogEvent&); /** * Parses a log_msg into a LogEvent object. diff --git a/cmds/statsd/src/socket/StatsSocketListener.h b/cmds/statsd/src/socket/StatsSocketListener.h index 73e4d33d3e18..b8185d2a32d2 100644 --- a/cmds/statsd/src/socket/StatsSocketListener.h +++ b/cmds/statsd/src/socket/StatsSocketListener.h @@ -35,7 +35,7 @@ namespace statsd { class StatsSocketListener : public SocketListener, public virtual android::RefBase { public: - StatsSocketListener(const sp<LogListener>& listener); + explicit StatsSocketListener(const sp<LogListener>& listener); virtual ~StatsSocketListener(); @@ -51,4 +51,4 @@ private: }; } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java index 02de7f05be7c..0775afeaeda4 100644 --- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java +++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java @@ -16,209 +16,193 @@ package com.android.statsd.shelltools.testdrive; import com.android.internal.os.StatsdConfigProto.AtomMatcher; +import com.android.internal.os.StatsdConfigProto.EventMetric; +import com.android.internal.os.StatsdConfigProto.FieldFilter; +import com.android.internal.os.StatsdConfigProto.GaugeMetric; import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher; import com.android.internal.os.StatsdConfigProto.StatsdConfig; +import com.android.internal.os.StatsdConfigProto.TimeUnit; import com.android.os.AtomsProto.Atom; import com.android.os.StatsLog.ConfigMetricsReport; import com.android.os.StatsLog.ConfigMetricsReportList; +import com.android.os.StatsLog.StatsLogReport; import com.android.statsd.shelltools.Utils; import com.google.common.io.Files; -import com.google.protobuf.TextFormat; -import com.google.protobuf.TextFormat.ParseException; import java.io.File; import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; public class TestDrive { - public static final int PULL_ATOM_START = 10000; - public static final long ATOM_MATCHER_ID = 1234567; - - public static final long CONFIG_ID = 54321; - - private static boolean mIsPushedAtom = false; - - private static final Logger logger = Logger.getLogger(TestDrive.class.getName()); + private static final int METRIC_ID_BASE = 1111; + private static final long ATOM_MATCHER_ID_BASE = 1234567; + private static final int PULL_ATOM_START = 10000; + private static final long CONFIG_ID = 54321; + private static final String[] ALLOWED_LOG_SOURCES = { + "AID_GRAPHICS", + "AID_INCIDENTD", + "AID_STATSD", + "AID_RADIO", + "com.android.systemui", + "com.android.vending", + "AID_SYSTEM", + "AID_ROOT", + "AID_BLUETOOTH", + "AID_LMKD" + }; + private static final Logger LOGGER = Logger.getLogger(TestDrive.class.getName()); + + private final Set<Long> mTrackedMetrics = new HashSet<>(); public static void main(String[] args) { TestDrive testDrive = new TestDrive(); - Utils.setUpLogger(logger, false); + Set<Integer> trackedAtoms = new HashSet<>(); + Utils.setUpLogger(LOGGER, false); + String remoteConfigPath = null; - if (args.length != 1) { - logger.log(Level.SEVERE, "Usage: ./test_drive <atomId>"); - return; - } - int atomId; - try { - atomId = Integer.valueOf(args[0]); - } catch (NumberFormatException e) { - logger.log(Level.SEVERE, "Bad atom id provided: " + args[0]); - return; - } - if (Atom.getDescriptor().findFieldByNumber(atomId) == null) { - logger.log(Level.SEVERE, "No such atom found: " + args[0]); + if (args.length < 1) { + LOGGER.log(Level.SEVERE, "Usage: ./test_drive <atomId1> <atomId2> ... <atomIdN>"); return; } - mIsPushedAtom = atomId < PULL_ATOM_START; + for (int i = 0; i < args.length; i++) { + try { + int atomId = Integer.valueOf(args[i]); + if (Atom.getDescriptor().findFieldByNumber(atomId) == null) { + LOGGER.log(Level.SEVERE, "No such atom found: " + args[i]); + continue; + } + trackedAtoms.add(atomId); + } catch (NumberFormatException e) { + LOGGER.log(Level.SEVERE, "Bad atom id provided: " + args[i]); + continue; + } + } try { - StatsdConfig config = testDrive.createConfig(atomId); + StatsdConfig config = testDrive.createConfig(trackedAtoms); if (config == null) { - logger.log(Level.SEVERE, "Failed to create valid config."); + LOGGER.log(Level.SEVERE, "Failed to create valid config."); return; } - testDrive.pushConfig(config); - logger.info("Pushed the following config to statsd:"); - logger.info(config.toString()); - if (mIsPushedAtom) { - logger.info( + remoteConfigPath = testDrive.pushConfig(config); + LOGGER.info("Pushed the following config to statsd:"); + LOGGER.info(config.toString()); + if (!hasPulledAtom(trackedAtoms)) { + LOGGER.info( "Now please play with the device to trigger the event. All events should " + "be dumped after 1 min ..."); Thread.sleep(60_000); } else { // wait for 2 min - logger.info("Now wait for 2 minutes ..."); + LOGGER.info("Now wait for 2 minutes ..."); Thread.sleep(120_000); } testDrive.dumpMetrics(); } catch (Exception e) { - logger.log(Level.SEVERE, "Failed to test drive: " + e.getMessage()); + LOGGER.log(Level.SEVERE, "Failed to test drive: " + e.getMessage(), e); } finally { testDrive.removeConfig(); + if (remoteConfigPath != null) { + try { + Utils.runCommand(null, LOGGER, "adb", "shell", "rm", remoteConfigPath); + } catch (Exception e) { + LOGGER.log(Level.WARNING, + "Unable to remove remote config file: " + remoteConfigPath, e); + } + } } } - private void pushConfig(StatsdConfig config) throws IOException, InterruptedException { - File configFile = File.createTempFile("statsdconfig", ".config"); - configFile.deleteOnExit(); - Files.write(config.toByteArray(), configFile); - String remotePath = "/data/local/tmp/" + configFile.getName(); - Utils.runCommand(null, logger, "adb", "push", configFile.getAbsolutePath(), remotePath); - Utils.runCommand(null, logger, - "adb", "shell", "cat", remotePath, "|", Utils.CMD_UPDATE_CONFIG, - String.valueOf(CONFIG_ID)); - } - - private void removeConfig() { - try { - Utils.runCommand(null, logger, - "adb", "shell", Utils.CMD_REMOVE_CONFIG, String.valueOf(CONFIG_ID)); - } catch (Exception e) { - logger.log(Level.SEVERE, "Failed to remove config: " + e.getMessage()); + private void dumpMetrics() throws Exception { + ConfigMetricsReportList reportList = Utils.getReportList(CONFIG_ID, true, false, LOGGER); + // We may get multiple reports. Take the last one. + ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1); + for (StatsLogReport statsLog : report.getMetricsList()) { + if (mTrackedMetrics.contains(statsLog.getMetricId())) { + LOGGER.info(statsLog.toString()); + } } } - private StatsdConfig createConfig(int atomId) { - try { - if (mIsPushedAtom) { - return createSimpleEventMetricConfig(atomId); + private StatsdConfig createConfig(Set<Integer> atomIds) { + long metricId = METRIC_ID_BASE; + long atomMatcherId = ATOM_MATCHER_ID_BASE; + + StatsdConfig.Builder builder = StatsdConfig.newBuilder(); + builder + .addAllAllowedLogSource(Arrays.asList(ALLOWED_LOG_SOURCES)) + .setHashStringsInMetricReport(false); + + for (int atomId : atomIds) { + if (isPulledAtom(atomId)) { + builder.addAtomMatcher(createAtomMatcher(atomId, atomMatcherId)); + GaugeMetric.Builder gaugeMetricBuilder = GaugeMetric.newBuilder(); + gaugeMetricBuilder + .setId(metricId) + .setWhat(atomMatcherId) + .setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true).build()) + .setBucket(TimeUnit.ONE_MINUTE); + builder.addGaugeMetric(gaugeMetricBuilder.build()); } else { - return createSimpleGaugeMetricConfig(atomId); + EventMetric.Builder eventMetricBuilder = EventMetric.newBuilder(); + eventMetricBuilder + .setId(metricId) + .setWhat(atomMatcherId); + builder.addEventMetric(eventMetricBuilder.build()); + builder.addAtomMatcher(createAtomMatcher(atomId, atomMatcherId)); } - } catch (ParseException e) { - logger.log( - Level.SEVERE, - "Failed to parse the config! line: " - + e.getLine() - + " col: " - + e.getColumn() - + " " - + e.getMessage()); + atomMatcherId++; + mTrackedMetrics.add(metricId++); } - return null; - } - - private StatsdConfig createSimpleEventMetricConfig(int atomId) throws ParseException { - StatsdConfig.Builder baseBuilder = getSimpleEventMetricBaseConfig(); - baseBuilder.addAtomMatcher(createAtomMatcher(atomId)); - return baseBuilder.build(); + return builder.build(); } - private StatsdConfig createSimpleGaugeMetricConfig(int atomId) throws ParseException { - StatsdConfig.Builder baseBuilder = getSimpleGaugeMetricBaseConfig(); - baseBuilder.addAtomMatcher(createAtomMatcher(atomId)); - return baseBuilder.build(); - } - - private AtomMatcher createAtomMatcher(int atomId) { + private static AtomMatcher createAtomMatcher(int atomId, long matcherId) { AtomMatcher.Builder atomMatcherBuilder = AtomMatcher.newBuilder(); atomMatcherBuilder - .setId(ATOM_MATCHER_ID) + .setId(matcherId) .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder().setAtomId(atomId)); return atomMatcherBuilder.build(); } - private StatsdConfig.Builder getSimpleEventMetricBaseConfig() throws ParseException { - StatsdConfig.Builder builder = StatsdConfig.newBuilder(); - TextFormat.merge(EVENT_BASE_CONFIG_SRTR, builder); - return builder; - } - - private StatsdConfig.Builder getSimpleGaugeMetricBaseConfig() throws ParseException { - StatsdConfig.Builder builder = StatsdConfig.newBuilder(); - TextFormat.merge(GAUGE_BASE_CONFIG_STR, builder); - return builder; + private static String pushConfig(StatsdConfig config) throws IOException, InterruptedException { + File configFile = File.createTempFile("statsdconfig", ".config"); + configFile.deleteOnExit(); + Files.write(config.toByteArray(), configFile); + String remotePath = "/data/local/tmp/" + configFile.getName(); + Utils.runCommand(null, LOGGER, "adb", "push", configFile.getAbsolutePath(), remotePath); + Utils.runCommand(null, LOGGER, + "adb", "shell", "cat", remotePath, "|", Utils.CMD_UPDATE_CONFIG, + String.valueOf(CONFIG_ID)); + return remotePath; } - private void dumpMetrics() throws Exception { - ConfigMetricsReportList reportList = Utils.getReportList(CONFIG_ID, true, false, logger); - // We may get multiple reports. Take the last one. - ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1); - // Really should be only one metric. - if (report.getMetricsCount() != 1) { - logger.log(Level.SEVERE, - "Only one report metric expected, got " + report.getMetricsCount()); - return; + private static void removeConfig() { + try { + Utils.runCommand(null, LOGGER, + "adb", "shell", Utils.CMD_REMOVE_CONFIG, String.valueOf(CONFIG_ID)); + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Failed to remove config: " + e.getMessage()); } - - logger.info("Got following metric data dump:"); - logger.info(report.getMetrics(0).toString()); } - private static final String EVENT_BASE_CONFIG_SRTR = - "id: 12345\n" - + "event_metric {\n" - + " id: 1111\n" - + " what: 1234567\n" - + "}\n" - + "allowed_log_source: \"AID_GRAPHICS\"\n" - + "allowed_log_source: \"AID_INCIDENTD\"\n" - + "allowed_log_source: \"AID_STATSD\"\n" - + "allowed_log_source: \"AID_RADIO\"\n" - + "allowed_log_source: \"com.android.systemui\"\n" - + "allowed_log_source: \"com.android.vending\"\n" - + "allowed_log_source: \"AID_SYSTEM\"\n" - + "allowed_log_source: \"AID_ROOT\"\n" - + "allowed_log_source: \"AID_BLUETOOTH\"\n" - + "allowed_log_source: \"AID_LMKD\"\n" - + "\n" - + "hash_strings_in_metric_report: false"; - - private static final String GAUGE_BASE_CONFIG_STR = - "id: 56789\n" - + "gauge_metric {\n" - + " id: 2222\n" - + " what: 1234567\n" - + " gauge_fields_filter {\n" - + " include_all: true\n" - + " }\n" - + " bucket: ONE_MINUTE\n" - + "}\n" - + "allowed_log_source: \"AID_GRAPHICS\"\n" - + "allowed_log_source: \"AID_INCIDENTD\"\n" - + "allowed_log_source: \"AID_STATSD\"\n" - + "allowed_log_source: \"AID_RADIO\"\n" - + "allowed_log_source: \"com.android.systemui\"\n" - + "allowed_log_source: \"com.android.vending\"\n" - + "allowed_log_source: \"AID_SYSTEM\"\n" - + "allowed_log_source: \"AID_ROOT\"\n" - + "allowed_log_source: \"AID_BLUETOOTH\"\n" - + "allowed_log_source: \"AID_LMKD\"\n" - + "\n" - + "hash_strings_in_metric_report: false"; + private static boolean isPulledAtom(int atomId) { + return atomId >= PULL_ATOM_START; + } + private static boolean hasPulledAtom(Set<Integer> atoms) { + for (Integer i : atoms) { + if (isPulledAtom(i)) { + return true; + } + } + return false; + } } diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt index 5cd3d5d4fa4d..7bbeb16b1b24 100644 --- a/config/hiddenapi-greylist.txt +++ b/config/hiddenapi-greylist.txt @@ -1474,7 +1474,6 @@ Landroid/service/dreams/IDreamManager;->getDreamComponents()[Landroid/content/Co Landroid/service/dreams/IDreamManager;->isDreaming()Z Landroid/service/dreams/IDreamManager;->setDreamComponents([Landroid/content/ComponentName;)V Landroid/service/euicc/IDeleteSubscriptionCallback;->onComplete(I)V -Landroid/service/euicc/IDownloadSubscriptionCallback;->onComplete(I)V Landroid/service/euicc/IEraseSubscriptionsCallback;->onComplete(I)V Landroid/service/euicc/IEuiccService$Stub;-><init>()V Landroid/service/euicc/IGetDefaultDownloadableSubscriptionListCallback;->onComplete(Landroid/service/euicc/GetDefaultDownloadableSubscriptionListResult;)V diff --git a/core/java/android/annotation/Px.java b/core/java/android/annotation/Px.java index a0ef2244d5e4..ad99fdb7657e 100644 --- a/core/java/android/annotation/Px.java +++ b/core/java/android/annotation/Px.java @@ -29,6 +29,8 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; * Denotes that a numeric parameter, field or method return value is expected * to represent a pixel dimension. * + * @paramDoc This units of this value are pixels. + * @returnDoc This units of this value are pixels. * {@hide} */ @Documented diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 791f3da0d5e8..78fe0024b0b0 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -2496,7 +2496,7 @@ public class AppOpsManager { * @param packageName The package performing the operation. * @param result The result of the note. */ - void onOpNoted(String code, int uid, String packageName, int result); + void onOpNoted(int code, int uid, String packageName, int result); } /** @@ -2953,7 +2953,7 @@ public class AppOpsManager { * @hide */ @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true) - public void startWatchingNoted(@NonNull String[] ops, @NonNull OnOpNotedListener callback) { + public void startWatchingNoted(@NonNull int[] ops, @NonNull OnOpNotedListener callback) { IAppOpsNotedCallback cb; synchronized (mNotedWatchers) { cb = mNotedWatchers.get(callback); @@ -2963,17 +2963,13 @@ public class AppOpsManager { cb = new IAppOpsNotedCallback.Stub() { @Override public void opNoted(int op, int uid, String packageName, int mode) { - callback.onOpNoted(sOpToString[op], uid, packageName, mode); + callback.onOpNoted(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); + mService.startWatchingNoted(ops, cb); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2983,7 +2979,7 @@ public class AppOpsManager { * 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 #startWatchingNoted(int[], OnOpNotedListener) * @see #noteOp(String, int, String) * * @hide diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java index 139a39fe3a1d..b5560331e7aa 100644 --- a/core/java/android/app/AppOpsManagerInternal.java +++ b/core/java/android/app/AppOpsManagerInternal.java @@ -16,7 +16,6 @@ package android.app; -import android.annotation.NonNull; import android.util.SparseIntArray; import com.android.internal.util.function.QuadFunction; @@ -86,10 +85,7 @@ public abstract class AppOpsManagerInternal { * * @param code The op code to set. * @param uid The UID for which to set. - * @param packageName The package for which to set. * @param mode The new mode to set. - * @param isPrivileged If the package is privileged */ - public abstract void setMode(int code, int uid, @NonNull String packageName, int mode, - boolean isPrivileged); + public abstract void setUidMode(int code, int uid, int mode); } diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 623bdda93b7e..f06df3d4dfba 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -145,6 +145,15 @@ public class PackageInstaller { public static final String ACTION_SESSION_COMMITTED = "android.content.pm.action.SESSION_COMMITTED"; + /** + * Broadcast Action: Send information about a staged install session when its state is updated. + * <p> + * The associated session information is defined in {@link #EXTRA_SESSION}. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SESSION_UPDATED = + "android.content.pm.action.SESSION_UPDATED"; + /** {@hide} */ public static final String ACTION_CONFIRM_INSTALL = "android.content.pm.action.CONFIRM_INSTALL"; diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java index ad82626dd6ad..35609c9a8926 100644 --- a/core/java/android/content/pm/SharedLibraryInfo.java +++ b/core/java/android/content/pm/SharedLibraryInfo.java @@ -33,11 +33,29 @@ import java.util.List; * This class provides information for a shared library. There are * three types of shared libraries: builtin - non-updatable part of * the OS; dynamic - updatable backwards-compatible dynamically linked; - * static - updatable non backwards-compatible emulating static linking. + * static - non backwards-compatible emulating static linking. */ public final class SharedLibraryInfo implements Parcelable { /** @hide */ + public static SharedLibraryInfo createForStatic(PackageParser.Package pkg) { + return new SharedLibraryInfo(null, pkg.packageName, pkg.getAllCodePaths(), + pkg.staticSharedLibName, + pkg.staticSharedLibVersion, + TYPE_STATIC, + new VersionedPackage(pkg.manifestPackageName, pkg.getLongVersionCode()), + null, null); + } + + /** @hide */ + public static SharedLibraryInfo createForDynamic(PackageParser.Package pkg, String name) { + return new SharedLibraryInfo(null, pkg.packageName, pkg.getAllCodePaths(), name, + (long) VERSION_UNDEFINED, + TYPE_DYNAMIC, new VersionedPackage(pkg.packageName, pkg.getLongVersionCode()), + null, null); + } + + /** @hide */ @IntDef(flag = true, prefix = { "TYPE_" }, value = { TYPE_BUILTIN, TYPE_DYNAMIC, diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 47145874490f..49c3dc63e151 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -187,13 +187,19 @@ public class ConnectivityManager { * is for a network to which the connectivity manager was failing over * following a disconnect on another network. * Retrieve it with {@link android.content.Intent#getBooleanExtra(String,boolean)}. + * + * @deprecated See {@link NetworkInfo}. */ + @Deprecated public static final String EXTRA_IS_FAILOVER = "isFailover"; /** * The lookup key for a {@link NetworkInfo} object. This is supplied when * there is another network that it may be possible to connect to. Retrieve with * {@link android.content.Intent#getParcelableExtra(String)}. + * + * @deprecated See {@link NetworkInfo}. */ + @Deprecated public static final String EXTRA_OTHER_NETWORK_INFO = "otherNetwork"; /** * The lookup key for a boolean that indicates whether there is a @@ -214,7 +220,10 @@ public class ConnectivityManager { * may be passed up from the lower networking layers, and its * meaning may be specific to a particular network type. Retrieve * it with {@link android.content.Intent#getStringExtra(String)}. + * + * @deprecated See {@link NetworkInfo#getExtraInfo()}. */ + @Deprecated public static final String EXTRA_EXTRA_INFO = "extraInfo"; /** * The lookup key for an int that provides information about @@ -895,7 +904,9 @@ public class ConnectivityManager { * * @return a {@link NetworkInfo} object for the current default network * or {@code null} if no default network is currently active + * @deprecated See {@link NetworkInfo}. */ + @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public NetworkInfo getActiveNetworkInfo() { try { @@ -1079,7 +1090,9 @@ public class ConnectivityManager { * @return a {@link NetworkInfo} object for the requested * network or {@code null} if the {@code Network} * is not valid. + * @deprecated See {@link NetworkInfo}. */ + @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public NetworkInfo getNetworkInfo(Network network) { return getNetworkInfoForUid(network, Process.myUid(), false); diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 1b9a66cd6ea7..80517ce28cda 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -18,6 +18,7 @@ package android.net; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -161,7 +162,7 @@ public final class LinkProperties implements Parcelable { /** * @hide */ - @UnsupportedAppUsage + @SystemApi public LinkProperties() { } @@ -195,7 +196,7 @@ public final class LinkProperties implements Parcelable { * @param iface The name of the network interface used for this link. * @hide */ - @UnsupportedAppUsage + @SystemApi public void setInterfaceName(String iface) { mIfaceName = iface; ArrayList<RouteInfo> newRoutes = new ArrayList<>(mRoutes.size()); @@ -346,7 +347,7 @@ public final class LinkProperties implements Parcelable { * object. * @hide */ - @UnsupportedAppUsage + @SystemApi public void setLinkAddresses(Collection<LinkAddress> addresses) { mLinkAddresses.clear(); for (LinkAddress address: addresses) { @@ -392,7 +393,7 @@ public final class LinkProperties implements Parcelable { * @param dnsServers The {@link Collection} of DNS servers to set in this object. * @hide */ - @UnsupportedAppUsage + @SystemApi public void setDnsServers(Collection<InetAddress> dnsServers) { mDnses.clear(); for (InetAddress dnsServer: dnsServers) { @@ -529,7 +530,7 @@ public final class LinkProperties implements Parcelable { * domains to search when resolving host names on this link. * @hide */ - @UnsupportedAppUsage + @SystemApi public void setDomains(String domains) { mDomains = domains; } @@ -552,7 +553,7 @@ public final class LinkProperties implements Parcelable { * @param mtu The MTU to use for this link. * @hide */ - @UnsupportedAppUsage + @SystemApi public void setMtu(int mtu) { mMtu = mtu; } @@ -562,9 +563,7 @@ public final class LinkProperties implements Parcelable { * this will return 0. * * @return The mtu value set for this link. - * @hide */ - @UnsupportedAppUsage public int getMtu() { return mMtu; } @@ -613,7 +612,7 @@ public final class LinkProperties implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @SystemApi public boolean addRoute(RouteInfo route) { if (route != null) { String routeIface = route.getInterface(); @@ -688,7 +687,7 @@ public final class LinkProperties implements Parcelable { * @param proxy A {@link ProxyInfo} defining the HTTP Proxy to use on this link. * @hide */ - @UnsupportedAppUsage + @SystemApi public void setHttpProxy(ProxyInfo proxy) { mHttpProxy = proxy; } @@ -760,7 +759,7 @@ public final class LinkProperties implements Parcelable { * Clears this object to its initial state. * @hide */ - @UnsupportedAppUsage + @SystemApi public void clear() { mIfaceName = null; mLinkAddresses.clear(); diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index c41a56c7305f..1b44c920a205 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -1017,7 +1017,7 @@ public final class NetworkCapabilities implements Parcelable { * @return The bearer-specific signal strength. * @hide */ - @UnsupportedAppUsage + @SystemApi public int getSignalStrength() { return mSignalStrength; } diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java index 1a1d2d33424c..89d99617dfbf 100644 --- a/core/java/android/net/NetworkInfo.java +++ b/core/java/android/net/NetworkInfo.java @@ -28,7 +28,20 @@ import java.util.EnumMap; * Describes the status of a network interface. * <p>Use {@link ConnectivityManager#getActiveNetworkInfo()} to get an instance that represents * the current network connection. + * + * @deprecated Callers should instead use the {@link ConnectivityManager.NetworkCallback} API to + * learn about connectivity changes, or switch to use + * {@link ConnectivityManager#getNetworkCapabilities} or + * {@link ConnectivityManager#getLinkProperties} to get information synchronously. Keep + * in mind that while callbacks are guaranteed to be called for every event in order, + * synchronous calls have no such constraints, and as such it is unadvisable to use the + * synchronous methods inside the callbacks as they will often not offer a view of + * networking that is consistent (that is: they may return a past or a future state with + * respect to the event being processed by the callback). Instead, callers are advised + * to only use the arguments of the callbacks, possibly memorizing the specific bits of + * information they need to keep from one callback to another. */ +@Deprecated public class NetworkInfo implements Parcelable { /** @@ -52,7 +65,10 @@ public class NetworkInfo implements Parcelable { * <tr><td><code>FAILED</code></td><td><code>DISCONNECTED</code></td></tr> * <tr><td><code>BLOCKED</code></td><td><code>DISCONNECTED</code></td></tr> * </table> + * + * @deprecated See {@link NetworkInfo}. */ + @Deprecated public enum State { CONNECTING, CONNECTED, SUSPENDED, DISCONNECTING, DISCONNECTED, UNKNOWN } @@ -61,7 +77,10 @@ public class NetworkInfo implements Parcelable { * The fine-grained state of a network connection. This level of detail * is probably of interest to few applications. Most should use * {@link android.net.NetworkInfo.State State} instead. + * + * @deprecated See {@link NetworkInfo}. */ + @Deprecated public enum DetailedState { /** Ready to start data connection setup. */ IDLE, @@ -463,8 +482,10 @@ public class NetworkInfo implements Parcelable { * Set the extraInfo field. * @param extraInfo an optional {@code String} providing addditional network state * information passed up from the lower networking layers. + * @deprecated See {@link NetworkInfo#getExtraInfo}. * @hide */ + @Deprecated public void setExtraInfo(String extraInfo) { synchronized (this) { this.mExtraInfo = extraInfo; @@ -488,7 +509,10 @@ public class NetworkInfo implements Parcelable { * Report the extra information about the network state, if any was * provided by the lower networking layers. * @return the extra information, or null if not available + * @deprecated Use other services e.g. WifiManager to get additional information passed up from + * the lower networking layers. */ + @Deprecated public String getExtraInfo() { synchronized (this) { return mExtraInfo; diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index 04b6b44013b9..3b01b03edd4e 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -17,6 +17,7 @@ package android.net; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.net.NetworkCapabilities.NetCapability; import android.net.NetworkCapabilities.Transport; @@ -344,7 +345,7 @@ public class NetworkRequest implements Parcelable { * @param signalStrength the bearer-specific signal strength. * @hide */ - @UnsupportedAppUsage + @SystemApi public Builder setSignalStrength(int signalStrength) { mNetworkCapabilities.setSignalStrength(signalStrength); return this; diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index c5e62f1dfe60..64f235546201 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -34,13 +34,13 @@ import android.os.RemoteException; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; -import android.view.contentcapture.ActivityContentCaptureSession; 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 android.view.contentcapture.MainContentCaptureSession; import com.android.internal.os.IResultReceiver; @@ -293,13 +293,26 @@ public abstract class ContentCaptureService extends Service { final List<ContentCaptureEvent> events = parceledEvents.getList(); for (int i = 0; i < events.size(); i++) { final ContentCaptureEvent event = events.get(i); + if (!handleIsRightCallerFor(event, uid)) continue; String sessionIdString = event.getSessionId(); if (!sessionIdString.equals(lastSessionId)) { - if (!handleIsRightCallerFor(sessionIdString, uid)) continue; sessionId = new ContentCaptureSessionId(sessionIdString); lastSessionId = sessionIdString; } - onContentCaptureEvent(sessionId, event); + switch (event.getType()) { + case ContentCaptureEvent.TYPE_SESSION_STARTED: + final ContentCaptureContext clientContext = event.getClientContext(); + clientContext.setParentSessionId(event.getParentSessionId()); + mSessionsByUid.put(sessionIdString, uid); + onCreateContentCaptureSession(clientContext, sessionId); + break; + case ContentCaptureEvent.TYPE_SESSION_FINISHED: + mSessionsByUid.remove(sessionIdString); + onDestroyContentCaptureSession(sessionId); + break; + default: + onContentCaptureEvent(sessionId, event); + } } } @@ -314,9 +327,18 @@ public abstract class ContentCaptureService extends Service { } /** - * Checks if the given {@code uid} owns the session. + * Checks if the given {@code uid} owns the session associated with the event. */ - private boolean handleIsRightCallerFor(@NonNull String sessionId, int uid) { + private boolean handleIsRightCallerFor(@NonNull ContentCaptureEvent event, int uid) { + final String sessionId; + switch (event.getType()) { + case ContentCaptureEvent.TYPE_SESSION_STARTED: + case ContentCaptureEvent.TYPE_SESSION_FINISHED: + sessionId = event.getParentSessionId(); + break; + default: + sessionId = event.getSessionId(); + } final Integer rightUid = mSessionsByUid.get(sessionId); if (rightUid == null) { if (VERBOSE) Log.v(TAG, "No session for " + sessionId); @@ -347,7 +369,7 @@ public abstract class ContentCaptureService extends Service { final Bundle extras; if (binder != null) { extras = new Bundle(); - extras.putBinder(ActivityContentCaptureSession.EXTRA_BINDER, binder); + extras.putBinder(MainContentCaptureSession.EXTRA_BINDER, binder); } else { extras = null; } diff --git a/core/java/android/service/euicc/DownloadSubscriptionResult.aidl b/core/java/android/service/euicc/DownloadSubscriptionResult.aidl new file mode 100644 index 000000000000..b625fd6d3cb4 --- /dev/null +++ b/core/java/android/service/euicc/DownloadSubscriptionResult.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.euicc; + +parcelable DownloadSubscriptionResult; diff --git a/core/java/android/service/euicc/DownloadSubscriptionResult.java b/core/java/android/service/euicc/DownloadSubscriptionResult.java new file mode 100644 index 000000000000..b410e35f3f83 --- /dev/null +++ b/core/java/android/service/euicc/DownloadSubscriptionResult.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.service.euicc; + +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.service.euicc.EuiccService.ResolvableError; +import android.service.euicc.EuiccService.Result; + +/** + * Result of a {@link EuiccService#onDownloadSubscription} operation. + * @hide + */ +@SystemApi +public final class DownloadSubscriptionResult implements Parcelable { + + public static final Creator<DownloadSubscriptionResult> CREATOR = + new Creator<DownloadSubscriptionResult>() { + @Override + public DownloadSubscriptionResult createFromParcel(Parcel in) { + return new DownloadSubscriptionResult(in); + } + + @Override + public DownloadSubscriptionResult[] newArray(int size) { + return new DownloadSubscriptionResult[size]; + } + }; + + private final @Result int mResult; + private final @ResolvableError int mResolvableErrors; + private final int mCardId; + + public DownloadSubscriptionResult(@Result int result, @ResolvableError int resolvableErrors, + int cardId) { + this.mResult = result; + this.mResolvableErrors = resolvableErrors; + this.mCardId = cardId; + } + + /** Gets the result of the operation. */ + public @Result int getResult() { + return mResult; + } + + /** + * Gets the bit map of resolvable errors. + * + * <p>The value is passed from EuiccService. The values can be + * + * <ul> + * <li>{@link EuiccService#RESOLVABLE_ERROR_CONFIRMATION_CODE} + * <li>{@link EuiccService#RESOLVABLE_ERROR_POLICY_RULES} + * </ul> + */ + public @ResolvableError int getResolvableErrors() { + return mResolvableErrors; + } + + /** + * Gets the card Id. This is used when resolving resolvable errors. The value is passed from + * EuiccService. + */ + public int getCardId() { + return mCardId; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mResult); + dest.writeInt(mResolvableErrors); + dest.writeInt(mCardId); + } + + @Override + public int describeContents() { + return 0; + } + + private DownloadSubscriptionResult(Parcel in) { + this.mResult = in.readInt(); + this.mResolvableErrors = in.readInt(); + this.mCardId = in.readInt(); + } +} diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java index 49a7320dab6d..4be1f9cd6ae5 100644 --- a/core/java/android/service/euicc/EuiccService.java +++ b/core/java/android/service/euicc/EuiccService.java @@ -16,17 +16,24 @@ package android.service.euicc; import android.annotation.CallSuper; +import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.app.Service; import android.content.Intent; +import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; +import android.telephony.TelephonyManager; import android.telephony.euicc.DownloadableSubscription; import android.telephony.euicc.EuiccInfo; import android.telephony.euicc.EuiccManager.OtaStatus; import android.util.ArraySet; +import android.util.Log; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; @@ -73,6 +80,8 @@ import java.util.concurrent.atomic.AtomicInteger; */ @SystemApi public abstract class EuiccService extends Service { + private static final String TAG = "EuiccService"; + /** Action which must be included in this service's intent filter. */ public static final String EUICC_SERVICE_INTERFACE = "android.service.euicc.EuiccService"; @@ -115,30 +124,91 @@ public abstract class EuiccService extends Service { public static final String ACTION_RESOLVE_NO_PRIVILEGES = "android.service.euicc.action.RESOLVE_NO_PRIVILEGES"; - /** Ask the user to input carrier confirmation code. */ + /** + * Ask the user to input carrier confirmation code. + * + * @deprecated From Q, the resolvable errors happened in the download step are presented as + * bit map in {@link #EXTRA_RESOLVABLE_ERRORS}. The corresponding action would be + * {@link #ACTION_RESOLVE_RESOLVABLE_ERRORS}. + */ + @Deprecated public static final String ACTION_RESOLVE_CONFIRMATION_CODE = "android.service.euicc.action.RESOLVE_CONFIRMATION_CODE"; + /** Ask the user to resolve all the resolvable errors. */ + public static final String ACTION_RESOLVE_RESOLVABLE_ERRORS = + "android.service.euicc.action.RESOLVE_RESOLVABLE_ERRORS"; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = { "RESOLVABLE_ERROR_" }, value = { + RESOLVABLE_ERROR_CONFIRMATION_CODE, + RESOLVABLE_ERROR_POLICY_RULES, + }) + public @interface ResolvableError {} + + /** + * Possible value for the bit map of resolvable errors indicating the download process needs + * the user to input confirmation code. + */ + public static final int RESOLVABLE_ERROR_CONFIRMATION_CODE = 1 << 0; + /** + * Possible value for the bit map of resolvable errors indicating the download process needs + * the user's consent to allow profile policy rules. + */ + public static final int RESOLVABLE_ERROR_POLICY_RULES = 1 << 1; + /** * Intent extra set for resolution requests containing the package name of the calling app. * This is used by the above actions including ACTION_RESOLVE_DEACTIVATE_SIM, - * ACTION_RESOLVE_NO_PRIVILEGES and ACTION_RESOLVE_CONFIRMATION_CODE. + * ACTION_RESOLVE_NO_PRIVILEGES and ACTION_RESOLVE_RESOLVABLE_ERRORS. */ public static final String EXTRA_RESOLUTION_CALLING_PACKAGE = "android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE"; /** + * Intent extra set for resolution requests containing the list of resolvable errors to be + * resolved. Each resolvable error is an integer. Its possible values include: + * <UL> + * <LI>{@link #RESOLVABLE_ERROR_CONFIRMATION_CODE} + * <LI>{@link #RESOLVABLE_ERROR_POLICY_RULES} + * </UL> + */ + public static final String EXTRA_RESOLVABLE_ERRORS = + "android.service.euicc.extra.RESOLVABLE_ERRORS"; + + /** * Intent extra set for resolution requests containing a boolean indicating whether to ask the * user to retry another confirmation code. */ public static final String EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED"; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "RESULT_" }, value = { + RESULT_OK, + RESULT_MUST_DEACTIVATE_SIM, + RESULT_RESOLVABLE_ERRORS, + RESULT_NEED_CONFIRMATION_CODE, + RESULT_FIRST_USER, + }) + public @interface Result {} + /** Result code for a successful operation. */ public static final int RESULT_OK = 0; /** Result code indicating that an active SIM must be deactivated to perform the operation. */ public static final int RESULT_MUST_DEACTIVATE_SIM = -1; - /** Result code indicating that the user must input a carrier confirmation code. */ + /** Result code indicating that the user must resolve resolvable errors. */ + public static final int RESULT_RESOLVABLE_ERRORS = -2; + /** + * Result code indicating that the user must input a carrier confirmation code. + * + * @deprecated From Q, the resolvable errors happened in the download step are presented as + * bit map in {@link #EXTRA_RESOLVABLE_ERRORS}. The corresponding result would be + * {@link #RESULT_RESOLVABLE_ERRORS}. + */ + @Deprecated public static final int RESULT_NEED_CONFIRMATION_CODE = -2; // New predefined codes should have negative values. @@ -154,7 +224,7 @@ public abstract class EuiccService extends Service { RESOLUTION_ACTIONS = new ArraySet<>(); RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM); RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_NO_PRIVILEGES); - RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_CONFIRMATION_CODE); + RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_RESOLVABLE_ERRORS); } /** @@ -169,6 +239,12 @@ public abstract class EuiccService extends Service { */ public static final String EXTRA_RESOLUTION_CONFIRMATION_CODE = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE"; + /** + * String extra for resolution actions indicating whether the user allows policy rules. + * This is used and set by the implementation and used in {@code EuiccOperation}. + */ + public static final String EXTRA_RESOLUTION_ALLOW_POLICY_RULES = + "android.service.euicc.extra.RESOLUTION_ALLOW_POLICY_RULES"; private final IEuiccService.Stub mStubWrapper; @@ -236,8 +312,7 @@ public abstract class EuiccService extends Service { /** * Return the EID of the eUICC. * - * @param slotId ID of the SIM slot being queried. This is currently not populated but is here - * to future-proof the APIs. + * @param slotId ID of the SIM slot being queried. * @return the EID. * @see android.telephony.euicc.EuiccManager#getEid */ @@ -247,8 +322,7 @@ public abstract class EuiccService extends Service { /** * Return the status of OTA update. * - * @param slotId ID of the SIM slot to use for the operation. This is currently not populated - * but is here to future-proof the APIs. + * @param slotId ID of the SIM slot to use for the operation. * @return The status of Euicc OTA update. * @see android.telephony.euicc.EuiccManager#getOtaStatus */ @@ -257,8 +331,7 @@ public abstract class EuiccService extends Service { /** * Perform OTA if current OS is not the latest one. * - * @param slotId ID of the SIM slot to use for the operation. This is currently not populated - * but is here to future-proof the APIs. + * @param slotId ID of the SIM slot to use for the operation. * @param statusChangedCallback Function called when OTA status changed. */ public abstract void onStartOtaIfNecessary( @@ -281,8 +354,7 @@ public abstract class EuiccService extends Service { /** * Return metadata for subscriptions which are available for download for this device. * - * @param slotId ID of the SIM slot to use for the operation. This is currently not populated - * but is here to future-proof the APIs. + * @param slotId ID of the SIM slot to use for the operation. * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the * eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM)} * should be returned to allow the user to consent to this operation first. @@ -302,13 +374,44 @@ public abstract class EuiccService extends Service { * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the * eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM} * should be returned to allow the user to consent to this operation first. + * @param resolvedBundle The bundle containing information on resolved errors. It can contain + * a string of confirmation code for the key {@link #EXTRA_RESOLUTION_CONFIRMATION_CODE}, + * and a boolean for key {@link #EXTRA_RESOLUTION_ALLOW_POLICY_RULES} indicating whether + * the user allows profile policy rules or not. + * @return a DownloadSubscriptionResult instance including a result code, a resolvable errors + * bit map, and original the card Id. The result code may be one of the predefined + * {@code RESULT_} constants or any implementation-specific code starting with + * {@link #RESULT_FIRST_USER}. The resolvable error bit map can be either 0 or values + * defined in {@code RESOLVABLE_ERROR_}. + * @see android.telephony.euicc.EuiccManager#downloadSubscription + */ + public abstract DownloadSubscriptionResult onDownloadSubscription(int slotId, + @NonNull DownloadableSubscription subscription, boolean switchAfterDownload, + boolean forceDeactivateSim, @Nullable Bundle resolvedBundle); + + /** + * Download the given subscription. + * + * @param slotId ID of the SIM slot to use for the operation. + * @param subscription The subscription to download. + * @param switchAfterDownload If true, the subscription should be enabled upon successful + * download. + * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the + * eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM} + * should be returned to allow the user to consent to this operation first. * @return the result of the download operation. May be one of the predefined {@code RESULT_} * constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}. * @see android.telephony.euicc.EuiccManager#downloadSubscription + * + * @deprecated From Q, please use the above + * {@link #onDownloadSubscription(int, DownloadableSubscription, boolean, boolean, Bundle)}. */ - public abstract int onDownloadSubscription(int slotId, - DownloadableSubscription subscription, boolean switchAfterDownload, - boolean forceDeactivateSim); + @Deprecated public @Result int onDownloadSubscription(int slotId, + @NonNull DownloadableSubscription subscription, boolean switchAfterDownload, + boolean forceDeactivateSim) { + throw new UnsupportedOperationException("onDownloadSubscription(int, " + + "DownloadableSubscription, boolean, boolean) is deprecated."); + } /** * Return a list of all @link EuiccProfileInfo}s. @@ -318,7 +421,7 @@ public abstract class EuiccService extends Service { * @see android.telephony.SubscriptionManager#getAvailableSubscriptionInfoList * @see android.telephony.SubscriptionManager#getAccessibleSubscriptionInfoList */ - public abstract GetEuiccProfileInfoListResult onGetEuiccProfileInfoList(int slotId); + public abstract @NonNull GetEuiccProfileInfoListResult onGetEuiccProfileInfoList(int slotId); /** * Return info about the eUICC chip/device. @@ -327,7 +430,7 @@ public abstract class EuiccService extends Service { * @return the {@link EuiccInfo} for the eUICC chip/device. * @see android.telephony.euicc.EuiccManager#getEuiccInfo */ - public abstract EuiccInfo onGetEuiccInfo(int slotId); + public abstract @NonNull EuiccInfo onGetEuiccInfo(int slotId); /** * Delete the given subscription. @@ -341,7 +444,7 @@ public abstract class EuiccService extends Service { * constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}. * @see android.telephony.euicc.EuiccManager#deleteSubscription */ - public abstract int onDeleteSubscription(int slotId, String iccid); + public abstract @Result int onDeleteSubscription(int slotId, String iccid); /** * Switch to the given subscription. @@ -357,7 +460,7 @@ public abstract class EuiccService extends Service { * constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}. * @see android.telephony.euicc.EuiccManager#switchToSubscription */ - public abstract int onSwitchToSubscription(int slotId, @Nullable String iccid, + public abstract @Result int onSwitchToSubscription(int slotId, @Nullable String iccid, boolean forceDeactivateSim); /** @@ -379,8 +482,7 @@ public abstract class EuiccService extends Service { * <p>This is intended to be used for device resets. As such, the reset should be performed even * if an active SIM must be deactivated in order to access the eUICC. * - * @param slotId ID of the SIM slot to use for the operation. This is currently not populated - * but is here to future-proof the APIs. + * @param slotId ID of the SIM slot to use for the operation. * @return the result of the erase operation. May be one of the predefined {@code RESULT_} * constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}. * @see android.telephony.euicc.EuiccManager#eraseSubscriptions @@ -395,8 +497,7 @@ public abstract class EuiccService extends Service { * should persist some bit that will remain accessible after the factory reset to bypass this * flow when this method is called. * - * @param slotId ID of the SIM slot to use for the operation. This is currently not populated - * but is here to future-proof the APIs. + * @param slotId ID of the SIM slot to use for the operation. * @return the result of the operation. May be one of the predefined {@code RESULT_} constants * or any implementation-specific code starting with {@link #RESULT_FIRST_USER}. */ @@ -408,13 +509,26 @@ public abstract class EuiccService extends Service { private class IEuiccServiceWrapper extends IEuiccService.Stub { @Override public void downloadSubscription(int slotId, DownloadableSubscription subscription, - boolean switchAfterDownload, boolean forceDeactivateSim, + boolean switchAfterDownload, boolean forceDeactivateSim, Bundle resolvedBundle, IDownloadSubscriptionCallback callback) { mExecutor.execute(new Runnable() { @Override public void run() { - int result = EuiccService.this.onDownloadSubscription( - slotId, subscription, switchAfterDownload, forceDeactivateSim); + DownloadSubscriptionResult result; + try { + result = + EuiccService.this.onDownloadSubscription( + slotId, subscription, switchAfterDownload, forceDeactivateSim, + resolvedBundle); + } catch (AbstractMethodError e) { + Log.w(TAG, "The new onDownloadSubscription(int, " + + "DownloadableSubscription, boolean, boolean, Bundle) is not " + + "implemented. Fall back to the old one.", e); + int resultCode = EuiccService.this.onDownloadSubscription( + slotId, subscription, switchAfterDownload, forceDeactivateSim); + result = new DownloadSubscriptionResult(resultCode, + 0 /* resolvableErrors */, TelephonyManager.INVALID_CARD_ID); + } try { callback.onComplete(result); } catch (RemoteException e) { diff --git a/core/java/android/service/euicc/IDownloadSubscriptionCallback.aidl b/core/java/android/service/euicc/IDownloadSubscriptionCallback.aidl index 6893c8559d9d..50ecbebf5e84 100644 --- a/core/java/android/service/euicc/IDownloadSubscriptionCallback.aidl +++ b/core/java/android/service/euicc/IDownloadSubscriptionCallback.aidl @@ -16,7 +16,9 @@ package android.service.euicc; +import android.service.euicc.DownloadSubscriptionResult; + /** @hide */ oneway interface IDownloadSubscriptionCallback { - void onComplete(int result); + void onComplete(in DownloadSubscriptionResult result); }
\ No newline at end of file diff --git a/core/java/android/service/euicc/IEuiccService.aidl b/core/java/android/service/euicc/IEuiccService.aidl index 45be52740f32..c2cdf093706f 100644 --- a/core/java/android/service/euicc/IEuiccService.aidl +++ b/core/java/android/service/euicc/IEuiccService.aidl @@ -30,11 +30,12 @@ import android.service.euicc.IRetainSubscriptionsForFactoryResetCallback; import android.service.euicc.ISwitchToSubscriptionCallback; import android.service.euicc.IUpdateSubscriptionNicknameCallback; import android.telephony.euicc.DownloadableSubscription; +import android.os.Bundle; /** @hide */ oneway interface IEuiccService { void downloadSubscription(int slotId, in DownloadableSubscription subscription, - boolean switchAfterDownload, boolean forceDeactivateSim, + boolean switchAfterDownload, boolean forceDeactivateSim, in Bundle resolvedBundle, in IDownloadSubscriptionCallback callback); void getDownloadableSubscriptionMetadata(int slotId, in DownloadableSubscription subscription, boolean forceDeactivateSim, in IGetDownloadableSubscriptionMetadataCallback callback); diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java index c850a4e0f815..dd0242ae2dc8 100644 --- a/core/java/android/service/notification/NotificationAssistantService.java +++ b/core/java/android/service/notification/NotificationAssistantService.java @@ -19,7 +19,7 @@ package android.service.notification; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; -import android.annotation.Nullable; +import android.annotation.NonNull; import android.annotation.SdkConstant; import android.annotation.SystemApi; import android.annotation.TestApi; @@ -179,13 +179,13 @@ public abstract class NotificationAssistantService extends NotificationListenerS * @param isExpanded whether the notification is expanded. */ public void onNotificationExpansionChanged( - String key, boolean isUserAction, boolean isExpanded) {} + @NonNull String key, boolean isUserAction, boolean isExpanded) {} /** * Implement this to know when a direct reply is sent from a notification. * @param key the notification key */ - public void onNotificationDirectReply(String key) {} + public void onNotificationDirectReply(@NonNull String key) {} /** * Implement this to know when a suggested reply is sent. @@ -193,7 +193,9 @@ public abstract class NotificationAssistantService extends NotificationListenerS * @param reply the reply that is just sent * @param source the source that provided the reply, e.g. SOURCE_FROM_APP */ - public void onSuggestedReplySent(String key, CharSequence reply, @Source int source) {} + public void onSuggestedReplySent(@NonNull String key, @NonNull CharSequence reply, + @Source int source) { + } /** * Implement this to know when an action is clicked. @@ -201,7 +203,8 @@ public abstract class NotificationAssistantService extends NotificationListenerS * @param action the action that is just clicked * @param source the source that provided the action, e.g. SOURCE_FROM_APP */ - public void onActionClicked(String key, @Nullable Notification.Action action, int source) { + public void onActionClicked(@NonNull String key, @NonNull Notification.Action action, + @Source int source) { } /** diff --git a/core/java/android/util/SparseSetArray.java b/core/java/android/util/SparseSetArray.java index d100f12ed026..680e85fa2ba8 100644 --- a/core/java/android/util/SparseSetArray.java +++ b/core/java/android/util/SparseSetArray.java @@ -55,6 +55,13 @@ public class SparseSetArray<T> { } /** + * @return the set of items at index n + */ + public ArraySet<T> get(int n) { + return mData.get(n); + } + + /** * Remove a value from index n. * @return TRUE when the value existed at the given index and removed, FALSE otherwise. */ diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 9dfd43cde628..330d72f139db 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -107,12 +107,6 @@ interface IWindowManager */ void endProlongedAnimations(); - // Re-evaluate the current orientation from the caller's state. - // If there is a change, the new Configuration is returned and the - // caller must call setNewConfiguration() sometime later. - Configuration updateOrientationFromAppTokens(in Configuration currentConfig, - IBinder freezeThisOneIfNeeded, int displayId); - void startFreezingScreen(int exitAnim, int enterAnim); void stopFreezingScreen(); diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java new file mode 100644 index 000000000000..7b9f78e70050 --- /dev/null +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.view; + +import static android.view.InsetsState.INSET_SIDE_BOTTOM; +import static android.view.InsetsState.INSET_SIDE_LEFT; +import static android.view.InsetsState.INSET_SIDE_RIGHT; +import static android.view.InsetsState.INSET_SIDE_TOP; + +import android.annotation.Nullable; +import android.graphics.Insets; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.os.UidProto.Sync; +import android.util.ArraySet; +import android.util.SparseArray; +import android.util.SparseIntArray; +import android.util.SparseSetArray; +import android.view.InsetsState.InsetSide; +import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; +import android.view.WindowInsets.Type.InsetType; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.ArrayList; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * Implements {@link WindowInsetsAnimationController} + * @hide + */ +@VisibleForTesting +public class InsetsAnimationControlImpl implements WindowInsetsAnimationController { + + private final WindowInsetsAnimationControlListener mListener; + private final SparseArray<InsetsSourceConsumer> mConsumers; + private final SparseIntArray mTypeSideMap = new SparseIntArray(); + private final SparseSetArray<InsetsSourceConsumer> mSideSourceMap = new SparseSetArray<>(); + + /** @see WindowInsetsAnimationController#getHiddenStateInsets */ + private final Insets mHiddenInsets; + + /** @see WindowInsetsAnimationController#getShownStateInsets */ + private final Insets mShownInsets; + private final Matrix mTmpMatrix = new Matrix(); + private final InsetsState mInitialInsetsState; + private final @InsetType int mTypes; + private final Supplier<SyncRtSurfaceTransactionApplier> mTransactionApplierSupplier; + + private Insets mCurrentInsets; + + @VisibleForTesting + public InsetsAnimationControlImpl(SparseArray<InsetsSourceConsumer> consumers, Rect frame, + InsetsState state, WindowInsetsAnimationControlListener listener, + @InsetType int types, + Supplier<SyncRtSurfaceTransactionApplier> transactionApplierSupplier) { + mConsumers = consumers; + mListener = listener; + mTypes = types; + mTransactionApplierSupplier = transactionApplierSupplier; + mInitialInsetsState = new InsetsState(state); + mCurrentInsets = getInsetsFromState(mInitialInsetsState, frame, null /* typeSideMap */); + mHiddenInsets = calculateInsets(mInitialInsetsState, frame, consumers, false /* shown */, + null /* typeSideMap */); + mShownInsets = calculateInsets(mInitialInsetsState, frame, consumers, true /* shown */, + mTypeSideMap); + buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mConsumers); + + // TODO: Check for controllability first and wait for IME if needed. + listener.onReady(this, types); + } + + @Override + public Insets getHiddenStateInsets() { + return mHiddenInsets; + } + + @Override + public Insets getShownStateInsets() { + return mShownInsets; + } + + @Override + public Insets getCurrentInsets() { + return mCurrentInsets; + } + + @Override + @InsetType + public int getTypes() { + return mTypes; + } + + @Override + public void changeInsets(Insets insets) { + insets = sanitize(insets); + final Insets offset = Insets.subtract(mShownInsets, insets); + ArrayList<SurfaceParams> params = new ArrayList<>(); + if (offset.left != 0) { + updateLeashesForSide(INSET_SIDE_LEFT, offset.left, params); + } + if (offset.top != 0) { + updateLeashesForSide(INSET_SIDE_TOP, offset.top, params); + } + if (offset.right != 0) { + updateLeashesForSide(INSET_SIDE_RIGHT, offset.right, params); + } + if (offset.bottom != 0) { + updateLeashesForSide(INSET_SIDE_BOTTOM, offset.bottom, params); + } + SyncRtSurfaceTransactionApplier applier = mTransactionApplierSupplier.get(); + applier.scheduleApply(params.toArray(new SurfaceParams[params.size()])); + mCurrentInsets = insets; + } + + @Override + public void finish(int shownTypes) { + // TODO + } + + private Insets calculateInsets(InsetsState state, Rect frame, + SparseArray<InsetsSourceConsumer> consumers, boolean shown, + @Nullable @InsetSide SparseIntArray typeSideMap) { + for (int i = consumers.size() - 1; i >= 0; i--) { + state.getSource(consumers.valueAt(i).getType()).setVisible(shown); + } + return getInsetsFromState(state, frame, typeSideMap); + } + + private Insets getInsetsFromState(InsetsState state, Rect frame, + @Nullable @InsetSide SparseIntArray typeSideMap) { + return state.calculateInsets(frame, false /* isScreenRound */, + false /* alwaysConsumerNavBar */, null /* displayCutout */, typeSideMap) + .getSystemWindowInsets(); + } + + private Insets sanitize(Insets insets) { + return Insets.max(Insets.min(insets, mShownInsets), mHiddenInsets); + } + + private void updateLeashesForSide(@InsetSide int side, int inset, + ArrayList<SurfaceParams> surfaceParams) { + ArraySet<InsetsSourceConsumer> items = mSideSourceMap.get(side); + // TODO: Implement behavior when inset spans over multiple types + for (int i = items.size() - 1; i >= 0; i--) { + final InsetsSourceConsumer consumer = items.valueAt(i); + final InsetsSource source = mInitialInsetsState.getSource(consumer.getType()); + final SurfaceControl leash = consumer.getControl().getLeash(); + mTmpMatrix.setTranslate(source.getFrame().left, source.getFrame().top); + addTranslationToMatrix(side, inset, mTmpMatrix); + surfaceParams.add(new SurfaceParams(leash, 1f, mTmpMatrix, null, 0, 0f)); + } + } + + private void addTranslationToMatrix(@InsetSide int side, int inset, Matrix m) { + switch (side) { + case INSET_SIDE_LEFT: + m.postTranslate(-inset, 0); + break; + case INSET_SIDE_TOP: + m.postTranslate(0, -inset); + break; + case INSET_SIDE_RIGHT: + m.postTranslate(inset, 0); + break; + case INSET_SIDE_BOTTOM: + m.postTranslate(0, inset); + break; + } + } + + private static void buildTypeSourcesMap(SparseIntArray typeSideMap, + SparseSetArray<InsetsSourceConsumer> sideSourcesMap, + SparseArray<InsetsSourceConsumer> consumers) { + for (int i = typeSideMap.size() - 1; i >= 0; i--) { + int type = typeSideMap.keyAt(i); + int side = typeSideMap.valueAt(i); + sideSourcesMap.add(side, consumers.get(type)); + } + } +} + diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index fb4f9c03fa68..4ab1f266cc70 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -28,6 +28,7 @@ import android.view.InsetsState.InternalInsetType; import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; +import java.util.ArrayList; /** * Implements {@link WindowInsetsController} on the client. @@ -41,6 +42,7 @@ public class InsetsController implements WindowInsetsController { private final ViewRootImpl mViewRoot; private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>(); + private final ArrayList<InsetsAnimationControlImpl> mAnimationControls = new ArrayList<>(); public InsetsController(ViewRootImpl viewRoot) { mViewRoot = viewRoot; @@ -67,9 +69,11 @@ public class InsetsController implements WindowInsetsController { /** * @see InsetsState#calculateInsets */ - WindowInsets calculateInsets(boolean isScreenRound, + @VisibleForTesting + public WindowInsets calculateInsets(boolean isScreenRound, boolean alwaysConsumeNavBar, DisplayCutout cutout) { - return mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout); + return mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout, + null /* typeSideMap */); } /** @@ -116,6 +120,28 @@ public class InsetsController implements WindowInsetsController { } } + @Override + public void controlWindowInsetsAnimation(@InsetType int types, + WindowInsetsAnimationControlListener listener) { + + // TODO: Check whether we already have a controller. + final ArraySet<Integer> internalTypes = mState.toInternalType(types); + final SparseArray<InsetsSourceConsumer> consumers = new SparseArray<>(); + for (int i = internalTypes.size() - 1; i >= 0; i--) { + InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i)); + if (consumer.getControl() != null) { + consumers.put(consumer.getType(), consumer); + } else { + // TODO: Let calling app know it's not possible, or wait + // TODO: Remove it from types + } + } + final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(consumers, + mFrame, mState, listener, types, + () -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView)); + mAnimationControls.add(controller); + } + private void applyLocalVisibilityOverride() { for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { final InsetsSourceConsumer controller = mSourceConsumers.valueAt(i); @@ -134,7 +160,8 @@ public class InsetsController implements WindowInsetsController { return controller; } - void notifyVisibilityChanged() { + @VisibleForTesting + public void notifyVisibilityChanged() { mViewRoot.notifyInsetsChanged(); } diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java index 0cb8ad72f102..f8148a906bb3 100644 --- a/core/java/android/view/InsetsSource.java +++ b/core/java/android/view/InsetsSource.java @@ -70,7 +70,8 @@ public class InsetsSource implements Parcelable { * * @param relativeFrame The frame to calculate the insets relative to. * @param ignoreVisibility If true, always reports back insets even if source isn't visible. - * @return The resulting insets. + * @return The resulting insets. The contract is that only one side will be occupied by a + * source. */ public Insets calculateInsets(Rect relativeFrame, boolean ignoreVisibility) { if (!ignoreVisibility && !mVisible) { diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index 689b14fe29c6..63025dc16f17 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -17,12 +17,15 @@ package android.view; import android.annotation.IntDef; +import android.annotation.Nullable; import android.graphics.Insets; import android.graphics.Rect; import android.os.Parcel; import android.os.Parcelable; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.SparseArray; +import android.util.SparseIntArray; import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetType; @@ -77,11 +80,30 @@ public class InsetsState implements Parcelable { /** A shelf is the same as the navigation bar. */ public static final int TYPE_SHELF = TYPE_NAVIGATION_BAR; + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "INSET_SIDE", value = { + INSET_SIDE_LEFT, + INSET_SIDE_TOP, + INSET_SIDE_RIGHT, + INSET_SIDE_BOTTOM, + INSET_SIDE_UNKNWON + }) + public @interface InsetSide {} + static final int INSET_SIDE_LEFT = 0; + static final int INSET_SIDE_TOP = 1; + static final int INSET_SIDE_RIGHT = 2; + static final int INSET_SIDE_BOTTOM = 3; + static final int INSET_SIDE_UNKNWON = 4; + private final ArrayMap<Integer, InsetsSource> mSources = new ArrayMap<>(); public InsetsState() { } + public InsetsState(InsetsState copy) { + set(copy); + } + /** * Calculates {@link WindowInsets} based on the current source configuration. * @@ -89,7 +111,8 @@ public class InsetsState implements Parcelable { * @return The calculated insets. */ public WindowInsets calculateInsets(Rect frame, boolean isScreenRound, - boolean alwaysConsumeNavBar, DisplayCutout cutout) { + boolean alwaysConsumeNavBar, DisplayCutout cutout, + @Nullable @InsetSide SparseIntArray typeSideMap) { Insets systemInsets = Insets.NONE; Insets maxInsets = Insets.NONE; final Rect relativeFrame = new Rect(frame); @@ -100,13 +123,13 @@ public class InsetsState implements Parcelable { continue; } systemInsets = processSource(source, systemInsets, relativeFrame, - false /* ignoreVisibility */); + false /* ignoreVisibility */, typeSideMap); // IME won't be reported in max insets as the size depends on the EditorInfo of the IME // target. if (source.getType() != TYPE_IME) { maxInsets = processSource(source, maxInsets, relativeFrameMax, - true /* ignoreVisibility */); + true /* ignoreVisibility */, null /* typeSideMap */); } } return new WindowInsets(new Rect(systemInsets), null, new Rect(maxInsets), isScreenRound, @@ -114,13 +137,39 @@ public class InsetsState implements Parcelable { } private Insets processSource(InsetsSource source, Insets insets, Rect relativeFrame, - boolean ignoreVisibility) { + boolean ignoreVisibility, @Nullable @InsetSide SparseIntArray typeSideMap) { Insets currentInsets = source.calculateInsets(relativeFrame, ignoreVisibility); insets = Insets.add(currentInsets, insets); relativeFrame.inset(insets); + if (typeSideMap != null && !Insets.NONE.equals(currentInsets)) { + @InsetSide int insetSide = getInsetSide(currentInsets); + if (insetSide != INSET_SIDE_UNKNWON) { + typeSideMap.put(source.getType(), getInsetSide(currentInsets)); + } + } return insets; } + /** + * Retrieves the side for a certain {@code insets}. It is required that only one field l/t/r/b + * is set in order that this method returns a meaningful result. + */ + private @InsetSide int getInsetSide(Insets insets) { + if (insets.left != 0) { + return INSET_SIDE_LEFT; + } + if (insets.top != 0) { + return INSET_SIDE_TOP; + } + if (insets.right != 0) { + return INSET_SIDE_RIGHT; + } + if (insets.bottom != 0) { + return INSET_SIDE_BOTTOM; + } + return INSET_SIDE_UNKNWON; + } + public InsetsSource getSource(@InternalInsetType int type) { return mSources.computeIfAbsent(type, InsetsSource::new); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java index 807edf624db8..0270acb3aea7 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java +++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java @@ -11,24 +11,22 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License + * limitations under the License. */ -package com.android.systemui.shared.system; +package android.view; -import android.graphics.HardwareRenderer; import android.graphics.Matrix; import android.graphics.Rect; -import android.view.Surface; -import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; -import android.view.View; -import android.view.ViewRootImpl; + +import com.android.internal.annotations.VisibleForTesting; import java.util.function.Consumer; /** * Helper class to apply surface transactions in sync with RenderThread. + * @hide */ public class SyncRtSurfaceTransactionApplier { @@ -54,30 +52,32 @@ public class SyncRtSurfaceTransactionApplier { if (mTargetViewRootImpl == null) { return; } - mTargetViewRootImpl.registerRtFrameCallback(new HardwareRenderer.FrameDrawingCallback() { - @Override - public void onFrameDraw(long frame) { - if (mTargetSurface == null || !mTargetSurface.isValid()) { - return; - } - Transaction t = new Transaction(); - for (int i = params.length - 1; i >= 0; i--) { - SurfaceParams surfaceParams = params[i]; - SurfaceControl surface = surfaceParams.surface; - t.deferTransactionUntilSurface(surface, mTargetSurface, frame); - applyParams(t, surfaceParams, mTmpFloat9); - } - t.setEarlyWakeup(); - t.apply(); + mTargetViewRootImpl.registerRtFrameCallback(frame -> { + if (mTargetSurface == null || !mTargetSurface.isValid()) { + return; } + Transaction t = new Transaction(); + for (int i = params.length - 1; i >= 0; i--) { + SurfaceParams surfaceParams = params[i]; + SurfaceControl surface = surfaceParams.surface; + t.deferTransactionUntilSurface(surface, mTargetSurface, frame); + applyParams(t, surfaceParams, mTmpFloat9); + } + t.setEarlyWakeup(); + t.apply(); }); // Make sure a frame gets scheduled. mTargetViewRootImpl.getView().invalidate(); } - public static void applyParams(TransactionCompat t, SurfaceParams params) { - applyParams(t.mTransaction, params, t.mTmpValues); + public static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) { + t.setMatrix(params.surface, params.matrix, tmpFloat9); + t.setWindowCrop(params.surface, params.windowCrop); + t.setAlpha(params.surface, params.alpha); + t.setLayer(params.surface, params.layer); + t.setCornerRadius(params.surface, params.cornerRadius); + t.show(params.surface); } /** @@ -109,15 +109,6 @@ public class SyncRtSurfaceTransactionApplier { } } - private static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) { - t.setMatrix(params.surface, params.matrix, tmpFloat9); - t.setWindowCrop(params.surface, params.windowCrop); - t.setAlpha(params.surface, params.alpha); - t.setLayer(params.surface, params.layer); - t.setCornerRadius(params.surface, params.cornerRadius); - t.show(params.surface); - } - public static class SurfaceParams { /** @@ -129,9 +120,9 @@ public class SyncRtSurfaceTransactionApplier { * @param matrix Matrix to apply. * @param windowCrop Crop to apply. */ - public SurfaceParams(SurfaceControlCompat surface, float alpha, Matrix matrix, + public SurfaceParams(SurfaceControl surface, float alpha, Matrix matrix, Rect windowCrop, int layer, float cornerRadius) { - this.surface = surface.mSurfaceControl; + this.surface = surface; this.alpha = alpha; this.matrix = new Matrix(matrix); this.windowCrop = new Rect(windowCrop); @@ -139,11 +130,22 @@ public class SyncRtSurfaceTransactionApplier { this.cornerRadius = cornerRadius; } - final SurfaceControl surface; - final float alpha; - final Matrix matrix; - final Rect windowCrop; - final int layer; + @VisibleForTesting + public final SurfaceControl surface; + + @VisibleForTesting + public final float alpha; + + @VisibleForTesting final float cornerRadius; + + @VisibleForTesting + public final Matrix matrix; + + @VisibleForTesting + public final Rect windowCrop; + + @VisibleForTesting + public final int layer; } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 468d92290c13..57635efff72e 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -9046,17 +9046,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@code onCreate()} and associate it with the root view of the activity: * * <pre> - * ContentCaptureManager mgr = getSystemService(ContentCaptureManager.class); - * if (mgr != null && mgr.isContentCaptureEnabled()) { - * View rootView = findViewById(R.my_root_view); - * ContentCaptureSession session = mgr.createContentCaptureSession(new + * ContentCaptureSession oldSession = rootView.getContentCaptureSession(); + * if (oldSession != null) { + * ContentCaptureSession newSession = oldSession.createContentCaptureSession(new * ContentCaptureContext.Builder().setUri(myUrl).build()); - * rootView.setContentCaptureSession(session); + * rootView.setContentCaptureSession(newSession); * } * </pre> * * @param contentCaptureSession a session created by - * {@link ContentCaptureManager#createContentCaptureSession( + * {@link ContentCaptureSession#createContentCaptureSession( * android.view.contentcapture.ContentCaptureContext)}. */ public void setContentCaptureSession(@NonNull ContentCaptureSession contentCaptureSession) { @@ -10489,6 +10488,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @return The {@link WindowInsetsController} or {@code null} if the view isn't attached to a * a window. + * @see Window#getInsetsController() * @hide pending unhide */ public @Nullable WindowInsetsController getWindowInsetsController() { diff --git a/core/java/android/view/WindowInsetsAnimationControlListener.java b/core/java/android/view/WindowInsetsAnimationControlListener.java new file mode 100644 index 000000000000..b27a23da61b7 --- /dev/null +++ b/core/java/android/view/WindowInsetsAnimationControlListener.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.annotation.NonNull; +import android.view.WindowInsets.Type.InsetType; +import android.view.inputmethod.EditorInfo; + +/** + * Interface that informs the client about {@link WindowInsetsAnimationController} state changes. + * @hide pending unhide + */ +public interface WindowInsetsAnimationControlListener { + + /** + * Gets called as soon as the animation is ready to be controlled. This may be + * delayed when the IME needs to redraw because of an {@link EditorInfo} change, or when the + * window is starting up. + * + * @param controller The controller to control the inset animation. + * @param types The {@link InsetType}s it was able to gain control over. Note that this may be + * different than the types passed into + * {@link WindowInsetsController#controlWindowInsetsAnimation} in case the window + * wasn't able to gain the controls because it wasn't the IME target or not + * currently the window that's controlling the system bars. + */ + void onReady(@NonNull WindowInsetsAnimationController controller, @InsetType int types); + + /** + * Called when the window no longer has control over the requested types. If it loses control + * over one type, the whole control will be cancelled. If none of the requested types were + * available when requesting the control, the animation control will be cancelled immediately + * without {@link #onReady} being called. + */ + void onCancelled(); +} diff --git a/core/java/android/view/WindowInsetsAnimationController.java b/core/java/android/view/WindowInsetsAnimationController.java new file mode 100644 index 000000000000..9de517dac5de --- /dev/null +++ b/core/java/android/view/WindowInsetsAnimationController.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.annotation.NonNull; +import android.graphics.Insets; +import android.view.WindowInsets.Type.InsetType; + +/** + * Interface to control a window inset animation frame-by-frame. + * @hide pending unhide + */ +public interface WindowInsetsAnimationController { + + /** + * Retrieves the {@link Insets} when the windows this animation is controlling are fully hidden. + * + * @return Insets when the windows this animation is controlling are fully hidden. + */ + @NonNull Insets getHiddenStateInsets(); + + /** + * Retrieves the {@link Insets} when the windows this animation is controlling are fully shown. + * <p> + * In case the size of a window causing insets is changing in the middle of the animation, we + * execute that height change after this animation has finished. + * + * @return Insets when the windows this animation is controlling are fully shown. + */ + @NonNull Insets getShownStateInsets(); + + /** + * @return The current insets on the window. These will follow any animation changes. + */ + @NonNull Insets getCurrentInsets(); + + /** + * @return The {@link InsetType}s this object is currently controlling. + */ + @InsetType int getTypes(); + + /** + * Modifies the insets by indirectly moving the windows around in the system that are causing + * window insets. + * <p> + * Note that this will <b>not</b> inform the view system of a full inset change via + * {@link View#dispatchApplyWindowInsets} in order to avoid a full layout pass during the + * animation. If you'd like to animate views during a window inset animation, use + * TODO add link to animation listeners. + * <p> + * {@link View#dispatchApplyWindowInsets} will instead be called once the animation has + * finished, i.e. once {@link #finish} has been called. + * + * @param insets The new insets to apply. Based on the requested insets, the system will + * calculate the positions of the windows in the system causing insets such that + * the resulting insets of that configuration will match the passed in parameter. + * Note that these insets are being clamped to the range from + * {@link #getHiddenStateInsets} to {@link #getShownStateInsets} + */ + void changeInsets(@NonNull Insets insets); + + /** + * @param shownTypes The list of windows causing insets that should remain shown after finishing + * the animation. + */ + void finish(@InsetType int shownTypes); +} diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java index 7be5f2e7a0b0..a35be273f3bf 100644 --- a/core/java/android/view/WindowInsetsController.java +++ b/core/java/android/view/WindowInsetsController.java @@ -16,6 +16,7 @@ package android.view; +import android.annotation.NonNull; import android.view.WindowInsets.Type.InsetType; /** @@ -51,4 +52,15 @@ public interface WindowInsetsController { * would like to make disappear. */ void hide(@InsetType int types); + + /** + * Lets the application control window inset animations in a frame-by-frame manner by modifying + * the position of the windows in the system causing insets directly. + * + * @param types The {@link InsetType}s the application has requested to control. + * @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the + * windows are ready to be controlled, among other callbacks. + */ + void controlWindowInsetsAnimation(@InsetType int types, + @NonNull WindowInsetsAnimationControlListener listener); } diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java new file mode 100644 index 000000000000..51668319cde2 --- /dev/null +++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java @@ -0,0 +1,99 @@ +/* + * 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.annotation.NonNull; +import android.annotation.Nullable; +import android.view.autofill.AutofillId; +import android.view.contentcapture.ViewNode.ViewStructureImpl; + +import com.android.internal.util.Preconditions; + +import java.io.PrintWriter; + +/** + * A session that is explicitly created by the app (and hence is a descendant of + * {@link MainContentCaptureSession}). + * + * @hide + */ +final class ChildContentCaptureSession extends ContentCaptureSession { + + @NonNull + private final MainContentCaptureSession mParent; + + /** + * {@link ContentCaptureContext} set by client, or {@code null} when it's the + * {@link ContentCaptureManager#getMainContentCaptureSession() default session} for the + * context. + * + * @hide + */ + @NonNull + private final ContentCaptureContext mClientContext; + + /** @hide */ + protected ChildContentCaptureSession(@NonNull MainContentCaptureSession parent, + @NonNull ContentCaptureContext clientContext) { + mParent = parent; + mClientContext = Preconditions.checkNotNull(clientContext); + } + + @Override + ContentCaptureSession newChild(@NonNull ContentCaptureContext context) { + // TODO(b/121033016): implement it + throw new UnsupportedOperationException("grand-children not implemented yet"); + } + + @Override + void flush() { + mParent.flush(); + } + + @Override + void onDestroy() { + mParent.notifyChildSessionFinished(mParent.mId, mId); + } + + @Override + void internalNotifyViewAppeared(@NonNull ViewStructureImpl node) { + mParent.notifyViewAppeared(mId, node); + } + + @Override + void internalNotifyViewDisappeared(@NonNull AutofillId id) { + mParent.notifyViewDisappeared(mId, id); + } + + @Override + void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text, + int flags) { + mParent.notifyViewTextChanged(mId, id, text, flags); + } + @Override + boolean isContentCaptureEnabled() { + return mParent.isContentCaptureEnabled(); + } + + @Override + void dump(String prefix, PrintWriter pw) { + if (mClientContext != null) { + // NOTE: we don't dump clientContent because it could have PII + pw.print(prefix); pw.println("hasClientContext"); + } + super.dump(prefix, pw); + } +} diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java index 9c11743fdf19..2d2987a035da 100644 --- a/core/java/android/view/contentcapture/ContentCaptureContext.java +++ b/core/java/android/view/contentcapture/ContentCaptureContext.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.app.TaskInfo; import android.content.ComponentName; +import android.content.Context; import android.net.Uri; import android.os.Bundle; import android.os.Parcel; @@ -76,7 +77,7 @@ public final class ContentCaptureContext implements Parcelable { /** * Flag indicating if this object has the app-provided context (which is set on - * {@link ContentCaptureManager#createContentCaptureSession(ContentCaptureContext)}). + * {@link ContentCaptureSession#createContentCaptureSession(ContentCaptureContext)}). */ private final boolean mHasClientContext; @@ -91,6 +92,9 @@ public final class ContentCaptureContext implements Parcelable { private final int mDisplayId; private final int mFlags; + // Fields below are set by the service upon "delivery" and are not marshalled in the parcel + private @Nullable String mParentSessionId; + /** @hide */ public ContentCaptureContext(@Nullable ContentCaptureContext clientContext, @NonNull ComponentName componentName, int taskId, int displayId, int flags) { @@ -153,16 +157,33 @@ public final class ContentCaptureContext implements Parcelable { } /** - * Gets the activity associated with this context. + * Gets the activity associated with this context, or {@code null} when it is a child session. * * @hide */ @SystemApi - public @NonNull ComponentName getActivityComponent() { + public @Nullable ComponentName getActivityComponent() { return mComponentName; } /** + * Gets the id of the session that originated this session (through + * {@link ContentCaptureSession#createContentCaptureSession(ContentCaptureContext)}), + * or {@code null} if this is the main session associated with the Activity's {@link Context}. + * + * @hide + */ + @SystemApi + public @Nullable ContentCaptureSessionId getParentSessionId() { + return mParentSessionId == null ? null : new ContentCaptureSessionId(mParentSessionId); + } + + /** @hide */ + public void setParentSessionId(@NonNull String parentSessionId) { + mParentSessionId = parentSessionId; + } + + /** * Gets the ID of the display associated with this context, as defined by * {G android.hardware.display.DisplayManager#getDisplay(int) DisplayManager.getDisplay()}. * @@ -242,6 +263,9 @@ public final class ContentCaptureContext implements Parcelable { pw.print("comp="); pw.print(ComponentName.flattenToShortString(mComponentName)); pw.print(", taskId="); pw.print(mTaskId); pw.print(", displayId="); pw.print(mDisplayId); + if (mParentSessionId != null) { + pw.print(", parentId="); pw.print(mParentSessionId); + } if (mFlags > 0) { pw.print(", flags="); pw.print(mFlags); } @@ -262,6 +286,9 @@ public final class ContentCaptureContext implements Parcelable { .append(", taskId=").append(mTaskId) .append(", displayId=").append(mDisplayId) .append(", flags=").append(mFlags); + if (mParentSessionId != null) { + builder.append(", parentId=").append(mParentSessionId); + } if (mExtras != null) { // NOTE: cannot print because it could contain PII builder.append(", hasExtras"); @@ -320,9 +347,9 @@ public final class ContentCaptureContext implements Parcelable { final int taskId = parcel.readInt(); final int displayId = parcel.readInt(); final int flags = parcel.readInt(); - return new ContentCaptureContext(clientContext, componentName, taskId, - displayId, flags); - } + return new ContentCaptureContext(clientContext, componentName, taskId, displayId, + flags); + } } @Override diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java index 43c9699b54b8..9e3da9234b58 100644 --- a/core/java/android/view/contentcapture/ContentCaptureEvent.java +++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java @@ -34,9 +34,9 @@ import java.lang.annotation.RetentionPolicy; public final class ContentCaptureEvent implements Parcelable { /** @hide */ - public static final int TYPE_ACTIVITY_DESTROYED = -2; + public static final int TYPE_SESSION_FINISHED = -2; /** @hide */ - public static final int TYPE_ACTIVITY_CREATED = -1; + public static final int TYPE_SESSION_STARTED = -1; /** * Called when a node has been added to the screen and is visible to the user. @@ -78,6 +78,8 @@ public final class ContentCaptureEvent implements Parcelable { private @Nullable AutofillId mId; private @Nullable ViewNode mNode; private @Nullable CharSequence mText; + private @Nullable String mParentSessionId; + private @Nullable ContentCaptureContext mClientContext; /** @hide */ public ContentCaptureEvent(@NonNull String sessionId, int type, long eventTime, int flags) { @@ -103,12 +105,52 @@ public final class ContentCaptureEvent implements Parcelable { return this; } + /** + * Used by {@link #TYPE_SESSION_STARTED} and {@link #TYPE_SESSION_FINISHED}. + * + * @hide + */ + public ContentCaptureEvent setParentSessionId(@NonNull String parentSessionId) { + mParentSessionId = parentSessionId; + return this; + } + + /** + * Used by {@link #TYPE_SESSION_STARTED} and {@link #TYPE_SESSION_FINISHED}. + * + * @hide + */ + public ContentCaptureEvent setClientContext(@NonNull ContentCaptureContext clientContext) { + mClientContext = clientContext; + return this; + } + /** @hide */ @NonNull public String getSessionId() { return mSessionId; } + /** + * Used by {@link #TYPE_SESSION_STARTED} and {@link #TYPE_SESSION_FINISHED}. + * + * @hide + */ + @Nullable + public String getParentSessionId() { + return mParentSessionId; + } + + /** + * Used by {@link #TYPE_SESSION_STARTED}. + * + * @hide + */ + @Nullable + public ContentCaptureContext getClientContext() { + return mClientContext; + } + /** @hide */ @NonNull public ContentCaptureEvent setViewNode(@NonNull ViewNode node) { @@ -191,7 +233,17 @@ public final class ContentCaptureEvent implements Parcelable { pw.print(", id="); pw.print(mId); } if (mNode != null) { - pw.print(", id="); pw.print(mNode.getAutofillId()); + pw.print(", mNode.id="); pw.print(mNode.getAutofillId()); + } + if (mSessionId != null) { + pw.print(", sessionId="); pw.print(mSessionId); + } + if (mParentSessionId != null) { + pw.print(", parentSessionId="); pw.print(mParentSessionId); + } + if (mText != null) { + // Cannot print content because could have PII + pw.print(", text="); pw.print(mText.length()); pw.print("_chars"); } } @@ -229,6 +281,12 @@ public final class ContentCaptureEvent implements Parcelable { parcel.writeParcelable(mId, flags); ViewNode.writeToParcel(parcel, mNode, flags); parcel.writeCharSequence(mText); + if (mType == TYPE_SESSION_STARTED || mType == TYPE_SESSION_FINISHED) { + parcel.writeString(mParentSessionId); + } + if (mType == TYPE_SESSION_STARTED) { + parcel.writeParcelable(mClientContext, flags); + } } public static final Parcelable.Creator<ContentCaptureEvent> CREATOR = @@ -251,6 +309,12 @@ public final class ContentCaptureEvent implements Parcelable { event.setViewNode(node); } event.setText(parcel.readCharSequence()); + if (type == TYPE_SESSION_STARTED || type == TYPE_SESSION_FINISHED) { + event.setParentSessionId(parcel.readString()); + } + if (type == TYPE_SESSION_STARTED) { + event.setClientContext(parcel.readParcelable(null)); + } return event; } @@ -263,6 +327,10 @@ public final class ContentCaptureEvent implements Parcelable { /** @hide */ public static String getTypeAsString(@EventType int type) { switch (type) { + case TYPE_SESSION_STARTED: + return "SESSION_STARTED"; + case TYPE_SESSION_FINISHED: + return "SESSION_FINISHED"; case TYPE_VIEW_APPEARED: return "VIEW_APPEARED"; case TYPE_VIEW_DISAPPEARED: diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index fca2857d6e06..983079073d02 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -18,13 +18,13 @@ package android.view.contentcapture; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemService; +import android.annotation.UiThread; import android.content.ComponentName; import android.content.Context; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.util.Log; -import android.view.View; import com.android.internal.util.Preconditions; @@ -66,7 +66,7 @@ public final class ContentCaptureManager { @NonNull private final Handler mHandler; - private ActivityContentCaptureSession mMainSession; + private MainContentCaptureSession mMainSession; /** @hide */ public ContentCaptureManager(@NonNull Context context, @@ -93,46 +93,20 @@ public final class ContentCaptureManager { } /** - * Creates a new {@link ContentCaptureSession}. - * - * <p>See {@link View#setContentCaptureSession(ContentCaptureSession)} for more info. - */ - @NonNull - public ContentCaptureSession createContentCaptureSession( - @NonNull ContentCaptureContext context) { - if (DEBUG) Log.d(TAG, "createContentCaptureSession(): " + context); - // TODO(b/121033016): for now we're updating the main session, but we need instead: - // 1.Keep a list of sessions - // 2.Making sure the applicationToken and componentName passed by - // the activity is used on all of these sessions - // 3.We might also need to delay the start of all of these sessions until - // onActivityStarted() is called (and the main session is created). - // 4.Close (and delete) these sessions when onActivityStopped() is called. - // 5.Figure out whether each session will have its own mDisabled AtomicBoolean. - if (mMainSession == null) { - mMainSession = new ActivityContentCaptureSession(mContext, mHandler, mService, - mDisabled, Preconditions.checkNotNull(context)); - } else { - throw new IllegalStateException("Manager already has a session: " + mMainSession); - } - return mMainSession; - } - - /** * Gets the main session associated with the context. * * <p>By default there's just one (associated with the activity lifecycle), but apps could - * explicitly add more using {@link #createContentCaptureSession(ContentCaptureContext)}. + * explicitly add more using + * {@link ContentCaptureSession#createContentCaptureSession(ContentCaptureContext)}. * * @hide */ @NonNull - public ActivityContentCaptureSession getMainContentCaptureSession() { - // TODO(b/121033016): figure out how to manage the "default" session when it support - // multiple sessions (can't just be the first one, as it could be closed). + @UiThread + public MainContentCaptureSession getMainContentCaptureSession() { if (mMainSession == null) { - mMainSession = new ActivityContentCaptureSession(mContext, mHandler, mService, - mDisabled, /* clientContext= */ null); + mMainSession = new MainContentCaptureSession(mContext, mHandler, mService, + mDisabled); if (VERBOSE) { Log.v(TAG, "getDefaultContentCaptureSession(): created " + mMainSession); } diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index aedb7a94ff5d..9f666a40bef3 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -15,8 +15,10 @@ */ package android.view.contentcapture; +import static android.view.contentcapture.ContentCaptureManager.DEBUG; import static android.view.contentcapture.ContentCaptureManager.VERBOSE; +import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.Log; @@ -30,6 +32,7 @@ import com.android.internal.util.Preconditions; import dalvik.system.CloseGuard; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.UUID; /** @@ -80,6 +83,8 @@ public abstract class ContentCaptureSession implements AutoCloseable { */ public static final int STATE_DISABLED_DUPLICATED_ID = 4; + private static final int INITIAL_CHILDREN_CAPACITY = 5; + /** @hide */ protected final String mTag = getClass().getSimpleName(); @@ -95,19 +100,17 @@ public abstract class ContentCaptureSession implements AutoCloseable { private ContentCaptureSessionId mContentCaptureSessionId; /** - * {@link ContentCaptureContext} set by client, or {@code null} when it's the - * {@link ContentCaptureManager#getMainContentCaptureSession() default session} for the - * context. - * - * @hide + * List of children session. */ + // TODO(b/121033016): need to synchonize access, either by changing on handler or UI thread + // (for now there's no handler on this class, so we need to wait for the next refactoring), + // most likely the former (as we have no guarantee that createContentCaptureSession() + // it will be called in the UiThread; for example, WebView most likely won't call on it) @Nullable - // TODO(b/121042846): move to ChildContentCaptureSession.java - protected final ContentCaptureContext mClientContext; + private ArrayList<ContentCaptureSession> mChildren; /** @hide */ - protected ContentCaptureSession(@Nullable ContentCaptureContext clientContext) { - mClientContext = clientContext; + protected ContentCaptureSession() { mCloseGuard.open("destroy"); } @@ -122,6 +125,28 @@ public abstract class ContentCaptureSession implements AutoCloseable { } /** + * Creates a new {@link ContentCaptureSession}. + * + * <p>See {@link View#setContentCaptureSession(ContentCaptureSession)} for more info. + */ + @NonNull + public final ContentCaptureSession createContentCaptureSession( + @NonNull ContentCaptureContext context) { + final ContentCaptureSession child = newChild(context); + if (DEBUG) { + Log.d(mTag, "createContentCaptureSession(" + context + ": parent=" + mId + ", child= " + + child.mId); + } + if (mChildren == null) { + mChildren = new ArrayList<>(INITIAL_CHILDREN_CAPACITY); + } + mChildren.add(child); + return child; + } + + abstract ContentCaptureSession newChild(@NonNull ContentCaptureContext context); + + /** * Flushes the buffered events to the service. */ abstract void flush(); @@ -134,24 +159,40 @@ public abstract class ContentCaptureSession implements AutoCloseable { public final void destroy() { //TODO(b/111276913): mark it as destroyed so other methods are ignored (and test on CTS) + //TODO(b/111276913): probably shouldn't check for it if (!isContentCaptureEnabled()) return; + mCloseGuard.close(); + //TODO(b/111276913): check state (for example, how to handle if it's waiting for remote // id) and send it to the cache of batched commands if (VERBOSE) { Log.v(mTag, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId); } - flush(); - - onDestroy(); + // Finish children first + if (mChildren != null) { + final int numberChildren = mChildren.size(); + if (VERBOSE) Log.v(mTag, "Destroying " + numberChildren + " children first"); + for (int i = 0; i < numberChildren; i++) { + final ContentCaptureSession child = mChildren.get(i); + try { + child.destroy(); + } catch (Exception e) { + Log.w(mTag, "exception destroying child session #" + i + ": " + e); + } + } + } - mCloseGuard.close(); + try { + flush(); + } finally { + onDestroy(); + } } abstract void onDestroy(); - /** @hide */ @Override public void close() { @@ -259,7 +300,19 @@ public abstract class ContentCaptureSession implements AutoCloseable { abstract boolean isContentCaptureEnabled(); - abstract void dump(@NonNull String prefix, @NonNull PrintWriter pw); + @CallSuper + void dump(@NonNull String prefix, @NonNull PrintWriter pw) { + if (mChildren != null && !mChildren.isEmpty()) { + final String prefix2 = prefix + " "; + final int numberChildren = mChildren.size(); + pw.print(prefix); pw.print("number children: "); pw.print(numberChildren); + for (int i = 0; i < numberChildren; i++) { + final ContentCaptureSession child = mChildren.get(i); + pw.print(prefix); pw.print(i); pw.println(": "); child.dump(prefix2, pw); + } + } + + } @Override public String toString() { diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl index cbd37017038e..01776f846434 100644 --- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl +++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl @@ -33,7 +33,6 @@ import java.util.List; */ oneway interface IContentCaptureManager { void startSession(int userId, IBinder activityToken, in ComponentName componentName, - String sessionId, in ContentCaptureContext clientContext, int flags, - in IResultReceiver result); + String sessionId, int flags, in IResultReceiver result); void finishSession(int userId, String sessionId); } diff --git a/core/java/android/view/contentcapture/ActivityContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index a8f3e0bbd45d..ea6f2fe16ef6 100644 --- a/core/java/android/view/contentcapture/ActivityContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -15,6 +15,8 @@ */ package android.view.contentcapture; +import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED; +import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED; @@ -59,7 +61,7 @@ import java.util.concurrent.atomic.AtomicBoolean; * * @hide */ -public final class ActivityContentCaptureSession extends ContentCaptureSession { +public final class MainContentCaptureSession extends ContentCaptureSession { /** * Handler message used to flush the buffer. @@ -129,18 +131,23 @@ public final class ActivityContentCaptureSession extends ContentCaptureSession { // Lazily created on demand. private ContentCaptureSessionId mContentCaptureSessionId; - /** - * @hide */ - protected ActivityContentCaptureSession(@NonNull Context context, @NonNull Handler handler, - @Nullable IContentCaptureManager systemServerInterface, @NonNull AtomicBoolean disabled, - @Nullable ContentCaptureContext clientContext) { - super(clientContext); + /** @hide */ + protected MainContentCaptureSession(@NonNull Context context, @NonNull Handler handler, + @Nullable IContentCaptureManager systemServerInterface, + @NonNull AtomicBoolean disabled) { mContext = context; mHandler = handler; mSystemServerInterface = systemServerInterface; mDisabled = disabled; } + @Override + ContentCaptureSession newChild(@NonNull ContentCaptureContext clientContext) { + final ContentCaptureSession child = new ChildContentCaptureSession(this, clientContext); + notifyChildSessionStarted(mId, child.mId, clientContext); + return child; + } + /** * Starts this session. * @@ -154,19 +161,19 @@ public final class ActivityContentCaptureSession extends ContentCaptureSession { + ComponentName.flattenToShortString(activityComponent)); } - mHandler.sendMessage(obtainMessage(ActivityContentCaptureSession::handleStartSession, this, + mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleStartSession, this, applicationToken, activityComponent)); } @Override void flush() { - mHandler.sendMessage(obtainMessage(ActivityContentCaptureSession::handleForceFlush, this)); + mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleForceFlush, this)); } @Override void onDestroy() { mHandler.sendMessage( - obtainMessage(ActivityContentCaptureSession::handleDestroySession, this)); + obtainMessage(MainContentCaptureSession::handleDestroySession, this)); } private void handleStartSession(@NonNull IBinder token, @NonNull ComponentName componentName) { @@ -188,7 +195,7 @@ public final class ActivityContentCaptureSession extends ContentCaptureSession { try { mSystemServerInterface.startSession(mContext.getUserId(), mApplicationToken, - componentName, mId, mClientContext, flags, new IResultReceiver.Stub() { + componentName, mId, flags, new IResultReceiver.Stub() { @Override public void send(int resultCode, Bundle resultData) { IBinder binder = null; @@ -296,7 +303,7 @@ public final class ActivityContentCaptureSession extends ContentCaptureSession { Log.v(mTag, "Scheduled to flush in " + FLUSHING_FREQUENCY_MS + "ms: " + mNextFlush); } mHandler.sendMessageDelayed( - obtainMessage(ActivityContentCaptureSession::handleFlushIfNeeded, this) + obtainMessage(MainContentCaptureSession::handleFlushIfNeeded, this) .setWhat(MSG_FLUSH), FLUSHING_FREQUENCY_MS); } @@ -312,7 +319,7 @@ public final class ActivityContentCaptureSession extends ContentCaptureSession { if (mEvents == null) return; if (mDirectServiceInterface == null) { - Log.w(mTag, "handleForceFlush(): client not available yet"); + if (DEBUG) Log.d(mTag, "handleForceFlush(): hold your horses, client not ready yet!"); if (!mHandler.hasMessages(MSG_FLUSH)) { handleScheduleFlush(/* checkExisting= */ false); } @@ -367,12 +374,15 @@ public final class ActivityContentCaptureSession extends ContentCaptureSession { handleResetSession(/* resetState= */ true); } - // TODO(b/121042846): once we support multiple sessions, we might need to move some of these + // TODO(b/121033016): once we support multiple sessions, we might need to move some of these // clearings out. private void handleResetSession(boolean resetState) { if (resetState) { mState = STATE_UNKNOWN; } + + // TODO(b/121033016): must reset children (which currently is owned by superclass) + mContentCaptureSessionId = null; mApplicationToken = null; mComponentName = null; @@ -386,24 +396,18 @@ public final class ActivityContentCaptureSession extends ContentCaptureSession { @Override void internalNotifyViewAppeared(@NonNull ViewStructureImpl node) { - mHandler.sendMessage(obtainMessage(ActivityContentCaptureSession::handleSendEvent, this, - new ContentCaptureEvent(mId, TYPE_VIEW_APPEARED) - .setViewNode(node.mNode), /* forceFlush= */ false)); + notifyViewAppeared(mId, node); } @Override void internalNotifyViewDisappeared(@NonNull AutofillId id) { - mHandler.sendMessage(obtainMessage(ActivityContentCaptureSession::handleSendEvent, this, - new ContentCaptureEvent(mId, TYPE_VIEW_DISAPPEARED).setAutofillId(id), - /* forceFlush= */ false)); + notifyViewDisappeared(mId, id); } @Override void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text, int flags) { - mHandler.sendMessage(obtainMessage(ActivityContentCaptureSession::handleSendEvent, this, - new ContentCaptureEvent(mId, TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id) - .setText(text), /* forceFlush= */ false)); + notifyViewTextChanged(mId, id, text, flags); } @Override @@ -411,6 +415,44 @@ public final class ActivityContentCaptureSession extends ContentCaptureSession { return mSystemServerInterface != null && !mDisabled.get(); } + // TODO(b/121033016): refactor "notifyXXXX" methods below to a common "Buffer" object that is + // shared between ActivityContentCaptureSession and ChildContentCaptureSession objects. Such + // change should also get get rid of the "internalNotifyXXXX" methods above + void notifyChildSessionStarted(@NonNull String parentSessionId, + @NonNull String childSessionId, @NonNull ContentCaptureContext clientContext) { + mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this, + new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED) + .setParentSessionId(parentSessionId) + .setClientContext(clientContext), + /* forceFlush= */ false)); + } + + void notifyChildSessionFinished(@NonNull String parentSessionId, + @NonNull String childSessionId) { + mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this, + new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED) + .setParentSessionId(parentSessionId), /* forceFlush= */ false)); + } + + void notifyViewAppeared(@NonNull String sessionId, @NonNull ViewStructureImpl node) { + mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this, + new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED) + .setViewNode(node.mNode), /* forceFlush= */ false)); + } + + void notifyViewDisappeared(@NonNull String sessionId, @NonNull AutofillId id) { + mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this, + new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED).setAutofillId(id), + /* forceFlush= */ false)); + } + + void notifyViewTextChanged(@NonNull String sessionId, @NonNull AutofillId id, + @Nullable CharSequence text, int flags) { + mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this, + new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id) + .setText(text), /* forceFlush= */ false)); + } + @Override void dump(@NonNull String prefix, @NonNull PrintWriter pw) { pw.print(prefix); pw.print("id: "); pw.println(mId); @@ -424,11 +466,6 @@ public final class ActivityContentCaptureSession extends ContentCaptureSession { pw.print(prefix); pw.print("mDirectServiceInterface: "); pw.println(mDirectServiceInterface); } - if (mClientContext != null) { - // NOTE: we don't dump clientContent because it could have PII - pw.print(prefix); pw.println("hasClientContext"); - - } pw.print(prefix); pw.print("mDisabled: "); pw.println(mDisabled.get()); pw.print(prefix); pw.print("isEnabled(): "); pw.println(isContentCaptureEnabled()); if (mContentCaptureSessionId != null) { @@ -459,6 +496,7 @@ public final class ActivityContentCaptureSession extends ContentCaptureSession { pw.print(prefix); pw.print("next flush: "); TimeUtils.formatDuration(mNextFlush - SystemClock.elapsedRealtime(), pw); pw.println(); } + super.dump(prefix, pw); } /** diff --git a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java index b41096c74bf7..77cb4cd28763 100644 --- a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java +++ b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java @@ -17,6 +17,7 @@ package android.view.textclassifier; import android.app.Person; +import android.content.Context; import android.text.TextUtils; import android.util.ArrayMap; @@ -28,7 +29,10 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.Objects; +import java.util.StringJoiner; import java.util.function.Function; import java.util.stream.Collectors; @@ -84,6 +88,29 @@ public final class ActionsSuggestionsHelper { new ActionsSuggestionsModel.ConversationMessage[nativeMessages.size()]); } + /** + * Returns the result id for logging. + */ + public static String createResultId( + Context context, + List<ConversationActions.Message> messages, + int modelVersion, + List<Locale> modelLocales) { + final StringJoiner localesJoiner = new StringJoiner(","); + for (Locale locale : modelLocales) { + localesJoiner.add(locale.toLanguageTag()); + } + final String modelName = String.format( + Locale.US, "%s_v%d", localesJoiner.toString(), modelVersion); + final int hash = Objects.hash( + messages.stream() + .map(ConversationActions.Message::getText) + .collect(Collectors.toList()), + context.getPackageName()); + return SelectionSessionLogger.SignatureParser.createSignature( + SelectionSessionLogger.CLASSIFIER_ID, modelName, hash); + } + private static final class PersonEncoder { private final Map<Person, Integer> mMapping = new ArrayMap<>(); private int mNextUserId = FIRST_NON_LOCAL_USER; diff --git a/core/java/android/view/textclassifier/TextClassifierEvent.java b/core/java/android/view/textclassifier/TextClassifierEvent.java index 3bb9ee88dae9..f2fea02171f9 100644 --- a/core/java/android/view/textclassifier/TextClassifierEvent.java +++ b/core/java/android/view/textclassifier/TextClassifierEvent.java @@ -27,6 +27,7 @@ import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; /** * A text classifier event. @@ -498,4 +499,25 @@ public final class TextClassifierEvent implements Parcelable { } // TODO: Add build(boolean validate). } + + @Override + public String toString() { + StringBuilder out = new StringBuilder(128); + out.append("TextClassifierEvent{"); + out.append("mEventCategory=").append(mEventCategory); + out.append(", mEventType=").append(mEventType); + out.append(", mEventContext=").append(mEventContext); + out.append(", mResultId=").append(mResultId); + out.append(", mEventIndex=").append(mEventIndex); + out.append(", mEventTime=").append(mEventTime); + out.append(", mExtras=").append(mExtras); + out.append(", mRelativeWordStartIndex=").append(mRelativeWordStartIndex); + out.append(", mRelativeWordEndIndex=").append(mRelativeWordEndIndex); + out.append(", mRelativeSuggestedWordStartIndex=").append(mRelativeSuggestedWordStartIndex); + out.append(", mRelativeSuggestedWordEndIndex=").append(mRelativeSuggestedWordEndIndex); + out.append(", mActionIndices=").append(Arrays.toString(mActionIndices)); + out.append(", mLanguage=").append(mLanguage); + out.append("}"); + return out.toString(); + } } diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java index 9b0f9c6ee5e8..fcd06c384921 100644 --- a/core/java/android/view/textclassifier/TextClassifierImpl.java +++ b/core/java/android/view/textclassifier/TextClassifierImpl.java @@ -80,6 +80,8 @@ public final class TextClassifierImpl implements TextClassifier { private static final String LOG_TAG = DEFAULT_LOG_TAG; + private static final boolean DEBUG = false; + private static final File FACTORY_MODEL_DIR = new File("/etc/textclassifier/"); // Annotator private static final String ANNOTATOR_FACTORY_MODEL_FILENAME_REGEX = @@ -109,6 +111,8 @@ public final class TextClassifierImpl implements TextClassifier { @GuardedBy("mLock") // Do not access outside this lock. private LangIdModel mLangIdImpl; @GuardedBy("mLock") // Do not access outside this lock. + private ModelFileManager.ModelFile mActionModelInUse; + @GuardedBy("mLock") // Do not access outside this lock. private ActionsSuggestionsModel mActionsImpl; private final Object mLoggerLock = new Object(); @@ -342,8 +346,10 @@ public final class TextClassifierImpl implements TextClassifier { } @Override - public void onTextClassifierEvent(@NonNull TextClassifierEvent event) { - // TODO: Implement. + public void onTextClassifierEvent(TextClassifierEvent event) { + if (DEBUG) { + Log.d(DEFAULT_LOG_TAG, "onTextClassifierEvent() called with: event = [" + event + "]"); + } } /** @inheritDoc */ @@ -408,7 +414,12 @@ public final class TextClassifierImpl implements TextClassifier { .setConfidenceScore(nativeSuggestion.getScore()) .build()); } - return new ConversationActions(conversationActions, /*id*/ null); + String resultId = ActionsSuggestionsHelper.createResultId( + mContext, + request.getConversation(), + mActionModelInUse.getVersion(), + mActionModelInUse.getSupportedLocales()); + return new ConversationActions(conversationActions, resultId); } catch (Throwable t) { // Avoid throwing from this method. Log the error. Log.e(LOG_TAG, "Error suggesting conversation actions.", t); @@ -517,6 +528,7 @@ public final class TextClassifierImpl implements TextClassifier { try { if (pfd != null) { mActionsImpl = new ActionsSuggestionsModel(pfd.getFd()); + mActionModelInUse = bestModel; } } finally { maybeCloseAndLogError(pfd); diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 12ca78a7ce92..eb7338a7ce58 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -45,7 +45,7 @@ namespace android { class BitmapWrapper { public: - BitmapWrapper(Bitmap* bitmap) + explicit BitmapWrapper(Bitmap* bitmap) : mBitmap(bitmap) { } void freePixels() { diff --git a/core/jni/android/graphics/FontUtils.h b/core/jni/android/graphics/FontUtils.h index 9f6462e67050..b36b4e60e33a 100644 --- a/core/jni/android/graphics/FontUtils.h +++ b/core/jni/android/graphics/FontUtils.h @@ -29,7 +29,7 @@ class FontFamily; namespace android { struct FontFamilyWrapper { - FontFamilyWrapper(std::shared_ptr<minikin::FontFamily>&& family) : family(family) {} + explicit FontFamilyWrapper(std::shared_ptr<minikin::FontFamily>&& family) : family(family) {} std::shared_ptr<minikin::FontFamily> family; }; diff --git a/core/jni/android/graphics/GIFMovie.cpp b/core/jni/android/graphics/GIFMovie.cpp index dd99b377988f..f84a4bd09073 100644 --- a/core/jni/android/graphics/GIFMovie.cpp +++ b/core/jni/android/graphics/GIFMovie.cpp @@ -21,7 +21,7 @@ class GIFMovie : public Movie { public: - GIFMovie(SkStream* stream); + explicit GIFMovie(SkStream* stream); virtual ~GIFMovie(); protected: diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index 516093e42691..1065738466e7 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -20,7 +20,6 @@ #include "android_media_AudioTrack.h" #include <nativehelper/JNIHelp.h> -#include <nativehelper/JniConstants.h> #include "core_jni_helpers.h" #include <utils/Log.h> diff --git a/core/jni/android_media_DeviceCallback.cpp b/core/jni/android_media_DeviceCallback.cpp index 108fa00fe510..a1a035110caf 100644 --- a/core/jni/android_media_DeviceCallback.cpp +++ b/core/jni/android_media_DeviceCallback.cpp @@ -20,7 +20,6 @@ #include <utils/Log.h> #include <nativehelper/JNIHelp.h> -#include <nativehelper/JniConstants.h> #include "core_jni_helpers.h" #include <media/AudioSystem.h> diff --git a/core/jni/android_os_SharedMemory.cpp b/core/jni/android_os_SharedMemory.cpp index f6e5c7a48c4c..c33405def25e 100644 --- a/core/jni/android_os_SharedMemory.cpp +++ b/core/jni/android_os_SharedMemory.cpp @@ -20,8 +20,9 @@ #include <cutils/ashmem.h> #include <utils/Log.h> + +#include <nativehelper/jni_macros.h> #include <nativehelper/JNIHelp.h> -#include <nativehelper/JniConstants.h> #include <nativehelper/ScopedLocalRef.h> #include <algorithm> @@ -31,10 +32,10 @@ namespace { -static void throwErrnoException(JNIEnv* env, const char* functionName, int error) { - static jmethodID ctor = env->GetMethodID(JniConstants::errnoExceptionClass, - "<init>", "(Ljava/lang/String;I)V"); +jclass errnoExceptionClass; +jmethodID errnoExceptionCtor; // MethodID for ErrnoException.<init>(String,I) +void throwErrnoException(JNIEnv* env, const char* functionName, int error) { ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName)); if (detailMessage.get() == NULL) { // Not really much we can do here. We're probably dead in the water, @@ -42,12 +43,14 @@ static void throwErrnoException(JNIEnv* env, const char* functionName, int error env->ExceptionClear(); } - jobject exception = env->NewObject(JniConstants::errnoExceptionClass, ctor, - detailMessage.get(), error); + jobject exception = env->NewObject(errnoExceptionClass, + errnoExceptionCtor, + detailMessage.get(), + error); env->Throw(reinterpret_cast<jthrowable>(exception)); } -static jobject SharedMemory_create(JNIEnv* env, jobject, jstring jname, jint size) { +jobject SharedMemory_nCreate(JNIEnv* env, jobject, jstring jname, jint size) { // Name is optional so we can't use ScopedUtfChars for this as it throws NPE on null const char* name = jname ? env->GetStringUTFChars(jname, nullptr) : nullptr; @@ -69,7 +72,7 @@ static jobject SharedMemory_create(JNIEnv* env, jobject, jstring jname, jint siz return jniCreateFileDescriptor(env, fd); } -static jint SharedMemory_getSize(JNIEnv* env, jobject, jobject fileDescriptor) { +jint SharedMemory_nGetSize(JNIEnv* env, jobject, jobject fileDescriptor) { int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); if (!ashmem_valid(fd)) { return -1; @@ -78,7 +81,7 @@ static jint SharedMemory_getSize(JNIEnv* env, jobject, jobject fileDescriptor) { return static_cast<jint>(std::min(size, static_cast<size_t>(std::numeric_limits<jint>::max()))); } -static jint SharedMemory_setProt(JNIEnv* env, jobject, jobject fileDescriptor, jint prot) { +jint SharedMemory_nSetProt(JNIEnv* env, jobject, jobject fileDescriptor, jint prot) { int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); int err = 0; if (ashmem_set_prot_region(fd, prot)) { @@ -87,18 +90,21 @@ static jint SharedMemory_setProt(JNIEnv* env, jobject, jobject fileDescriptor, j return err; } -static const JNINativeMethod methods[] = { - {"nCreate", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)SharedMemory_create}, - {"nGetSize", "(Ljava/io/FileDescriptor;)I", (void*)SharedMemory_getSize}, - {"nSetProt", "(Ljava/io/FileDescriptor;I)I", (void*)SharedMemory_setProt}, +const JNINativeMethod methods[] = { + NATIVE_METHOD(SharedMemory, nCreate, "(Ljava/lang/String;I)Ljava/io/FileDescriptor;"), + NATIVE_METHOD(SharedMemory, nGetSize, "(Ljava/io/FileDescriptor;)I"), + NATIVE_METHOD(SharedMemory, nSetProt, "(Ljava/io/FileDescriptor;I)I") }; } // anonymous namespace namespace android { -int register_android_os_SharedMemory(JNIEnv* env) -{ +int register_android_os_SharedMemory(JNIEnv* env) { + errnoExceptionClass = + MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/system/ErrnoException")); + errnoExceptionCtor = + GetMethodIDOrDie(env, errnoExceptionClass, "<init>", "(Ljava/lang/String;I)V"); return RegisterMethodsOrDie(env, "android/os/SharedMemory", methods, NELEM(methods)); } diff --git a/core/jni/android_util_jar_StrictJarFile.cpp b/core/jni/android_util_jar_StrictJarFile.cpp index 4ab8db4395f6..182a621c6978 100644 --- a/core/jni/android_util_jar_StrictJarFile.cpp +++ b/core/jni/android_util_jar_StrictJarFile.cpp @@ -22,24 +22,26 @@ #include <log/log.h> +#include <nativehelper/jni_macros.h> #include <nativehelper/JNIHelp.h> -#include <nativehelper/JniConstants.h> #include <nativehelper/ScopedLocalRef.h> #include <nativehelper/ScopedUtfChars.h> -#include "jni.h" + +#include "core_jni_helpers.h" #include "ziparchive/zip_archive.h" -namespace android { +namespace { +jclass zipEntryClass; // The method ID for ZipEntry.<init>(String,String,JJJIII[BJJ) -static jmethodID zipEntryCtor; +jmethodID zipEntryCtor; -static void throwIoException(JNIEnv* env, const int32_t errorCode) { +void throwIoException(JNIEnv* env, const int32_t errorCode) { jniThrowException(env, "java/io/IOException", ErrorCodeString(errorCode)); } -static jobject newZipEntry(JNIEnv* env, const ZipEntry& entry, jstring entryName) { - return env->NewObject(JniConstants::zipEntryClass, +jobject newZipEntry(JNIEnv* env, const ZipEntry& entry, jstring entryName) { + return env->NewObject(zipEntryClass, zipEntryCtor, entryName, NULL, // comment @@ -52,7 +54,7 @@ static jobject newZipEntry(JNIEnv* env, const ZipEntry& entry, jstring entryName static_cast<jlong>(entry.offset)); } -static jlong StrictJarFile_nativeOpenJarFile(JNIEnv* env, jobject, jstring name, jint fd) { +jlong StrictJarFile_nativeOpenJarFile(JNIEnv* env, jobject, jstring name, jint fd) { // Name argument is used for logging, and can be any string. ScopedUtfChars nameChars(env, name); if (nameChars.c_str() == NULL) { @@ -90,7 +92,7 @@ class IterationHandle { }; -static jlong StrictJarFile_nativeStartIteration(JNIEnv* env, jobject, jlong nativeHandle, +jlong StrictJarFile_nativeStartIteration(JNIEnv* env, jobject, jlong nativeHandle, jstring prefix) { ScopedUtfChars prefixChars(env, prefix); if (prefixChars.c_str() == NULL) { @@ -116,7 +118,7 @@ static jlong StrictJarFile_nativeStartIteration(JNIEnv* env, jobject, jlong nati return reinterpret_cast<jlong>(handle); } -static jobject StrictJarFile_nativeNextEntry(JNIEnv* env, jobject, jlong iterationHandle) { +jobject StrictJarFile_nativeNextEntry(JNIEnv* env, jobject, jlong iterationHandle) { ZipEntry data; ZipString entryName; @@ -135,7 +137,7 @@ static jobject StrictJarFile_nativeNextEntry(JNIEnv* env, jobject, jlong iterati return newZipEntry(env, data, entryNameString.get()); } -static jobject StrictJarFile_nativeFindEntry(JNIEnv* env, jobject, jlong nativeHandle, +jobject StrictJarFile_nativeFindEntry(JNIEnv* env, jobject, jlong nativeHandle, jstring entryName) { ScopedUtfChars entryNameChars(env, entryName); if (entryNameChars.c_str() == NULL) { @@ -152,11 +154,11 @@ static jobject StrictJarFile_nativeFindEntry(JNIEnv* env, jobject, jlong nativeH return newZipEntry(env, data, entryName); } -static void StrictJarFile_nativeClose(JNIEnv*, jobject, jlong nativeHandle) { +void StrictJarFile_nativeClose(JNIEnv*, jobject, jlong nativeHandle) { CloseArchive(reinterpret_cast<ZipArchiveHandle>(nativeHandle)); } -static JNINativeMethod gMethods[] = { +JNINativeMethod gMethods[] = { NATIVE_METHOD(StrictJarFile, nativeOpenJarFile, "(Ljava/lang/String;I)J"), NATIVE_METHOD(StrictJarFile, nativeStartIteration, "(JLjava/lang/String;)J"), NATIVE_METHOD(StrictJarFile, nativeNextEntry, "(J)Ljava/util/zip/ZipEntry;"), @@ -164,14 +166,15 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(StrictJarFile, nativeClose, "(J)V"), }; -int register_android_util_jar_StrictJarFile(JNIEnv* env) { - jniRegisterNativeMethods(env, "android/util/jar/StrictJarFile", gMethods, NELEM(gMethods)); +} // namespace - zipEntryCtor = env->GetMethodID(JniConstants::zipEntryClass, "<init>", - "(Ljava/lang/String;Ljava/lang/String;JJJII[BJ)V"); - LOG_ALWAYS_FATAL_IF(zipEntryCtor == NULL, "Unable to find ZipEntry.<init>"); +namespace android { - return 0; +int register_android_util_jar_StrictJarFile(JNIEnv* env) { + zipEntryClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/util/zip/ZipEntry")); + zipEntryCtor = GetMethodIDOrDie(env, zipEntryClass, "<init>", + "(Ljava/lang/String;Ljava/lang/String;JJJII[BJ)V"); + return jniRegisterNativeMethods(env, "android/util/jar/StrictJarFile", gMethods, NELEM(gMethods)); } }; // namespace android diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp index 33b26899fe81..bd87dcc325a8 100644 --- a/core/jni/fd_utils.cpp +++ b/core/jni/fd_utils.cpp @@ -152,7 +152,7 @@ class FileDescriptorInfo { const bool is_sock; private: - FileDescriptorInfo(int fd); + explicit FileDescriptorInfo(int fd); FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags, int fd_flags, int fs_flags, off_t offset); diff --git a/core/jni/fd_utils.h b/core/jni/fd_utils.h index a3570d7ed1fb..09022a2e2408 100644 --- a/core/jni/fd_utils.h +++ b/core/jni/fd_utils.h @@ -86,7 +86,7 @@ class FileDescriptorTable { bool ReopenOrDetach(std::string* error_msg); private: - FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo*>& map); + explicit FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo*>& map); bool RestatInternal(std::set<int>& open_fds, std::string* error_msg); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 8b6f33fd97e7..c6343a8738d8 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -611,6 +611,7 @@ <protected-broadcast android:name="android.intent.action.DOCK_ACTIVE" /> <!-- Added in Q --> + <protected-broadcast android:name="android.content.pm.action.SESSION_UPDATED" /> <!-- For CarIdlenessTracker --> <protected-broadcast android:name="com.android.server.jobscheduler.GARAGE_MODE_ON" /> @@ -4336,7 +4337,7 @@ <permission android:name="android.permission.SMS_FINANCIAL_TRANSACTIONS" android:protectionLevel="signature|appop" /> - <!-- Required for apps targeting {@link android.os.Build.VERSION_CODES#P} that want to use + <!-- Required for apps targeting {@link android.os.Build.VERSION_CODES#Q} that want to use {@link android.app.Notification.Builder#setFullScreenIntent notification full screen intents}. --> <permission android:name="android.permission.USE_FULL_SCREEN_INTENT" diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml index 4b97fe754fea..7c95d1efb60d 100644 --- a/core/res/res/values/styles_device_defaults.xml +++ b/core/res/res/values/styles_device_defaults.xml @@ -42,7 +42,7 @@ easier. <item name="textColor">@color/btn_colored_text_material</item> </style> <style name="Widget.DeviceDefault.TextView" parent="Widget.Material.TextView"> - <item name="textAppearance">@string/config_bodyFontFamily</item> + <item name="fontFamily">@string/config_bodyFontFamily</item> </style> <style name="Widget.DeviceDefault.CheckedTextView" parent="Widget.Material.CheckedTextView"/> <style name="Widget.DeviceDefault.AutoCompleteTextView" parent="Widget.Material.AutoCompleteTextView"/> diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java new file mode 100644 index 000000000000..d520f151c2fa --- /dev/null +++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.view; + +import static android.view.InsetsState.TYPE_NAVIGATION_BAR; +import static android.view.InsetsState.TYPE_TOP_BAR; +import static junit.framework.Assert.assertEquals; +import static org.mockito.Mockito.verify; + +import android.graphics.Insets; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.RectF; +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.FlakyTest; +import android.support.test.runner.AndroidJUnit4; +import android.util.SparseArray; +import android.view.SurfaceControl.Transaction; +import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.List; + +@Presubmit +@FlakyTest(detail = "Promote once confirmed non-flaky") +@RunWith(AndroidJUnit4.class) +public class InsetsAnimationControlImplTest { + + private InsetsAnimationControlImpl mController; + + private SurfaceSession mSession = new SurfaceSession(); + private SurfaceControl mTopLeash; + private SurfaceControl mNavLeash; + + @Mock Transaction mMockTransaction; + @Mock InsetsController mMockController; + @Mock WindowInsetsAnimationControlListener mMockListener; + @Mock SyncRtSurfaceTransactionApplier mMockTransactionApplier; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mTopLeash = new SurfaceControl.Builder(mSession) + .setName("testSurface") + .build(); + mNavLeash = new SurfaceControl.Builder(mSession) + .setName("testSurface") + .build(); + InsetsState state = new InsetsState(); + state.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 500, 100)); + state.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(400, 0, 500, 500)); + InsetsSourceConsumer topConsumer = new InsetsSourceConsumer(TYPE_TOP_BAR, state, + () -> mMockTransaction, mMockController); + topConsumer.setControl(new InsetsSourceControl(TYPE_TOP_BAR, mTopLeash)); + + InsetsSourceConsumer navConsumer = new InsetsSourceConsumer(TYPE_NAVIGATION_BAR, state, + () -> mMockTransaction, mMockController); + navConsumer.hide(); + navConsumer.setControl(new InsetsSourceControl(TYPE_NAVIGATION_BAR, mNavLeash)); + + SparseArray<InsetsSourceConsumer> consumers = new SparseArray<>(); + consumers.put(TYPE_TOP_BAR, topConsumer); + consumers.put(TYPE_NAVIGATION_BAR, navConsumer); + mController = new InsetsAnimationControlImpl(consumers, + new Rect(0, 0, 500, 500), state, mMockListener, WindowInsets.Type.systemBars(), + () -> mMockTransactionApplier); + } + + @Test + public void testGetters() { + assertEquals(Insets.of(0, 100, 100, 0), mController.getShownStateInsets()); + assertEquals(Insets.of(0, 0, 0, 0), mController.getHiddenStateInsets()); + assertEquals(Insets.of(0, 100, 0, 0), mController.getCurrentInsets()); + assertEquals(WindowInsets.Type.systemBars(), mController.getTypes()); + } + + @Test + public void testChangeInsets() { + mController.changeInsets(Insets.of(0, 30, 40, 0)); + assertEquals(Insets.of(0, 30, 40, 0), mController.getCurrentInsets()); + + ArgumentCaptor<SurfaceParams> captor = ArgumentCaptor.forClass(SurfaceParams.class); + verify(mMockTransactionApplier).scheduleApply(captor.capture()); + List<SurfaceParams> params = captor.getAllValues(); + assertEquals(2, params.size()); + SurfaceParams first = params.get(0); + SurfaceParams second = params.get(1); + SurfaceParams topParams = first.surface == mTopLeash ? first : second; + SurfaceParams navParams = first.surface == mNavLeash ? first : second; + assertPosition(topParams.matrix, new Rect(0, 0, 500, 100), new Rect(0, -70, 500, 30)); + assertPosition(navParams.matrix, new Rect(400, 0, 500, 500), new Rect(460, 0, 560, 500)); + } + + private void assertPosition(Matrix m, Rect original, Rect transformed) { + RectF rect = new RectF(original); + rect.offsetTo(0, 0); + m.mapRect(rect); + rect.round(original); + assertEquals(original, transformed); + } +} diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index 2ad6028960df..d3d274a64682 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -28,6 +28,7 @@ import android.support.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mockito; @Presubmit @FlakyTest(detail = "Promote once confirmed non-flaky") diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java index 6bb9539e89bd..d41a718147f2 100644 --- a/core/tests/coretests/src/android/view/InsetsStateTest.java +++ b/core/tests/coretests/src/android/view/InsetsStateTest.java @@ -16,6 +16,8 @@ package android.view; +import static android.view.InsetsState.INSET_SIDE_BOTTOM; +import static android.view.InsetsState.INSET_SIDE_TOP; import static android.view.InsetsState.TYPE_IME; import static android.view.InsetsState.TYPE_NAVIGATION_BAR; import static android.view.InsetsState.TYPE_TOP_BAR; @@ -27,6 +29,7 @@ import android.os.Parcel; import android.platform.test.annotations.Presubmit; import android.support.test.filters.FlakyTest; import android.support.test.runner.AndroidJUnit4; +import android.util.SparseIntArray; import org.junit.Test; import org.junit.runner.RunWith; @@ -45,9 +48,12 @@ public class InsetsStateTest { mState.getSource(TYPE_TOP_BAR).setVisible(true); mState.getSource(TYPE_IME).setFrame(new Rect(0, 200, 100, 300)); mState.getSource(TYPE_IME).setVisible(true); + SparseIntArray typeSideMap = new SparseIntArray(); WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false, - DisplayCutout.NO_CUTOUT); + DisplayCutout.NO_CUTOUT, typeSideMap); assertEquals(new Rect(0, 100, 0, 100), insets.getSystemWindowInsets()); + assertEquals(INSET_SIDE_TOP, typeSideMap.get(TYPE_TOP_BAR)); + assertEquals(INSET_SIDE_BOTTOM, typeSideMap.get(TYPE_IME)); } @Test @@ -57,7 +63,7 @@ public class InsetsStateTest { mState.getSource(TYPE_IME).setFrame(new Rect(0, 100, 100, 300)); mState.getSource(TYPE_IME).setVisible(true); WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false, - DisplayCutout.NO_CUTOUT); + DisplayCutout.NO_CUTOUT, null); assertEquals(100, insets.getStableInsetBottom()); assertEquals(new Rect(0, 0, 0, 200), insets.getSystemWindowInsets()); } @@ -69,7 +75,7 @@ public class InsetsStateTest { mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300)); mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true); WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false, - DisplayCutout.NO_CUTOUT); + DisplayCutout.NO_CUTOUT, null); assertEquals(new Rect(0, 100, 20, 0), insets.getSystemWindowInsets()); } @@ -81,7 +87,7 @@ public class InsetsStateTest { mState.getSource(TYPE_IME).setVisible(true); mState.removeSource(TYPE_IME); WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false, - DisplayCutout.NO_CUTOUT); + DisplayCutout.NO_CUTOUT, null); assertEquals(0, insets.getSystemWindowInsetBottom()); } diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index c21642559bb9..58b57e5b7efd 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -33,6 +33,10 @@ applications that come with the platform <permission name="android.permission.CRYPT_KEEPER"/> </privapp-permissions> + <privapp-permissions package="com.android.carrierconfig"> + <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> + </privapp-permissions> + <privapp-permissions package="com.android.cellbroadcastreceiver"> <permission name="android.permission.INTERACT_ACROSS_USERS"/> <permission name="android.permission.MANAGE_USERS"/> diff --git a/graphics/java/android/graphics/Insets.java b/graphics/java/android/graphics/Insets.java index d9da27c8b931..8258b575d63f 100644 --- a/graphics/java/android/graphics/Insets.java +++ b/graphics/java/android/graphics/Insets.java @@ -93,6 +93,41 @@ public final class Insets { } /** + * Subtract two Insets. + * + * @param a The minuend. + * @param b The subtrahend. + * @return a - b, i. e. all insets on every side are subtracted from each other. + */ + public static @NonNull Insets subtract(@NonNull Insets a, @NonNull Insets b) { + return Insets.of(a.left - b.left, a.top - b.top, a.right - b.right, a.bottom - b.bottom); + } + + /** + * Retrieves the maximum of two Insets. + * + * @param a The first Insets. + * @param b The second Insets. + * @return max(a, b), i. e. the larger of every inset on every side is taken for the result. + */ + public static @NonNull Insets max(@NonNull Insets a, @NonNull Insets b) { + return Insets.of(Math.max(a.left, b.left), Math.max(a.top, b.top), + Math.max(a.right, b.right), Math.max(a.bottom, b.bottom)); + } + + /** + * Retrieves the minimum of two Insets. + * + * @param a The first Insets. + * @param b The second Insets. + * @return min(a, b), i. e. the smaller of every inset on every side is taken for the result. + */ + public static @NonNull Insets min(@NonNull Insets a, @NonNull Insets b) { + return Insets.of(Math.min(a.left, b.left), Math.min(a.top, b.top), + Math.min(a.right, b.right), Math.min(a.bottom, b.bottom)); + } + + /** * Two Insets instances are equal iff they belong to the same class and their fields are * pairwise equal. * diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index 6e6ed30fef8c..9361c7c29bcb 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -55,6 +55,7 @@ import java.math.BigInteger; import java.io.ByteArrayInputStream; import java.io.IOException; import java.security.InvalidKeyException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; @@ -315,13 +316,14 @@ public class KeyStore { } /** - * List uids of all keys that are auth bound to the current user. + * List uids of all keys that are auth bound to the current user. * Only system is allowed to call this method. */ @UnsupportedAppUsage public int[] listUidsOfAuthBoundKeys() { - final int MAX_RESULT_SIZE = 100; - int[] uidsOut = new int[MAX_RESULT_SIZE]; + // uids are returned as a list of strings because list of integers + // as an output parameter is not supported by aidl-cpp. + List<String> uidsOut = new ArrayList<>(); try { int rc = mBinder.listUidsOfAuthBoundKeys(uidsOut); if (rc != NO_ERROR) { @@ -335,8 +337,8 @@ public class KeyStore { Log.w(TAG, "KeyStore exception", e); return null; } - // Remove any 0 entries - return Arrays.stream(uidsOut).filter(x -> x > 0).toArray(); + // Turn list of strings into an array of uid integers. + return uidsOut.stream().mapToInt(Integer::parseInt).toArray(); } public String[] list(String prefix) { diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index f2906de4d5a0..ff873133c6fb 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -30,7 +30,7 @@ namespace skiapipeline { class SkiaPipeline : public renderthread::IRenderPipeline { public: - SkiaPipeline(renderthread::RenderThread& thread); + explicit SkiaPipeline(renderthread::RenderThread& thread); virtual ~SkiaPipeline(); TaskManager* getTaskManager() override; diff --git a/libs/hwui/pipeline/skia/SkiaProfileRenderer.h b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h index daa4c1839693..dc8420f4e01b 100644 --- a/libs/hwui/pipeline/skia/SkiaProfileRenderer.h +++ b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h @@ -23,7 +23,7 @@ namespace uirenderer { class SkiaProfileRenderer : public IProfileRenderer { public: - SkiaProfileRenderer(SkCanvas* canvas) : mCanvas(canvas) {} + explicit SkiaProfileRenderer(SkCanvas* canvas) : mCanvas(canvas) {} void drawRect(float left, float top, float right, float bottom, const SkPaint& paint) override; void drawRects(const float* rects, int count, const SkPaint& paint) override; diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index 02874c7d2c69..53ffc4422fd7 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -25,7 +25,7 @@ namespace skiapipeline { class SkiaVulkanPipeline : public SkiaPipeline { public: - SkiaVulkanPipeline(renderthread::RenderThread& thread); + explicit SkiaVulkanPipeline(renderthread::RenderThread& thread); virtual ~SkiaVulkanPipeline() {} renderthread::MakeCurrentResult makeCurrent() override; diff --git a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h b/libs/hwui/pipeline/skia/VectorDrawableAtlas.h index 74e48cea2a87..5e892aa7e92c 100644 --- a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h +++ b/libs/hwui/pipeline/skia/VectorDrawableAtlas.h @@ -62,8 +62,8 @@ class VectorDrawableAtlas : public virtual RefBase { public: enum class StorageMode { allowSharedSurface, disallowSharedSurface }; - VectorDrawableAtlas(size_t surfaceArea, - StorageMode storageMode = StorageMode::allowSharedSurface); + explicit VectorDrawableAtlas(size_t surfaceArea, + StorageMode storageMode = StorageMode::allowSharedSurface); /** * "prepareForDraw" may allocate a new surface if needed. It may schedule to repack the diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h index 35fc91a42510..66f04f1ba266 100644 --- a/libs/hwui/renderthread/CacheManager.h +++ b/libs/hwui/renderthread/CacheManager.h @@ -59,7 +59,7 @@ public: private: friend class RenderThread; - CacheManager(const DisplayInfo& display); + explicit CacheManager(const DisplayInfo& display); void reset(sk_sp<GrContext> grContext); void destroy(); diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index 69ca23acb1fd..9eb942c9d6ee 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -160,6 +160,7 @@ private: fPtr = ptr; return *this; } + // NOLINTNEXTLINE(google-explicit-constructor) operator FNPTR_TYPE() const { return fPtr; } private: diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include/android/os/IncidentReportArgs.h index c56f689b7419..ee1e33c43b89 100644 --- a/libs/incident/include/android/os/IncidentReportArgs.h +++ b/libs/incident/include/android/os/IncidentReportArgs.h @@ -40,7 +40,7 @@ const uint8_t DEST_AUTOMATIC = 200; class IncidentReportArgs : public Parcelable { public: IncidentReportArgs(); - explicit IncidentReportArgs(const IncidentReportArgs& that); + IncidentReportArgs(const IncidentReportArgs& that); virtual ~IncidentReportArgs(); virtual status_t writeToParcel(Parcel* out) const; diff --git a/location/java/android/location/GnssMeasurementCallbackTransport.java b/location/java/android/location/GnssMeasurementCallbackTransport.java index 21f63068084e..1188b13b7841 100644 --- a/location/java/android/location/GnssMeasurementCallbackTransport.java +++ b/location/java/android/location/GnssMeasurementCallbackTransport.java @@ -19,6 +19,8 @@ package android.location; import android.content.Context; import android.os.RemoteException; +import com.android.internal.util.Preconditions; + /** * A handler class to manage transport callbacks for {@link GnssMeasurementsEvent.Callback}. * @@ -26,12 +28,13 @@ import android.os.RemoteException; */ class GnssMeasurementCallbackTransport extends LocalListenerHelper<GnssMeasurementsEvent.Callback> { + private static final String TAG = "GnssMeasCbTransport"; private final ILocationManager mLocationManager; private final IGnssMeasurementsListener mListenerTransport = new ListenerTransport(); public GnssMeasurementCallbackTransport(Context context, ILocationManager locationManager) { - super(context, "GnssMeasurementListenerTransport"); + super(context, TAG); mLocationManager = locationManager; } @@ -47,17 +50,34 @@ class GnssMeasurementCallbackTransport mLocationManager.removeGnssMeasurementsListener(mListenerTransport); } + /** + * Injects GNSS measurement corrections into the GNSS chipset. + * + * @param measurementCorrections a {@link GnssMeasurementCorrections} object with the GNSS + * measurement corrections to be injected into the GNSS chipset. + */ + protected void injectGnssMeasurementCorrections( + GnssMeasurementCorrections measurementCorrections) throws RemoteException { + Preconditions.checkNotNull(measurementCorrections); + mLocationManager.injectGnssMeasurementCorrections( + measurementCorrections, getContext().getPackageName()); + } + + protected int getGnssCapabilities() throws RemoteException { + return mLocationManager.getGnssCapabilities(getContext().getPackageName()); + } + private class ListenerTransport extends IGnssMeasurementsListener.Stub { @Override public void onGnssMeasurementsReceived(final GnssMeasurementsEvent event) { ListenerOperation<GnssMeasurementsEvent.Callback> operation = new ListenerOperation<GnssMeasurementsEvent.Callback>() { - @Override - public void execute(GnssMeasurementsEvent.Callback callback) - throws RemoteException { - callback.onGnssMeasurementsReceived(event); - } - }; + @Override + public void execute(GnssMeasurementsEvent.Callback callback) + throws RemoteException { + callback.onGnssMeasurementsReceived(event); + } + }; foreach(operation); } diff --git a/location/java/android/location/GnssMeasurementCorrections.aidl b/location/java/android/location/GnssMeasurementCorrections.aidl new file mode 100644 index 000000000000..b05eb5455c82 --- /dev/null +++ b/location/java/android/location/GnssMeasurementCorrections.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 208 The Android Open 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.location; + +parcelable GnssMeasurementCorrections; diff --git a/location/java/android/location/GnssMeasurementCorrections.java b/location/java/android/location/GnssMeasurementCorrections.java new file mode 100644 index 000000000000..b81bf908053c --- /dev/null +++ b/location/java/android/location/GnssMeasurementCorrections.java @@ -0,0 +1,210 @@ +/* + * 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.location; + +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A class representing a GNSS measurement corrections for all used GNSS satellites at the location + * and time specified + * + * @hide + */ +@SystemApi +public final class GnssMeasurementCorrections implements Parcelable { + + /** Represents latitude in degrees at which the corrections are computed. */ + private double mLatitudeDegrees; + /** Represents longitude in degrees at which the corrections are computed. */ + private double mLongitudeDegrees; + /** + * Represents altitude in meters above the WGS 84 reference ellipsoid at which the corrections + * are computed. + */ + private double mAltitudeMeters; + + /** Time Of Applicability, GPS time of week */ + private long mToaGpsNanosecondsOfWeek; + + /** + * A set of {@link GnssSingleSatCorrection} each containing measurement corrections for a + * satellite in view + */ + private @Nullable List<GnssSingleSatCorrection> mSingleSatCorrectionList; + + private GnssMeasurementCorrections(Builder builder) { + mLatitudeDegrees = builder.mLatitudeDegrees; + mLongitudeDegrees = builder.mLongitudeDegrees; + mAltitudeMeters = builder.mAltitudeMeters; + mToaGpsNanosecondsOfWeek = builder.mToaGpsNanosecondsOfWeek; + mSingleSatCorrectionList = + builder.mSingleSatCorrectionList == null + ? null + : Collections.unmodifiableList( + new ArrayList<>(builder.mSingleSatCorrectionList)); + } + + /** Gets the latitude in degrees at which the corrections are computed. */ + public double getLatitudeDegrees() { + return mLatitudeDegrees; + } + + /** Gets the longitude in degrees at which the corrections are computed. */ + public double getLongitudeDegrees() { + return mLongitudeDegrees; + } + + /** + * Gets the altitude in meters above the WGS 84 reference ellipsoid at which the corrections are + * computed. + */ + public double getAltitudeMeters() { + return mAltitudeMeters; + } + + /** Gets the time of applicability, GPS time of week in nanoseconds. */ + public long getToaGpsNanosecondsOfWeek() { + return mToaGpsNanosecondsOfWeek; + } + + /** + * Gets a set of {@link GnssSingleSatCorrection} each containing measurement corrections for a + * satellite in view + */ + public @Nullable List<GnssSingleSatCorrection> getSingleSatCorrectionList() { + return mSingleSatCorrectionList; + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator<GnssMeasurementCorrections> CREATOR = + new Creator<GnssMeasurementCorrections>() { + @Override + public GnssMeasurementCorrections createFromParcel(Parcel parcel) { + GnssMeasurementCorrections.Builder gnssMeasurementCorrectons = + new Builder() + .setLatitudeDegrees(parcel.readDouble()) + .setLongitudeDegrees(parcel.readDouble()) + .setAltitudeMeters(parcel.readDouble()) + .setToaGpsNanosecondsOfWeek(parcel.readLong()); + List<GnssSingleSatCorrection> singleSatCorrectionList = new ArrayList<>(); + parcel.readTypedList(singleSatCorrectionList, GnssSingleSatCorrection.CREATOR); + gnssMeasurementCorrectons.setSingleSatCorrectionList( + singleSatCorrectionList.isEmpty() ? null : singleSatCorrectionList); + return gnssMeasurementCorrectons.build(); + } + + @Override + public GnssMeasurementCorrections[] newArray(int i) { + return new GnssMeasurementCorrections[i]; + } + }; + + @Override + public String toString() { + final String format = " %-29s = %s\n"; + StringBuilder builder = new StringBuilder("GnssMeasurementCorrections:\n"); + builder.append(String.format(format, "LatitudeDegrees = ", mLatitudeDegrees)); + builder.append(String.format(format, "LongitudeDegrees = ", mLongitudeDegrees)); + builder.append(String.format(format, "AltitudeMeters = ", mAltitudeMeters)); + builder.append( + String.format(format, "ToaGpsNanosecondsOfWeek = ", mToaGpsNanosecondsOfWeek)); + builder.append( + String.format(format, "mSingleSatCorrectionList = ", mSingleSatCorrectionList)); + return builder.toString(); + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeDouble(mLatitudeDegrees); + parcel.writeDouble(mLongitudeDegrees); + parcel.writeDouble(mAltitudeMeters); + parcel.writeLong(mToaGpsNanosecondsOfWeek); + parcel.writeTypedList(mSingleSatCorrectionList); + } + + /** Builder for {@link GnssMeasurementCorrections} */ + public static class Builder { + /** + * For documentation of below fields, see corresponding fields in {@link + * GnssMeasurementCorrections}. + */ + private double mLatitudeDegrees; + + private double mLongitudeDegrees; + private double mAltitudeMeters; + private long mToaGpsNanosecondsOfWeek; + private List<GnssSingleSatCorrection> mSingleSatCorrectionList; + + /** Sets the latitude in degrees at which the corrections are computed. */ + public Builder setLatitudeDegrees(double latitudeDegrees) { + mLatitudeDegrees = latitudeDegrees; + return this; + } + + /** Sets the longitude in degrees at which the corrections are computed. */ + public Builder setLongitudeDegrees(double longitudeDegrees) { + mLongitudeDegrees = longitudeDegrees; + return this; + } + + /** + * Sets the altitude in meters above the WGS 84 reference ellipsoid at which the corrections + * are computed. + */ + public Builder setAltitudeMeters(double altitudeMeters) { + mAltitudeMeters = altitudeMeters; + return this; + } + + /** Sets the time of applicability, GPS time of week in nanoseconds. */ + public Builder setToaGpsNanosecondsOfWeek(long toaGpsNanosecondsOfWeek) { + mToaGpsNanosecondsOfWeek = toaGpsNanosecondsOfWeek; + return this; + } + + /** + * Sets a the list of {@link GnssSingleSatCorrection} containing measurement corrections for + * a satellite in view + */ + public Builder setSingleSatCorrectionList( + @Nullable List<GnssSingleSatCorrection> singleSatCorrectionList) { + if (singleSatCorrectionList == null) { + mSingleSatCorrectionList = null; + } else { + mSingleSatCorrectionList = + Collections.unmodifiableList(new ArrayList<>(singleSatCorrectionList)); + } + return this; + } + + /** Builds a {@link GnssMeasurementCorrections} instance as specified by this builder. */ + public GnssMeasurementCorrections build() { + return new GnssMeasurementCorrections(this); + } + } +} diff --git a/location/java/android/location/GnssReflectingPlane.java b/location/java/android/location/GnssReflectingPlane.java new file mode 100644 index 000000000000..64b37524e37f --- /dev/null +++ b/location/java/android/location/GnssReflectingPlane.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location; + +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Holds the characteristics of the reflecting plane that a satellite signal has bounced from. + * + * @hide + */ +@SystemApi +public final class GnssReflectingPlane implements Parcelable { + + /** Represents latitude in degrees of the reflecting plane */ + private double mLatitudeDegrees; + /** Represents longitude in degrees of the reflecting plane. */ + private double mLongitudeDegrees; + /** + * Represents altitude in meters above the WGS 84 reference ellipsoid of the reflection point in + * the plane + */ + private double mAltitudeMeters; + + /** Represents azimuth clockwise from north of the reflecting plane in degrees. */ + private double mAzimuthDegrees; + + private GnssReflectingPlane(Builder builder) { + mLatitudeDegrees = builder.mLatitudeDegrees; + mLongitudeDegrees = builder.mLongitudeDegrees; + mAltitudeMeters = builder.mAltitudeMeters; + mAzimuthDegrees = builder.mAzimuthDegrees; + } + + /** Gets the latitude in degrees of the reflecting plane. */ + public double getLatitudeDegrees() { + return mLatitudeDegrees; + } + + /** Gets the longitude in degrees of the reflecting plane. */ + public double getLongitudeDegrees() { + return mLongitudeDegrees; + } + + /** + * Gets the altitude in meters above the WGS 84 reference ellipsoid of the reflecting point + * within the plane + */ + public double getAltitudeMeters() { + return mAltitudeMeters; + } + + /** Gets the azimuth clockwise from north of the reflecting plane in degrees. */ + public double getAzimuthDegrees() { + return mAzimuthDegrees; + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator<GnssReflectingPlane> CREATOR = + new Creator<GnssReflectingPlane>() { + @Override + public GnssReflectingPlane createFromParcel(Parcel parcel) { + GnssReflectingPlane reflectingPlane = + new Builder() + .setLatitudeDegrees(parcel.readDouble()) + .setLongitudeDegrees(parcel.readDouble()) + .setAltitudeMeters(parcel.readDouble()) + .setAzimuthDegrees(parcel.readDouble()) + .build(); + return reflectingPlane; + } + + @Override + public GnssReflectingPlane[] newArray(int i) { + return new GnssReflectingPlane[i]; + } + }; + + @Override + public String toString() { + final String format = " %-29s = %s\n"; + StringBuilder builder = new StringBuilder("ReflectingPlane:\n"); + builder.append(String.format(format, "LatitudeDegrees = ", mLatitudeDegrees)); + builder.append(String.format(format, "LongitudeDegrees = ", mLongitudeDegrees)); + builder.append(String.format(format, "AltitudeMeters = ", mAltitudeMeters)); + builder.append(String.format(format, "AzimuthDegrees = ", mAzimuthDegrees)); + return builder.toString(); + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeDouble(mLatitudeDegrees); + parcel.writeDouble(mLongitudeDegrees); + parcel.writeDouble(mAltitudeMeters); + parcel.writeDouble(mAzimuthDegrees); + } + + /** Builder for {@link GnssReflectingPlane} */ + public static class Builder { + /** For documentation, see corresponding fields in {@link GnssReflectingPlane}. */ + private double mLatitudeDegrees; + + private double mLongitudeDegrees; + private double mAltitudeMeters; + private double mAzimuthDegrees; + + /** Sets the latitude in degrees of the reflecting plane. */ + public Builder setLatitudeDegrees(double latitudeDegrees) { + mLatitudeDegrees = latitudeDegrees; + return this; + } + + /** Sets the longitude in degrees of the reflecting plane. */ + public Builder setLongitudeDegrees(double longitudeDegrees) { + mLongitudeDegrees = longitudeDegrees; + return this; + } + + /** + * Sets the altitude in meters above the WGS 84 reference ellipsoid of the reflecting point + * within the plane + */ + public Builder setAltitudeMeters(double altitudeMeters) { + mAltitudeMeters = altitudeMeters; + return this; + } + + /** Sets the azimuth clockwise from north of the reflecting plane in degrees. */ + public Builder setAzimuthDegrees(double azimuthDegrees) { + mAzimuthDegrees = azimuthDegrees; + return this; + } + + /** Builds a {@link GnssReflectingPlane} object as specified by this builder. */ + public GnssReflectingPlane build() { + return new GnssReflectingPlane(this); + } + } +} diff --git a/location/java/android/location/GnssSingleSatCorrection.java b/location/java/android/location/GnssSingleSatCorrection.java new file mode 100644 index 000000000000..6c757f949f19 --- /dev/null +++ b/location/java/android/location/GnssSingleSatCorrection.java @@ -0,0 +1,346 @@ +/* + * 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.location; + +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A container with measurement corrections for a single visible satellite + * + * @hide + */ +@SystemApi +public final class GnssSingleSatCorrection implements Parcelable { + + /** + * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link + * #mSatIsLos}. + */ + public static final int HAS_SAT_IS_LOS_MASK = 1 << 0; + + /** + * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link + * #mExcessPathLengthMeters}. + */ + public static final int HAS_EXCESS_PATH_LENGTH_MASK = 1 << 1; + + /** + * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link + * #mExcessPathLengthUncertaintyMeters}. + */ + public static final int HAS_EXCESS_PATH_LENGTH_UNC_MASK = 1 << 2; + + /** + * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link + * #mReflectingPlane}. + */ + public static final int HAS_REFLECTING_PLANE_MASK = 1 << 3; + + /** A bitmask of fields present in this object (see HAS_* constants defined above) */ + private int mSingleSatCorrectionFlags; + + /** Defines the constellation of the given satellite as defined in {@link GnssStatus}. */ + private int mConstellationType; + + /** + * Satellite vehicle ID number + * + * <p>Interpretation depends on {@link GnssStatus#getSvid(int)}. + */ + private int mSatId; + + /** + * Carrier frequency of the signal to be corrected, for example it can be the GPS center + * frequency for L1 = 1,575,420,000 Hz, varying GLO channels, etc. + * + * <p>For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two correction + * objects will be reported for this same satellite, in one of the correction objects, all the + * values related to L1 will be filled, and in the other all of the values related to L5 will be + * filled. + */ + private float mCarrierFrequencyHz; + + /** + * True if the satellite is estimated to be in Line-of-Sight condition at the given location. + */ + private boolean mSatIsLos; + + /** + * Excess path length to be subtracted from pseudorange before using it in calculating location. + */ + private float mExcessPathLengthMeters; + + /** Error estimate (1-sigma) for the Excess path length estimate */ + private float mExcessPathLengthUncertaintyMeters; + + /** + * Defines the reflecting plane location and azimuth information + * + * <p>The flag HAS_REFLECTING_PLANE will be used to set this value to invalid if the satellite + * signal goes through multiple reflections or if reflection plane serving is not supported. + */ + private @Nullable GnssReflectingPlane mReflectingPlane; + + private GnssSingleSatCorrection(Builder builder) { + mSingleSatCorrectionFlags = builder.mSingleSatCorrectionFlags; + mSatId = builder.mSatId; + mConstellationType = builder.mConstellationType; + mCarrierFrequencyHz = builder.mCarrierFrequencyHz; + mSatIsLos = builder.mSatIsLos; + mExcessPathLengthMeters = builder.mExcessPathLengthMeters; + mExcessPathLengthUncertaintyMeters = builder.mExcessPathLengthUncertaintyMeters; + mReflectingPlane = builder.mReflectingPlane; + } + + /** Gets a bitmask of fields present in this object */ + public int getSingleSatCorrectionFlags() { + return mSingleSatCorrectionFlags; + } + + /** + * Gets the constellation type. + * + * <p>The return value is one of those constants with {@code CONSTELLATION_} prefix in {@link + * GnssStatus}. + */ + @GnssStatus.ConstellationType + public int getConstellationType() { + return mConstellationType; + } + + /** + * Gets the satellite ID. + * + * <p>Interpretation depends on {@link #getConstellationType()}. See {@link + * GnssStatus#getSvid(int)}. + */ + public int getSatId() { + return mSatId; + } + + /** + * Gets the carrier frequency of the tracked signal. + * + * <p>For example it can be the GPS central frequency for L1 = 1575.45 MHz, or L2 = 1227.60 MHz, + * L5 = 1176.45 MHz, varying GLO channels, etc. + * + * <p>For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two correction + * objects will be reported for this same satellite, in one of the correction objects, all the + * values related to L1 will be filled, and in the other all of the values related to L5 will be + * filled. + * + * @return the carrier frequency of the signal tracked in Hz. + */ + public float getCarrierFrequencyHz() { + return mCarrierFrequencyHz; + } + + /** True if the satellite is line-of-sight */ + public boolean isSatelliteLineOfSight() { + return mSatIsLos; + } + + /** + * Returns the Excess path length to be subtracted from pseudorange before using it in + * calculating location. + */ + public float getExcessPathLengthMeters() { + return mExcessPathLengthMeters; + } + + /** Returns the error estimate (1-sigma) for the Excess path length estimate */ + public float getExcessPathLengthUncertaintyMeters() { + return mExcessPathLengthUncertaintyMeters; + } + + /** + * Returns the reflecting plane characteristics at which the signal has bounced + * + * <p>The flag HAS_REFLECTING_PLANE will be used to set this value to invalid if the satellite + * signal goes through multiple reflections or if reflection plane serving is not supported + */ + public @Nullable GnssReflectingPlane getReflectingPlane() { + return mReflectingPlane; + } + + /** Returns {@code true} if {@link #isSatelliteLineOfSight()} is valid. */ + public boolean hasSatelliteLineOfSight() { + return (mSingleSatCorrectionFlags & HAS_SAT_IS_LOS_MASK) != 0; + } + + /** Returns {@code true} if {@link #getExcessPathLengthMeters()} is valid. */ + public boolean hasExcessPathLength() { + return (mSingleSatCorrectionFlags & HAS_EXCESS_PATH_LENGTH_MASK) != 0; + } + + /** Returns {@code true} if {@link #getExcessPathLengthUncertaintyMeters()} is valid. */ + public boolean hasExcessPathLengthUncertainty() { + return (mSingleSatCorrectionFlags & HAS_EXCESS_PATH_LENGTH_UNC_MASK) != 0; + } + + /** Returns {@code true} if {@link #getReflectingPlane()} is valid. */ + public boolean hasReflectingPlane() { + return (mSingleSatCorrectionFlags & HAS_REFLECTING_PLANE_MASK) != 0; + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator<GnssSingleSatCorrection> CREATOR = + new Creator<GnssSingleSatCorrection>() { + @Override + public GnssSingleSatCorrection createFromParcel(Parcel parcel) { + GnssSingleSatCorrection singleSatCorrection = + new Builder() + .setSingleSatCorrectionFlags(parcel.readInt()) + .setConstellationType(parcel.readInt()) + .setSatId(parcel.readInt()) + .setCarrierFrequencyHz(parcel.readFloat()) + .setSatIsLos(parcel.readBoolean()) + .setExcessPathLengthMeters(parcel.readFloat()) + .setExcessPathLengthUncertaintyMeters(parcel.readFloat()) + .setReflectingPlane( + GnssReflectingPlane.CREATOR.createFromParcel(parcel)) + .build(); + return singleSatCorrection; + } + + @Override + public GnssSingleSatCorrection[] newArray(int i) { + return new GnssSingleSatCorrection[i]; + } + }; + + @Override + public String toString() { + final String format = " %-29s = %s\n"; + StringBuilder builder = new StringBuilder("GnssSingleSatCorrection:\n"); + builder.append( + String.format(format, "SingleSatCorrectionFlags = ", mSingleSatCorrectionFlags)); + builder.append(String.format(format, "ConstellationType = ", mConstellationType)); + builder.append(String.format(format, "SatId = ", mSatId)); + builder.append(String.format(format, "CarrierFrequencyHz = ", mCarrierFrequencyHz)); + builder.append(String.format(format, "SatIsLos = ", mSatIsLos)); + builder.append(String.format(format, "ExcessPathLengthMeters = ", mExcessPathLengthMeters)); + builder.append( + String.format( + format, + "ExcessPathLengthUncertaintyMeters = ", + mExcessPathLengthUncertaintyMeters)); + builder.append(String.format(format, "ReflectingPlane = ", mReflectingPlane)); + return builder.toString(); + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(mSingleSatCorrectionFlags); + parcel.writeInt(mConstellationType); + parcel.writeInt(mSatId); + parcel.writeFloat(mCarrierFrequencyHz); + parcel.writeBoolean(mSatIsLos); + parcel.writeFloat(mExcessPathLengthMeters); + parcel.writeFloat(mExcessPathLengthUncertaintyMeters); + mReflectingPlane.writeToParcel(parcel, flags); + } + + /** Builder for {@link GnssSingleSatCorrection} */ + public static class Builder { + + /** + * For documentation of below fields, see corresponding fields in {@link + * GnssSingleSatCorrection}. + */ + private int mSingleSatCorrectionFlags; + + private int mConstellationType; + private int mSatId; + private float mCarrierFrequencyHz; + private boolean mSatIsLos; + private float mExcessPathLengthMeters; + private float mExcessPathLengthUncertaintyMeters; + private GnssReflectingPlane mReflectingPlane; + + /** Sets a bitmask of fields present in this object */ + public Builder setSingleSatCorrectionFlags(int singleSatCorrectionFlags) { + mSingleSatCorrectionFlags = singleSatCorrectionFlags; + return this; + } + + /** Sets the constellation type. */ + public Builder setConstellationType(int constellationType) { + mConstellationType = constellationType; + return this; + } + + /** Sets the Satellite ID. */ + public Builder setSatId(int satId) { + mSatId = satId; + return this; + } + + /** Sets the Carrier frequency in Hz. */ + public Builder setCarrierFrequencyHz(float carrierFrequencyHz) { + mCarrierFrequencyHz = carrierFrequencyHz; + return this; + } + + /** Sets the line=of-sight state of the satellite */ + public Builder setSatIsLos(boolean satIsLos) { + mSatIsLos = satIsLos; + mSingleSatCorrectionFlags = (byte) (mSingleSatCorrectionFlags | HAS_SAT_IS_LOS_MASK); + return this; + } + + /** + * Sets the Excess path length to be subtracted from pseudorange before using it in + * calculating location. + */ + public Builder setExcessPathLengthMeters(float excessPathLengthMeters) { + mExcessPathLengthMeters = excessPathLengthMeters; + mSingleSatCorrectionFlags = + (byte) (mSingleSatCorrectionFlags | HAS_EXCESS_PATH_LENGTH_MASK); + return this; + } + + /** Sets the error estimate (1-sigma) for the Excess path length estimate */ + public Builder setExcessPathLengthUncertaintyMeters( + float excessPathLengthUncertaintyMeters) { + mExcessPathLengthUncertaintyMeters = excessPathLengthUncertaintyMeters; + mSingleSatCorrectionFlags = + (byte) (mSingleSatCorrectionFlags | HAS_EXCESS_PATH_LENGTH_UNC_MASK); + return this; + } + + /** Sets the reflecting plane information */ + public Builder setReflectingPlane(GnssReflectingPlane reflectingPlane) { + mReflectingPlane = reflectingPlane; + mSingleSatCorrectionFlags = + (byte) (mSingleSatCorrectionFlags | HAS_REFLECTING_PLANE_MASK); + return this; + } + + /** Builds a {@link GnssSingleSatCorrection} instance as specified by this builder. */ + public GnssSingleSatCorrection build() { + return new GnssSingleSatCorrection(this); + } + } +} diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index 05d49e592747..bdc84da57799 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -21,6 +21,7 @@ import android.location.Address; import android.location.Criteria; import android.location.GeocoderParams; import android.location.Geofence; +import android.location.GnssMeasurementCorrections; import android.location.IBatchedLocationCallback; import android.location.IGnssMeasurementsListener; import android.location.IGnssStatusListener; @@ -63,6 +64,9 @@ interface ILocationManager boolean sendNiResponse(int notifId, int userResponse); boolean addGnssMeasurementsListener(in IGnssMeasurementsListener listener, in String packageName); + void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections, + in String packageName); + int getGnssCapabilities(in String packageName); void removeGnssMeasurementsListener(in IGnssMeasurementsListener listener); boolean addGnssNavigationMessageListener( diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 1cd3d86a0c27..040e4f9f1bd0 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -22,6 +22,7 @@ import static android.Manifest.permission.LOCATION_HARDWARE; import static android.Manifest.permission.WRITE_SECURE_SETTINGS; import android.Manifest; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; @@ -2079,17 +2080,54 @@ public class LocationManager { } /** - * No-op method to keep backward-compatibility. - * Don't use it. Use {@link #unregisterGnssMeasurementsCallback} instead. + * Injects GNSS measurement corrections into the GNSS chipset. + * + * @param measurementCorrections a {@link GnssMeasurementCorrections} object with the GNSS + * measurement corrections to be injected into the GNSS chipset. + * @hide + */ + @SystemApi + @RequiresPermission(ACCESS_FINE_LOCATION) + public void injectGnssMeasurementCorrections( + @NonNull GnssMeasurementCorrections measurementCorrections) { + try { + mGnssMeasurementCallbackTransport.injectGnssMeasurementCorrections( + measurementCorrections); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns an integer with flags representing the capabilities of the GNSS chipset. + * + * @hide + */ + @SystemApi + /** + * Returns the integer capability flags of the GNSS chipset as defined in {@code + * IGnssCallback.hal} + */ + public int getGnssCapabilities() { + try { + return mGnssMeasurementCallbackTransport.getGnssCapabilities(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * No-op method to keep backward-compatibility. Don't use it. Use {@link + * #unregisterGnssMeasurementsCallback} instead. + * * @hide * @deprecated use {@link #unregisterGnssMeasurementsCallback(GnssMeasurementsEvent.Callback)} - * instead. + * instead. */ @Deprecated @SystemApi @SuppressLint("Doclava125") - public void removeGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) { - } + public void removeGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) {} /** * Unregisters a GPS Measurement callback. diff --git a/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java b/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java new file mode 100644 index 000000000000..c18d58f9a704 --- /dev/null +++ b/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java @@ -0,0 +1,107 @@ +/* + * 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.location; + +import android.os.Parcel; + +import junit.framework.TestCase; + +import java.util.ArrayList; +import java.util.List; + +/** Unit tests for {@link GnssMeasurementCorrections}. */ +public class GnssMeasurementCorrectionsTest extends TestCase { + public void testDescribeContents() { + GnssMeasurementCorrections measurementCorrections = + new GnssMeasurementCorrections.Builder().build(); + measurementCorrections.describeContents(); + } + + public void testWriteToParcel() { + GnssMeasurementCorrections.Builder measurementCorrections = + new GnssMeasurementCorrections.Builder(); + setTestValues(measurementCorrections); + Parcel parcel = Parcel.obtain(); + measurementCorrections.build().writeToParcel(parcel, 0); + parcel.setDataPosition(0); + GnssMeasurementCorrections newMeasurementCorrection = + GnssMeasurementCorrections.CREATOR.createFromParcel(parcel); + verifyTestValues(newMeasurementCorrection); + parcel.recycle(); + } + + private static void verifyTestValues(GnssMeasurementCorrections measurementCorrections) { + assertEquals(37.386051, measurementCorrections.getLatitudeDegrees()); + assertEquals(-122.083855, measurementCorrections.getLongitudeDegrees()); + assertEquals(32.0, measurementCorrections.getAltitudeMeters()); + assertEquals(604000000000000L, measurementCorrections.getToaGpsNanosecondsOfWeek()); + + GnssSingleSatCorrection singleSatCorrection = + measurementCorrections.getSingleSatCorrectionList().get(0); + GnssSingleSatCorrectionsTest.verifyTestValues(singleSatCorrection); + + singleSatCorrection = measurementCorrections.getSingleSatCorrectionList().get(1); + assertEquals(15, singleSatCorrection.getSingleSatCorrectionFlags()); + assertEquals(GnssStatus.CONSTELLATION_GPS, singleSatCorrection.getConstellationType()); + assertEquals(11, singleSatCorrection.getSatId()); + assertEquals(1575430000f, singleSatCorrection.getCarrierFrequencyHz()); + assertEquals(false, singleSatCorrection.isSatelliteLineOfSight()); + assertEquals(50.0f, singleSatCorrection.getExcessPathLengthMeters()); + assertEquals(55.0f, singleSatCorrection.getExcessPathLengthUncertaintyMeters()); + GnssReflectingPlane reflectingPlane = singleSatCorrection.getReflectingPlane(); + assertEquals(37.386054, reflectingPlane.getLatitudeDegrees()); + assertEquals(-122.083855, reflectingPlane.getLongitudeDegrees()); + assertEquals(120.0, reflectingPlane.getAltitudeMeters()); + assertEquals(153.0, reflectingPlane.getAzimuthDegrees()); + } + + private static void setTestValues(GnssMeasurementCorrections.Builder measurementCorrections) { + measurementCorrections + .setLatitudeDegrees(37.386051) + .setLongitudeDegrees(-122.083855) + .setAltitudeMeters(32) + .setToaGpsNanosecondsOfWeek(604000000000000L); + List<GnssSingleSatCorrection> singleSatCorrectionList = new ArrayList<>(); + singleSatCorrectionList.add(GnssSingleSatCorrectionsTest.generateTestSingleSatCorrection()); + singleSatCorrectionList.add(generateTestSingleSatCorrection()); + measurementCorrections.setSingleSatCorrectionList(singleSatCorrectionList); + } + + private static GnssSingleSatCorrection generateTestSingleSatCorrection() { + GnssSingleSatCorrection.Builder singleSatCorrection = new GnssSingleSatCorrection.Builder(); + singleSatCorrection + .setSingleSatCorrectionFlags(8) + .setConstellationType(GnssStatus.CONSTELLATION_GPS) + .setSatId(11) + .setCarrierFrequencyHz(1575430000f) + .setSatIsLos(false) + .setExcessPathLengthMeters(50.0f) + .setExcessPathLengthUncertaintyMeters(55.0f) + .setReflectingPlane(generateTestReflectingPlane()); + return singleSatCorrection.build(); + } + + private static GnssReflectingPlane generateTestReflectingPlane() { + GnssReflectingPlane.Builder reflectingPlane = + new GnssReflectingPlane.Builder() + .setLatitudeDegrees(37.386054) + .setLongitudeDegrees(-122.083855) + .setAltitudeMeters(120.0) + .setAzimuthDegrees(153); + return reflectingPlane.build(); + } +} diff --git a/location/tests/locationtests/src/android/location/GnssReflectingPlaneTest.java b/location/tests/locationtests/src/android/location/GnssReflectingPlaneTest.java new file mode 100644 index 000000000000..d7a3378e5a12 --- /dev/null +++ b/location/tests/locationtests/src/android/location/GnssReflectingPlaneTest.java @@ -0,0 +1,67 @@ +/* + * 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.location; + +import android.os.Parcel; + +import junit.framework.TestCase; + +/** Unit tests for {@link GnssReflectingPlane}. */ +public class GnssReflectingPlaneTest extends TestCase { + public void testDescribeContents() { + GnssReflectingPlane reflectingPlane = new GnssReflectingPlane.Builder().build(); + reflectingPlane.describeContents(); + } + + public void testWriteToParcel() { + GnssReflectingPlane.Builder reflectingPlane = new GnssReflectingPlane.Builder(); + setTestValues(reflectingPlane); + Parcel parcel = Parcel.obtain(); + reflectingPlane.build().writeToParcel(parcel, 0); + parcel.setDataPosition(0); + GnssReflectingPlane newReflectingPlane = + GnssReflectingPlane.CREATOR.createFromParcel(parcel); + verifyTestValues(newReflectingPlane); + parcel.recycle(); + } + + public static void verifyTestValues(GnssReflectingPlane reflectingPlane) { + assertEquals(37.386052, reflectingPlane.getLatitudeDegrees()); + assertEquals(-122.083853, reflectingPlane.getLongitudeDegrees()); + assertEquals(100.0, reflectingPlane.getAltitudeMeters()); + assertEquals(123.0, reflectingPlane.getAzimuthDegrees()); + } + + private static void setTestValues(GnssReflectingPlane.Builder reflectingPlane) { + GnssReflectingPlane refPlane = generateTestReflectingPlane(); + reflectingPlane + .setLatitudeDegrees(refPlane.getLatitudeDegrees()) + .setLongitudeDegrees(refPlane.getLongitudeDegrees()) + .setAltitudeMeters(refPlane.getAltitudeMeters()) + .setAzimuthDegrees(refPlane.getAzimuthDegrees()); + } + + public static GnssReflectingPlane generateTestReflectingPlane() { + GnssReflectingPlane.Builder reflectingPlane = + new GnssReflectingPlane.Builder() + .setLatitudeDegrees(37.386052) + .setLongitudeDegrees(-122.083853) + .setAltitudeMeters(100.0) + .setAzimuthDegrees(123.0); + return reflectingPlane.build(); + } +} diff --git a/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java b/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java new file mode 100644 index 000000000000..2e54ae463595 --- /dev/null +++ b/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java @@ -0,0 +1,81 @@ +/* + * 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.location; + +import android.os.Parcel; + +import junit.framework.TestCase; + +/** Unit tests for {@link GnssSingleSatCorrection}. */ +public class GnssSingleSatCorrectionsTest extends TestCase { + public void testDescribeContents() { + GnssSingleSatCorrection singleSatCorrection = new GnssSingleSatCorrection.Builder().build(); + singleSatCorrection.describeContents(); + } + + public void testWriteToParcel() { + GnssSingleSatCorrection.Builder singleSatCorrection = new GnssSingleSatCorrection.Builder(); + setTestValues(singleSatCorrection); + Parcel parcel = Parcel.obtain(); + singleSatCorrection.build().writeToParcel(parcel, 0); + parcel.setDataPosition(0); + GnssSingleSatCorrection newSingleSatCorrection = + GnssSingleSatCorrection.CREATOR.createFromParcel(parcel); + verifyTestValues(newSingleSatCorrection); + parcel.recycle(); + } + + public static void verifyTestValues(GnssSingleSatCorrection singleSatCorrection) { + assertEquals(15, singleSatCorrection.getSingleSatCorrectionFlags()); + assertEquals(GnssStatus.CONSTELLATION_GALILEO, singleSatCorrection.getConstellationType()); + assertEquals(12, singleSatCorrection.getSatId()); + assertEquals(1575420000f, singleSatCorrection.getCarrierFrequencyHz()); + assertEquals(true, singleSatCorrection.isSatelliteLineOfSight()); + assertEquals(10.0f, singleSatCorrection.getExcessPathLengthMeters()); + assertEquals(5.0f, singleSatCorrection.getExcessPathLengthUncertaintyMeters()); + GnssReflectingPlane reflectingPlane = singleSatCorrection.getReflectingPlane(); + GnssReflectingPlaneTest.verifyTestValues(reflectingPlane); + } + + private static void setTestValues(GnssSingleSatCorrection.Builder singleSatCorrection) { + GnssSingleSatCorrection singleSatCorr = generateTestSingleSatCorrection(); + singleSatCorrection + .setSingleSatCorrectionFlags(singleSatCorr.getSingleSatCorrectionFlags()) + .setConstellationType(singleSatCorr.getConstellationType()) + .setSatId(singleSatCorr.getSatId()) + .setCarrierFrequencyHz(singleSatCorr.getCarrierFrequencyHz()) + .setSatIsLos(singleSatCorr.isSatelliteLineOfSight()) + .setExcessPathLengthMeters(singleSatCorr.getExcessPathLengthMeters()) + .setExcessPathLengthUncertaintyMeters( + singleSatCorr.getExcessPathLengthUncertaintyMeters()) + .setReflectingPlane(singleSatCorr.getReflectingPlane()); + } + + public static GnssSingleSatCorrection generateTestSingleSatCorrection() { + GnssSingleSatCorrection.Builder singleSatCorrection = + new GnssSingleSatCorrection.Builder() + .setSingleSatCorrectionFlags(15) + .setConstellationType(GnssStatus.CONSTELLATION_GALILEO) + .setSatId(12) + .setCarrierFrequencyHz(1575420000f) + .setSatIsLos(true) + .setExcessPathLengthMeters(10.0f) + .setExcessPathLengthUncertaintyMeters(5.0f) + .setReflectingPlane(GnssReflectingPlaneTest.generateTestReflectingPlane()); + return singleSatCorrection.build(); + } +} diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 4b2353c992f2..33f81f1db69c 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -16,8 +16,10 @@ package android.media; +import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.app.ActivityThread; @@ -43,6 +45,7 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.concurrent.Executor; /** * The AudioRecord class manages the audio resources for Java applications @@ -58,7 +61,7 @@ import java.util.List; * been read yet. Data should be read from the audio hardware in chunks of sizes inferior to * the total recording buffer size. */ -public class AudioRecord implements AudioRouting +public class AudioRecord implements AudioRouting, AudioRecordingMonitor, AudioRecordingMonitorClient { //--------------------------------------------------------- // Constants @@ -1654,6 +1657,56 @@ public class AudioRecord implements AudioRouting return activeMicrophones; } + + //-------------------------------------------------------------------------- + // Implementation of AudioRecordingMonitor interface + //-------------------- + + AudioRecordingMonitorImpl mRecordingInfoImpl = + new AudioRecordingMonitorImpl((AudioRecordingMonitorClient) this); + + /** + * Register a callback to be notified of audio capture changes via a + * {@link AudioManager.AudioRecordingCallback}. A callback is received when the capture path + * configuration changes (pre-processing, format, sampling rate...) or capture is + * silenced/unsilenced by the system. + * @param executor {@link Executor} to handle the callbacks. + * @param cb non-null callback to register + */ + public void registerAudioRecordingCallback(@NonNull @CallbackExecutor Executor executor, + @NonNull AudioManager.AudioRecordingCallback cb) { + mRecordingInfoImpl.registerAudioRecordingCallback(executor, cb); + } + + /** + * Unregister an audio recording callback previously registered with + * {@link #registerAudioRecordingCallback(Executor, AudioManager.AudioRecordingCallback)}. + * @param cb non-null callback to unregister + */ + public void unregisterAudioRecordingCallback(@NonNull AudioManager.AudioRecordingCallback cb) { + mRecordingInfoImpl.unregisterAudioRecordingCallback(cb); + } + + /** + * Returns the current active audio recording for this audio recorder. + * @return a valid {@link AudioRecordingConfiguration} if this recorder is active + * or null otherwise. + * @see AudioRecordingConfiguration + */ + public @Nullable AudioRecordingConfiguration getActiveRecordingConfiguration() { + return mRecordingInfoImpl.getActiveRecordingConfiguration(); + } + + //--------------------------------------------------------- + // Implementation of AudioRecordingMonitorClient interface + //-------------------- + /** + * @hide + */ + public int getPortId() { + return native_getPortId(); + } + //--------------------------------------------------------- // Interface definitions //-------------------- diff --git a/media/java/android/media/AudioRecordingMonitor.java b/media/java/android/media/AudioRecordingMonitor.java new file mode 100644 index 000000000000..e2605d074c86 --- /dev/null +++ b/media/java/android/media/AudioRecordingMonitor.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.annotation.CallbackExecutor; +import android.annotation.NonNull; +import android.annotation.Nullable; + +import java.util.concurrent.Executor; + +/** + * AudioRecordingMonitor defines an interface implemented by {@link AudioRecord} and + * {@link MediaRecorder} allowing applications to install a callback and be notified of changes + * in the capture path while recoding is active. + */ +public interface AudioRecordingMonitor { + /** + * Register a callback to be notified of audio capture changes via a + * {@link AudioManager.AudioRecordingCallback}. A callback is received when the capture path + * configuration changes (pre-processing, format, sampling rate...) or capture is + * silenced/unsilenced by the system. + * @param executor {@link Executor} to handle the callbacks. + * @param cb non-null callback to register + */ + void registerAudioRecordingCallback(@NonNull @CallbackExecutor Executor executor, + @NonNull AudioManager.AudioRecordingCallback cb); + + /** + * Unregister an audio recording callback previously registered with + * {@link #registerAudioRecordingCallback(Executor, AudioManager.AudioRecordingCallback)}. + * @param cb non-null callback to unregister + */ + void unregisterAudioRecordingCallback(@NonNull AudioManager.AudioRecordingCallback cb); + + /** + * Returns the current active audio recording for this audio recorder. + * @return a valid {@link AudioRecordingConfiguration} if this recorder is active + * or null otherwise. + * @see AudioRecordingConfiguration + */ + @Nullable AudioRecordingConfiguration getActiveRecordingConfiguration(); +} diff --git a/media/java/android/media/AudioRecordingMonitorClient.java b/media/java/android/media/AudioRecordingMonitorClient.java new file mode 100644 index 000000000000..7578d9b9a5fd --- /dev/null +++ b/media/java/android/media/AudioRecordingMonitorClient.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +/** + * Interface implemented by classes using { @link AudioRecordingMonitor} interface. + * @hide + */ +public interface AudioRecordingMonitorClient { + /** + * @return the unique port ID allocated by audio framework to this recorder + */ + int getPortId(); +} diff --git a/media/java/android/media/AudioRecordingMonitorImpl.java b/media/java/android/media/AudioRecordingMonitorImpl.java new file mode 100644 index 000000000000..c2cd4bc0b7eb --- /dev/null +++ b/media/java/android/media/AudioRecordingMonitorImpl.java @@ -0,0 +1,250 @@ +/* + * 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.media; + +import android.annotation.CallbackExecutor; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.os.Binder; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +import com.android.internal.annotations.GuardedBy; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Executor; + +/** + * Implementation of AudioRecordingMonitor interface. + * @hide + */ +public class AudioRecordingMonitorImpl implements AudioRecordingMonitor { + + private static final String TAG = "android.media.AudioRecordingMonitor"; + + private static IAudioService sService; //lazy initialization, use getService() + + private final AudioRecordingMonitorClient mClient; + + AudioRecordingMonitorImpl(@NonNull AudioRecordingMonitorClient client) { + mClient = client; + } + + /** + * Register a callback to be notified of audio capture changes via a + * {@link AudioManager.AudioRecordingCallback}. A callback is received when the capture path + * configuration changes (pre-processing, format, sampling rate...) or capture is + * silenced/unsilenced by the system. + * @param executor {@link Executor} to handle the callbacks. + * @param cb non-null callback to register + */ + public void registerAudioRecordingCallback(@NonNull @CallbackExecutor Executor executor, + @NonNull AudioManager.AudioRecordingCallback cb) { + if (cb == null) { + throw new IllegalArgumentException("Illegal null AudioRecordingCallback"); + } + if (executor == null) { + throw new IllegalArgumentException("Illegal null Executor"); + } + synchronized (mRecordCallbackLock) { + // check if eventCallback already in list + for (AudioRecordingCallbackInfo arci : mRecordCallbackList) { + if (arci.mCb == cb) { + throw new IllegalArgumentException( + "AudioRecordingCallback already registered"); + } + } + beginRecordingCallbackHandling(); + mRecordCallbackList.add(new AudioRecordingCallbackInfo(executor, cb)); + } + } + + /** + * Unregister an audio recording callback previously registered with + * {@link #registerAudioRecordingCallback(Executor, AudioManager.AudioRecordingCallback)}. + * @param cb non-null callback to unregister + */ + public void unregisterAudioRecordingCallback(@NonNull AudioManager.AudioRecordingCallback cb) { + if (cb == null) { + throw new IllegalArgumentException("Illegal null AudioRecordingCallback argument"); + } + + synchronized (mRecordCallbackLock) { + for (AudioRecordingCallbackInfo arci : mRecordCallbackList) { + if (arci.mCb == cb) { + // ok to remove while iterating over list as we exit iteration + mRecordCallbackList.remove(arci); + if (mRecordCallbackList.size() == 0) { + endRecordingCallbackHandling(); + } + return; + } + } + throw new IllegalArgumentException("AudioRecordingCallback was not registered"); + } + } + + /** + * Returns the current active audio recording for this audio recorder. + * @return a valid {@link AudioRecordingConfiguration} if this recorder is active + * or null otherwise. + * @see AudioRecordingConfiguration + */ + public @Nullable AudioRecordingConfiguration getActiveRecordingConfiguration() { + final IAudioService service = getService(); + try { + List<AudioRecordingConfiguration> configs = service.getActiveRecordingConfigurations(); + return getMyConfig(configs); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + private static class AudioRecordingCallbackInfo { + final AudioManager.AudioRecordingCallback mCb; + final Executor mExecutor; + AudioRecordingCallbackInfo(Executor e, AudioManager.AudioRecordingCallback cb) { + mExecutor = e; + mCb = cb; + } + } + + private static final int MSG_RECORDING_CONFIG_CHANGE = 1; + + private final Object mRecordCallbackLock = new Object(); + @GuardedBy("mRecordCallbackLock") + @NonNull private LinkedList<AudioRecordingCallbackInfo> mRecordCallbackList = + new LinkedList<AudioRecordingCallbackInfo>(); + @GuardedBy("mRecordCallbackLock") + private @Nullable HandlerThread mRecordingCallbackHandlerThread; + @GuardedBy("mRecordCallbackLock") + private @Nullable volatile Handler mRecordingCallbackHandler; + + @GuardedBy("mRecordCallbackLock") + private final IRecordingConfigDispatcher mRecordingCallback = + new IRecordingConfigDispatcher.Stub() { + @Override + public void dispatchRecordingConfigChange(List<AudioRecordingConfiguration> configs) { + AudioRecordingConfiguration config = getMyConfig(configs); + if (config != null) { + synchronized (mRecordCallbackLock) { + if (mRecordingCallbackHandler != null) { + final Message m = mRecordingCallbackHandler.obtainMessage( + MSG_RECORDING_CONFIG_CHANGE/*what*/, config /*obj*/); + mRecordingCallbackHandler.sendMessage(m); + } + } + } + } + }; + + @GuardedBy("mRecordCallbackLock") + private void beginRecordingCallbackHandling() { + if (mRecordingCallbackHandlerThread == null) { + mRecordingCallbackHandlerThread = new HandlerThread(TAG + ".RecordingCallback"); + mRecordingCallbackHandlerThread.start(); + final Looper looper = mRecordingCallbackHandlerThread.getLooper(); + if (looper != null) { + mRecordingCallbackHandler = new Handler(looper) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_RECORDING_CONFIG_CHANGE: { + if (msg.obj == null) { + return; + } + ArrayList<AudioRecordingConfiguration> configs = + new ArrayList<AudioRecordingConfiguration>(); + configs.add((AudioRecordingConfiguration) msg.obj); + + final LinkedList<AudioRecordingCallbackInfo> cbInfoList; + synchronized (mRecordCallbackLock) { + if (mRecordCallbackList.size() == 0) { + return; + } + cbInfoList = new LinkedList<AudioRecordingCallbackInfo>( + mRecordCallbackList); + } + + final long identity = Binder.clearCallingIdentity(); + try { + for (AudioRecordingCallbackInfo cbi : cbInfoList) { + cbi.mExecutor.execute(() -> + cbi.mCb.onRecordingConfigChanged(configs)); + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } break; + default: + Log.e(TAG, "Unknown event " + msg.what); + break; + } + } + }; + final IAudioService service = getService(); + try { + service.registerRecordingCallback(mRecordingCallback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + } + + @GuardedBy("mRecordCallbackLock") + private void endRecordingCallbackHandling() { + if (mRecordingCallbackHandlerThread != null) { + final IAudioService service = getService(); + try { + service.unregisterRecordingCallback(mRecordingCallback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + mRecordingCallbackHandlerThread.quit(); + mRecordingCallbackHandlerThread = null; + } + } + + AudioRecordingConfiguration getMyConfig(List<AudioRecordingConfiguration> configs) { + int portId = mClient.getPortId(); + for (AudioRecordingConfiguration config : configs) { + if (config.getClientPortId() == portId) { + return config; + } + } + return null; + } + + private static IAudioService getService() { + if (sService != null) { + return sService; + } + IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); + sService = IAudioService.Stub.asInterface(b); + return sService; + } +} diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java index a3702d68cc49..34345b31ded1 100644 --- a/media/java/android/media/MediaPlayer2.java +++ b/media/java/android/media/MediaPlayer2.java @@ -793,7 +793,7 @@ public class MediaPlayer2 implements AutoCloseable // throws IllegalArgumentException if dsd is null or underline PFD of dsd has been closed. private void checkDataSourceDesc(DataSourceDesc dsd) { - if (dsd != null) { + if (dsd == null) { throw new IllegalArgumentException("dsd is expected to be non null"); } if (dsd instanceof FileDataSourceDesc) { diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 8ced021b1025..1cdc29102758 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -16,7 +16,9 @@ package android.media; +import android.annotation.CallbackExecutor; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; @@ -40,6 +42,8 @@ import java.io.RandomAccessFile; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executor; + /** * Used to record audio and video. The recording control is based on a @@ -83,7 +87,9 @@ import java.util.List; * <a href="{@docRoot}guide/topics/media/audio-capture.html">Audio Capture</a> developer guide.</p> * </div> */ -public class MediaRecorder implements AudioRouting +public class MediaRecorder implements AudioRouting, + AudioRecordingMonitor, + AudioRecordingMonitorClient { static { System.loadLibrary("media_jni"); @@ -304,7 +310,7 @@ public class MediaRecorder implements AudioRouting /** * Audio source for preemptible, low-priority software hotword detection - * It presents the same gain and pre processing tuning as {@link #VOICE_RECOGNITION}. + * It presents the same gain and pre-processing tuning as {@link #VOICE_RECOGNITION}. * <p> * An application should use this audio source when it wishes to do * always-on software hotword detection, while gracefully giving in to any other application @@ -1471,6 +1477,57 @@ public class MediaRecorder implements AudioRouting private native final int native_getActiveMicrophones( ArrayList<MicrophoneInfo> activeMicrophones); + //-------------------------------------------------------------------------- + // Implementation of AudioRecordingMonitor interface + //-------------------- + + AudioRecordingMonitorImpl mRecordingInfoImpl = + new AudioRecordingMonitorImpl((AudioRecordingMonitorClient) this); + + /** + * Register a callback to be notified of audio capture changes via a + * {@link AudioManager.AudioRecordingCallback}. A callback is received when the capture path + * configuration changes (pre-processing, format, sampling rate...) or capture is + * silenced/unsilenced by the system. + * @param executor {@link Executor} to handle the callbacks. + * @param cb non-null callback to register + */ + public void registerAudioRecordingCallback(@NonNull @CallbackExecutor Executor executor, + @NonNull AudioManager.AudioRecordingCallback cb) { + mRecordingInfoImpl.registerAudioRecordingCallback(executor, cb); + } + + /** + * Unregister an audio recording callback previously registered with + * {@link #registerAudioRecordingCallback(Executor, AudioManager.AudioRecordingCallback)}. + * @param cb non-null callback to unregister + */ + public void unregisterAudioRecordingCallback(@NonNull AudioManager.AudioRecordingCallback cb) { + mRecordingInfoImpl.unregisterAudioRecordingCallback(cb); + } + + /** + * Returns the current active audio recording for this audio recorder. + * @return a valid {@link AudioRecordingConfiguration} if this recorder is active + * or null otherwise. + * @see AudioRecordingConfiguration + */ + public @Nullable AudioRecordingConfiguration getActiveRecordingConfiguration() { + return mRecordingInfoImpl.getActiveRecordingConfiguration(); + } + + //--------------------------------------------------------- + // Implementation of AudioRecordingMonitorClient interface + //-------------------- + /** + * @hide + */ + public int getPortId() { + return native_getPortId(); + } + + private native int native_getPortId(); + /** * Called from native code when an interesting event happens. This method * just uses the EventHandler system to post the event back to the main app thread. diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp index b3a8b21147c1..ca30f32ea438 100644 --- a/media/jni/android_media_MediaRecorder.cpp +++ b/media/jni/android_media_MediaRecorder.cpp @@ -763,6 +763,20 @@ android_media_MediaRecord_getActiveMicrophones(JNIEnv *env, } return jStatus; } + +static jint android_media_MediaRecord_getPortId(JNIEnv *env, jobject thiz) { + sp<MediaRecorder> mr = getMediaRecorder(env, thiz); + if (mr == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return (jint)AUDIO_PORT_HANDLE_NONE; + } + + audio_port_handle_t portId; + process_media_recorder_call(env, mr->getPortId(&portId), + "java/lang/RuntimeException", "getPortId failed."); + return (jint)portId; +} + // ---------------------------------------------------------------------------- static const JNINativeMethod gMethods[] = { @@ -801,6 +815,7 @@ static const JNINativeMethod gMethods[] = { {"native_enableDeviceCallback", "(Z)V", (void *)android_media_MediaRecorder_enableDeviceCallback}, {"native_getActiveMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_MediaRecord_getActiveMicrophones}, + {"native_getPortId", "()I", (void *)android_media_MediaRecord_getPortId}, }; // This function only registers the native methods, and is called from diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml index c527711f563f..2e05c382a35e 100644 --- a/packages/CarSystemUI/res/values/config.xml +++ b/packages/CarSystemUI/res/values/config.xml @@ -32,7 +32,7 @@ SystemUi b/c it can't be overlayed at this level for now --> <string-array name="config_systemUIServiceComponents" translatable="false"> - <item>com.android.systemui.Dependency</item> + <item>com.android.systemui.Dependency$DependencyCreator</item> <item>com.android.systemui.util.NotificationChannels</item> <item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item> <item>com.android.systemui.keyguard.KeyguardViewMediator</item> diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml index 0be71e6d17b9..42885e8193a7 100644 --- a/packages/CompanionDeviceManager/AndroidManifest.xml +++ b/packages/CompanionDeviceManager/AndroidManifest.xml @@ -25,7 +25,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_FINE_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/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java index 133d8ba8357b..af52c00388f3 100644 --- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java +++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java @@ -117,7 +117,7 @@ public class Assistant extends NotificationAssistantService { mPackageManager = ActivityThread.getPackageManager(); mSettings = mSettingsFactory.createAndRegister(mHandler, getApplicationContext().getContentResolver(), getUserId(), this::updateThresholds); - mSmartActionsHelper = new SmartActionsHelper(); + mSmartActionsHelper = new SmartActionsHelper(getContext(), mSettings); mNotificationCategorizer = new NotificationCategorizer(); mAgingHelper = new AgingHelper(getContext(), mNotificationCategorizer, @@ -215,10 +215,8 @@ public class Assistant extends NotificationAssistantService { return null; } NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel); - ArrayList<Notification.Action> actions = - mSmartActionsHelper.suggestActions(this, entry, mSettings); - ArrayList<CharSequence> replies = - mSmartActionsHelper.suggestReplies(this, entry, mSettings); + ArrayList<Notification.Action> actions = mSmartActionsHelper.suggestActions(entry); + ArrayList<CharSequence> replies = mSmartActionsHelper.suggestReplies(entry); return createEnqueuedNotificationAdjustment(entry, actions, replies); } @@ -294,7 +292,7 @@ public class Assistant extends NotificationAssistantService { synchronized (mkeyToImpressions) { ChannelImpressions ci = mkeyToImpressions.getOrDefault(key, createChannelImpressionsWithThresholds()); - if (stats.hasSeen()) { + if (stats != null && stats.hasSeen()) { ci.incrementViews(); updatedImpressions = true; } @@ -343,6 +341,7 @@ public class Assistant extends NotificationAssistantService { if (entry != null) { entry.setSeen(); mAgingHelper.onNotificationSeen(entry); + mSmartActionsHelper.onNotificationSeen(entry); } } } catch (Throwable e) { @@ -351,34 +350,46 @@ public class Assistant extends NotificationAssistantService { } @Override - public void onNotificationExpansionChanged(String key, boolean isUserAction, + public void onNotificationExpansionChanged(@NonNull String key, boolean isUserAction, boolean isExpanded) { if (DEBUG) { - Log.i(TAG, - "onNotificationExpansionChanged " + key + ", isUserAction =" + isUserAction - + ", isExpanded = isExpanded"); + Log.d(TAG, "onNotificationExpansionChanged() called with: key = [" + key + + "], isUserAction = [" + isUserAction + "], isExpanded = [" + isExpanded + + "]"); + } + NotificationEntry entry = mLiveNotifications.get(key); + + if (entry != null) { + entry.setExpanded(isExpanded); + mSmartActionsHelper.onNotificationExpansionChanged(entry, isUserAction, isExpanded); } } @Override - public void onNotificationDirectReply(String key) { + public void onNotificationDirectReply(@NonNull String key) { if (DEBUG) Log.i(TAG, "onNotificationDirectReply " + key); + mSmartActionsHelper.onNotificationDirectReply(key); } @Override - public void onSuggestedReplySent(String key, CharSequence reply, int source) { + public void onSuggestedReplySent(@NonNull String key, @NonNull CharSequence reply, + @Source int source) { if (DEBUG) { Log.d(TAG, "onSuggestedReplySent() called with: key = [" + key + "], reply = [" + reply + "], source = [" + source + "]"); } + mSmartActionsHelper.onSuggestedReplySent(key, reply, source); } @Override - public void onActionClicked(String key, Notification.Action action, int source) { + public void onActionClicked(@NonNull String key, @NonNull Notification.Action action, + @Source int source) { if (DEBUG) { - Log.d(TAG, "onActionClicked() called with: key = [" + key + "], action = [" + action.title - + "], source = [" + source + "]"); + Log.d(TAG, + "onActionClicked() called with: key = [" + key + "], action = [" + action.title + + "], source = [" + source + "]"); } + mSmartActionsHelper.onActionClicked(key, action, source); } @Override diff --git a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java b/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java index 6f437bd5d96f..71fd9ce6e4af 100644 --- a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java +++ b/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java @@ -52,6 +52,8 @@ public class NotificationEntry { private NotificationChannel mChannel; private int mImportance; private boolean mSeen; + private boolean mExpanded; + private boolean mIsShowActionEventLogged; public NotificationEntry(IPackageManager packageManager, StatusBarNotification sbn, NotificationChannel channel) { @@ -216,10 +218,26 @@ public class NotificationEntry { mSeen = true; } + public void setExpanded(boolean expanded) { + mExpanded = expanded; + } + + public void setShowActionEventLogged() { + mIsShowActionEventLogged = true; + } + public boolean hasSeen() { return mSeen; } + public boolean isExpanded() { + return mExpanded; + } + + public boolean isShowActionEventLogged() { + return mIsShowActionEventLogged; + } + public StatusBarNotification getSbn() { return mSbn; } diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java index 38df9b0a6fdc..b041842c45b9 100644 --- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java +++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java @@ -24,12 +24,16 @@ import android.content.Context; import android.os.Bundle; import android.os.Parcelable; import android.os.Process; +import android.service.notification.NotificationAssistantService; import android.text.TextUtils; import android.util.ArrayMap; +import android.util.LruCache; import android.view.textclassifier.ConversationActions; import android.view.textclassifier.TextClassification; +import android.view.textclassifier.TextClassificationContext; import android.view.textclassifier.TextClassificationManager; import android.view.textclassifier.TextClassifier; +import android.view.textclassifier.TextClassifierEvent; import android.view.textclassifier.TextLinks; import java.time.Instant; @@ -47,6 +51,7 @@ public class SmartActionsHelper { private static final ArrayList<Notification.Action> EMPTY_ACTION_LIST = new ArrayList<>(); private static final ArrayList<CharSequence> EMPTY_REPLY_LIST = new ArrayList<>(); + private static final String KEY_ACTION_TYPE = "action_type"; // If a notification has any of these flags set, it's inelgibile for actions being added. private static final int FLAG_MASK_INELGIBILE_FOR_ACTIONS = Notification.FLAG_ONGOING_EVENT @@ -59,6 +64,7 @@ public class SmartActionsHelper { private static final int MAX_SUGGESTED_REPLIES = 3; // TODO: Make this configurable. private static final int MAX_MESSAGES_TO_EXTRACT = 5; + private static final int MAX_RESULT_ID_TO_CACHE = 20; private static final ConversationActions.TypeConfig TYPE_CONFIG = new ConversationActions.TypeConfig.Builder().setIncludedTypes( @@ -68,26 +74,36 @@ public class SmartActionsHelper { private static final List<String> HINTS = Collections.singletonList(ConversationActions.HINT_FOR_NOTIFICATION); - SmartActionsHelper() { + private Context mContext; + @Nullable + private TextClassifier mTextClassifier; + @NonNull + private AssistantSettings mSettings; + private LruCache<String, String> mNotificationKeyToResultIdCache = + new LruCache<>(MAX_RESULT_ID_TO_CACHE); + + SmartActionsHelper(Context context, AssistantSettings settings) { + mContext = context; + TextClassificationManager textClassificationManager = + mContext.getSystemService(TextClassificationManager.class); + if (textClassificationManager != null) { + mTextClassifier = textClassificationManager.getTextClassifier(); + } + mSettings = settings; } /** * Adds action adjustments based on the notification contents. */ @NonNull - ArrayList<Notification.Action> suggestActions(@Nullable Context context, - @NonNull NotificationEntry entry, @NonNull AssistantSettings settings) { - if (!settings.mGenerateActions) { + ArrayList<Notification.Action> suggestActions(@NonNull NotificationEntry entry) { + if (!mSettings.mGenerateActions) { return EMPTY_ACTION_LIST; } if (!isEligibleForActionAdjustment(entry)) { return EMPTY_ACTION_LIST; } - if (context == null) { - return EMPTY_ACTION_LIST; - } - TextClassificationManager tcm = context.getSystemService(TextClassificationManager.class); - if (tcm == null) { + if (mTextClassifier == null) { return EMPTY_ACTION_LIST; } List<ConversationActions.Message> messages = extractMessages(entry.getNotification()); @@ -96,22 +112,17 @@ public class SmartActionsHelper { } // TODO: Move to TextClassifier.suggestConversationActions once it is ready. return suggestActionsFromText( - tcm, messages.get(messages.size() - 1).getText(), MAX_SMART_ACTIONS); + messages.get(messages.size() - 1).getText(), MAX_SMART_ACTIONS); } - ArrayList<CharSequence> suggestReplies(@Nullable Context context, - @NonNull NotificationEntry entry, @NonNull AssistantSettings settings) { - if (!settings.mGenerateReplies) { + ArrayList<CharSequence> suggestReplies(@NonNull NotificationEntry entry) { + if (!mSettings.mGenerateReplies) { return EMPTY_REPLY_LIST; } if (!isEligibleForReplyAdjustment(entry)) { return EMPTY_REPLY_LIST; } - if (context == null) { - return EMPTY_REPLY_LIST; - } - TextClassificationManager tcm = context.getSystemService(TextClassificationManager.class); - if (tcm == null) { + if (mTextClassifier == null) { return EMPTY_REPLY_LIST; } List<ConversationActions.Message> messages = extractMessages(entry.getNotification()); @@ -125,14 +136,122 @@ public class SmartActionsHelper { .setTypeConfig(TYPE_CONFIG) .build(); - TextClassifier textClassifier = tcm.getTextClassifier(); + ConversationActions conversationActionsResult = + mTextClassifier.suggestConversationActions(request); List<ConversationActions.ConversationAction> conversationActions = - textClassifier.suggestConversationActions(request).getConversationActions(); - - return conversationActions.stream() + conversationActionsResult.getConversationActions(); + ArrayList<CharSequence> replies = conversationActions.stream() .map(conversationAction -> conversationAction.getTextReply()) .filter(textReply -> !TextUtils.isEmpty(textReply)) .collect(Collectors.toCollection(ArrayList::new)); + + String resultId = conversationActionsResult.getId(); + if (resultId != null && !replies.isEmpty()) { + mNotificationKeyToResultIdCache.put(entry.getSbn().getKey(), resultId); + } + return replies; + } + + void onNotificationSeen(@NonNull NotificationEntry entry) { + if (entry.isExpanded()) { + maybeSendActionShownEvent(entry); + } + } + + void onNotificationExpansionChanged(@NonNull NotificationEntry entry, boolean isUserAction, + boolean isExpanded) { + // Notification can be expanded in the background, and thus the isUserAction check. + if (isUserAction && isExpanded) { + maybeSendActionShownEvent(entry); + } + } + + void onNotificationDirectReply(@NonNull String key) { + if (mTextClassifier == null) { + return; + } + String resultId = mNotificationKeyToResultIdCache.get(key); + if (resultId == null) { + return; + } + TextClassifierEvent textClassifierEvent = + createTextClassifierEventBuilder(TextClassifierEvent.TYPE_MANUAL_REPLY, resultId) + .build(); + mTextClassifier.onTextClassifierEvent(textClassifierEvent); + } + + void onSuggestedReplySent(@NonNull String key, @NonNull CharSequence reply, + @NotificationAssistantService.Source int source) { + if (mTextClassifier == null) { + return; + } + if (source != NotificationAssistantService.SOURCE_FROM_ASSISTANT) { + return; + } + String resultId = mNotificationKeyToResultIdCache.get(key); + if (resultId == null) { + return; + } + TextClassifierEvent textClassifierEvent = + createTextClassifierEventBuilder(TextClassifierEvent.TYPE_SMART_ACTION, resultId) + .setEntityType(ConversationActions.TYPE_TEXT_REPLY) + .build(); + mTextClassifier.onTextClassifierEvent(textClassifierEvent); + } + + void onActionClicked(@NonNull String key, @NonNull Notification.Action action, + @NotificationAssistantService.Source int source) { + if (mTextClassifier == null) { + return; + } + if (source != NotificationAssistantService.SOURCE_FROM_ASSISTANT) { + return; + } + String resultId = mNotificationKeyToResultIdCache.get(key); + if (resultId == null) { + return; + } + String actionType = action.getExtras().getString(KEY_ACTION_TYPE); + if (actionType == null) { + return; + } + TextClassifierEvent textClassifierEvent = + createTextClassifierEventBuilder(TextClassifierEvent.TYPE_SMART_ACTION, resultId) + .setEntityType(actionType) + .build(); + mTextClassifier.onTextClassifierEvent(textClassifierEvent); + } + + private TextClassifierEvent.Builder createTextClassifierEventBuilder( + int eventType, @NonNull String resultId) { + return new TextClassifierEvent.Builder( + TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS, eventType) + .setEventTime(System.currentTimeMillis()) + .setEventContext( + new TextClassificationContext.Builder( + mContext.getPackageName(), TextClassifier.WIDGET_TYPE_NOTIFICATION) + .build()) + .setResultId(resultId); + } + + private void maybeSendActionShownEvent(@NonNull NotificationEntry entry) { + if (mTextClassifier == null) { + return; + } + String resultId = mNotificationKeyToResultIdCache.get(entry.getSbn().getKey()); + if (resultId == null) { + return; + } + // Only report if this is the first time the user sees these suggestions. + if (entry.isShowActionEventLogged()) { + return; + } + entry.setShowActionEventLogged(); + TextClassifierEvent textClassifierEvent = + createTextClassifierEventBuilder(TextClassifierEvent.TYPE_ACTIONS_SHOWN, resultId) + .build(); + // TODO: If possible, report which replies / actions are actually seen by user. + mTextClassifier.onTextClassifierEvent(textClassifierEvent); } /** @@ -220,13 +339,10 @@ public class SmartActionsHelper { /** Returns a list of actions to act on entities in a given piece of text. */ @NonNull private ArrayList<Notification.Action> suggestActionsFromText( - @NonNull TextClassificationManager tcm, @Nullable CharSequence text, - int maxSmartActions) { + @Nullable CharSequence text, int maxSmartActions) { if (TextUtils.isEmpty(text)) { return EMPTY_ACTION_LIST; } - TextClassifier textClassifier = tcm.getTextClassifier(); - // We want to process only text visible to the user to avoid confusing suggestions, so we // truncate the text to a reasonable length. This is particularly important for e.g. // email apps that sometimes include the text for the entire thread. @@ -239,7 +355,7 @@ public class SmartActionsHelper { Collections.singletonList( TextClassifier.HINT_TEXT_IS_NOT_EDITABLE))) .build(); - TextLinks links = textClassifier.generateLinks(textLinksRequest); + TextLinks links = mTextClassifier.generateLinks(textLinksRequest); ArrayMap<String, Integer> entityTypeFrequency = getEntityTypeFrequency(links); ArrayList<Notification.Action> actions = new ArrayList<>(); @@ -254,19 +370,26 @@ public class SmartActionsHelper { // Generate the actions, and add the most prominent ones to the action bar. TextClassification classification = - textClassifier.classifyText( + mTextClassifier.classifyText( new TextClassification.Request.Builder( text, link.getStart(), link.getEnd()).build()); + if (classification.getEntityCount() == 0) { + continue; + } int numOfActions = Math.min( MAX_ACTIONS_PER_LINK, classification.getActions().size()); for (int i = 0; i < numOfActions; ++i) { - RemoteAction action = classification.getActions().get(i); - actions.add( - new Notification.Action.Builder( - action.getIcon(), - action.getTitle(), - action.getActionIntent()) - .build()); + RemoteAction remoteAction = classification.getActions().get(i); + Notification.Action action = new Notification.Action.Builder( + remoteAction.getIcon(), + remoteAction.getTitle(), + remoteAction.getActionIntent()) + .setSemanticAction( + Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) + .addExtras(Bundle.forPair(KEY_ACTION_TYPE, classification.getEntity(0))) + .build(); + actions.add(action); + // We have enough smart actions. if (actions.size() >= maxSmartActions) { return actions; diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java index 0352ebcec8b3..da382a003621 100644 --- a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java +++ b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java @@ -28,12 +28,14 @@ import android.app.Notification; import android.app.Person; import android.content.Context; import android.os.Process; +import android.service.notification.NotificationAssistantService; import android.service.notification.StatusBarNotification; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import android.view.textclassifier.ConversationActions; import android.view.textclassifier.TextClassificationManager; import android.view.textclassifier.TextClassifier; +import android.view.textclassifier.TextClassifierEvent; import com.google.common.truth.FailureStrategy; import com.google.common.truth.Subject; @@ -44,12 +46,14 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.time.Instant; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -58,8 +62,14 @@ import javax.annotation.Nullable; @RunWith(AndroidJUnit4.class) public class SmartActionHelperTest { + private static final String NOTIFICATION_KEY = "key"; + private static final String RESULT_ID = "id"; - private SmartActionsHelper mSmartActionsHelper = new SmartActionsHelper(); + private static final ConversationActions.ConversationAction REPLY_ACTION = + new ConversationActions.ConversationAction.Builder( + ConversationActions.TYPE_TEXT_REPLY).setTextReply("Home").build(); + + private SmartActionsHelper mSmartActionsHelper; private Context mContext; @Mock private TextClassifier mTextClassifier; @Mock private NotificationEntry mNotificationEntry; @@ -75,7 +85,7 @@ public class SmartActionHelperTest { mContext.getSystemService(TextClassificationManager.class) .setTextClassifier(mTextClassifier); when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class))) - .thenReturn(new ConversationActions(Collections.emptyList(), null)); + .thenReturn(new ConversationActions(Arrays.asList(REPLY_ACTION), RESULT_ID)); when(mNotificationEntry.getSbn()).thenReturn(mStatusBarNotification); // The notification is eligible to have smart suggestions. @@ -83,18 +93,20 @@ public class SmartActionHelperTest { when(mNotificationEntry.isMessaging()).thenReturn(true); when(mStatusBarNotification.getPackageName()).thenReturn("random.app"); when(mStatusBarNotification.getUser()).thenReturn(Process.myUserHandle()); + when(mStatusBarNotification.getKey()).thenReturn(NOTIFICATION_KEY); mNotificationBuilder = new Notification.Builder(mContext, "channel"); mSettings = AssistantSettings.createForTesting( null, null, Process.myUserHandle().getIdentifier(), null); mSettings.mGenerateActions = true; mSettings.mGenerateReplies = true; + mSmartActionsHelper = new SmartActionsHelper(mContext, mSettings); } @Test public void testSuggestReplies_notMessagingApp() { when(mNotificationEntry.isMessaging()).thenReturn(false); ArrayList<CharSequence> textReplies = - mSmartActionsHelper.suggestReplies(mContext, mNotificationEntry, mSettings); + mSmartActionsHelper.suggestReplies(mNotificationEntry); assertThat(textReplies).isEmpty(); } @@ -102,7 +114,7 @@ public class SmartActionHelperTest { public void testSuggestReplies_noInlineReply() { when(mNotificationEntry.hasInlineReply()).thenReturn(false); ArrayList<CharSequence> textReplies = - mSmartActionsHelper.suggestReplies(mContext, mNotificationEntry, mSettings); + mSmartActionsHelper.suggestReplies(mNotificationEntry); assertThat(textReplies).isEmpty(); } @@ -169,18 +181,128 @@ public class SmartActionHelperTest { .build(); when(mNotificationEntry.getNotification()).thenReturn(notification); - mSmartActionsHelper.suggestReplies(mContext, mNotificationEntry, mSettings); + mSmartActionsHelper.suggestReplies(mNotificationEntry); verify(mTextClassifier, never()) .suggestConversationActions(any(ConversationActions.Request.class)); } + @Test + public void testOnSuggestedReplySent() { + final String message = "Where are you?"; + Notification notification = mNotificationBuilder.setContentText(message).build(); + when(mNotificationEntry.getNotification()).thenReturn(notification); + + mSmartActionsHelper.suggestReplies(mNotificationEntry); + mSmartActionsHelper.onSuggestedReplySent( + NOTIFICATION_KEY, message, NotificationAssistantService.SOURCE_FROM_ASSISTANT); + + ArgumentCaptor<TextClassifierEvent> argumentCaptor = + ArgumentCaptor.forClass(TextClassifierEvent.class); + verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture()); + TextClassifierEvent textClassifierEvent = argumentCaptor.getValue(); + assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_SMART_ACTION); + } + + @Test + public void testOnSuggestedReplySent_anotherNotification() { + final String message = "Where are you?"; + Notification notification = mNotificationBuilder.setContentText(message).build(); + when(mNotificationEntry.getNotification()).thenReturn(notification); + + mSmartActionsHelper.suggestReplies(mNotificationEntry); + mSmartActionsHelper.onSuggestedReplySent( + "something_else", message, NotificationAssistantService.SOURCE_FROM_ASSISTANT); + + verify(mTextClassifier, never()) + .onTextClassifierEvent(Mockito.any(TextClassifierEvent.class)); + } + + @Test + public void testOnSuggestedReplySent_missingResultId() { + when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class))) + .thenReturn(new ConversationActions(Collections.emptyList(), null)); + + final String message = "Where are you?"; + Notification notification = mNotificationBuilder.setContentText(message).build(); + when(mNotificationEntry.getNotification()).thenReturn(notification); + + mSmartActionsHelper.suggestReplies(mNotificationEntry); + mSmartActionsHelper.onSuggestedReplySent( + "something_else", message, NotificationAssistantService.SOURCE_FROM_ASSISTANT); + + verify(mTextClassifier, never()) + .onTextClassifierEvent(Mockito.any(TextClassifierEvent.class)); + } + + @Test + public void testOnNotificationDirectReply() { + Notification notification = mNotificationBuilder.setContentText("Where are you?").build(); + when(mNotificationEntry.getNotification()).thenReturn(notification); + + mSmartActionsHelper.suggestReplies(mNotificationEntry); + mSmartActionsHelper.onNotificationDirectReply(NOTIFICATION_KEY); + + ArgumentCaptor<TextClassifierEvent> argumentCaptor = + ArgumentCaptor.forClass(TextClassifierEvent.class); + verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture()); + TextClassifierEvent textClassifierEvent = argumentCaptor.getValue(); + assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_MANUAL_REPLY); + } + + @Test + public void testOnNotificationExpansionChanged() { + final String message = "Where are you?"; + Notification notification = mNotificationBuilder.setContentText(message).build(); + when(mNotificationEntry.getNotification()).thenReturn(notification); + + mSmartActionsHelper.suggestReplies(mNotificationEntry); + mSmartActionsHelper.onNotificationExpansionChanged(mNotificationEntry, true, true); + + ArgumentCaptor<TextClassifierEvent> argumentCaptor = + ArgumentCaptor.forClass(TextClassifierEvent.class); + verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture()); + TextClassifierEvent textClassifierEvent = argumentCaptor.getValue(); + assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_SHOWN); + } + + @Test + public void testOnNotificationsSeen_notExpanded() { + final String message = "Where are you?"; + Notification notification = mNotificationBuilder.setContentText(message).build(); + when(mNotificationEntry.getNotification()).thenReturn(notification); + when(mNotificationEntry.isExpanded()).thenReturn(false); + + mSmartActionsHelper.suggestReplies(mNotificationEntry); + mSmartActionsHelper.onNotificationSeen(mNotificationEntry); + + verify(mTextClassifier, never()).onTextClassifierEvent( + Mockito.any(TextClassifierEvent.class)); + } + + @Test + public void testOnNotificationsSeen_expanded() { + final String message = "Where are you?"; + Notification notification = mNotificationBuilder.setContentText(message).build(); + when(mNotificationEntry.getNotification()).thenReturn(notification); + when(mNotificationEntry.isExpanded()).thenReturn(true); + + mSmartActionsHelper.suggestReplies(mNotificationEntry); + mSmartActionsHelper.onNotificationSeen(mNotificationEntry); + + ArgumentCaptor<TextClassifierEvent> argumentCaptor = + ArgumentCaptor.forClass(TextClassifierEvent.class); + verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture()); + TextClassifierEvent textClassifierEvent = argumentCaptor.getValue(); + assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_SHOWN); + } + private ZonedDateTime createZonedDateTimeFromMsUtc(long msUtc) { return ZonedDateTime.ofInstant(Instant.ofEpochMilli(msUtc), ZoneOffset.systemDefault()); } private List<ConversationActions.Message> getMessagesInRequest() { - mSmartActionsHelper.suggestReplies(mContext, mNotificationEntry, mSettings); + mSmartActionsHelper.suggestReplies(mNotificationEntry); ArgumentCaptor<ConversationActions.Request> argumentCaptor = ArgumentCaptor.forClass(ConversationActions.Request.class); @@ -189,6 +311,17 @@ public class SmartActionHelperTest { return request.getConversation(); } + private void assertTextClassifierEvent( + TextClassifierEvent textClassifierEvent, int expectedEventType) { + assertThat(textClassifierEvent.getEventCategory()) + .isEqualTo(TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS); + assertThat(textClassifierEvent.getEventContext().getPackageName()) + .isEqualTo(InstrumentationRegistry.getTargetContext().getPackageName()); + assertThat(textClassifierEvent.getEventContext().getWidgetType()) + .isEqualTo(TextClassifier.WIDGET_TYPE_NOTIFICATION); + assertThat(textClassifierEvent.getEventType()).isEqualTo(expectedEventType); + } + private static final class MessageSubject extends Subject<MessageSubject, ConversationActions.Message> { diff --git a/packages/ExternalStorageProvider/tests/Android.bp b/packages/ExternalStorageProvider/tests/Android.bp index 83427d4ebf00..04cf01af073a 100644 --- a/packages/ExternalStorageProvider/tests/Android.bp +++ b/packages/ExternalStorageProvider/tests/Android.bp @@ -14,7 +14,7 @@ android_test { ], static_libs: [ - "android-support-test", + "androidx.test.rules", "mockito-target", "truth-prebuilt", ], diff --git a/packages/ExternalStorageProvider/tests/AndroidManifest.xml b/packages/ExternalStorageProvider/tests/AndroidManifest.xml index 58b6e86dfc77..f1a6af08ec4c 100644 --- a/packages/ExternalStorageProvider/tests/AndroidManifest.xml +++ b/packages/ExternalStorageProvider/tests/AndroidManifest.xml @@ -6,7 +6,7 @@ <uses-library android:name="android.test.runner" /> </application> - <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="com.android.externalstorage" android:label="ExternalStorageProvider Tests" /> </manifest> diff --git a/packages/ExternalStorageProvider/tests/AndroidTest.xml b/packages/ExternalStorageProvider/tests/AndroidTest.xml index e5fa73f59836..f8438b22b603 100644 --- a/packages/ExternalStorageProvider/tests/AndroidTest.xml +++ b/packages/ExternalStorageProvider/tests/AndroidTest.xml @@ -23,7 +23,7 @@ <option name="test-tag" value="ExternalStorageProviderTests" /> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="com.android.externalstorage.tests" /> - <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> <option name="hidden-api-checks" value="false"/> </test> </configuration> diff --git a/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java b/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java index a88b3e146ea1..fbf2e4b8ff19 100644 --- a/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java +++ b/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java @@ -23,8 +23,9 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.content.pm.ProviderInfo; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java b/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java index 0312b81493be..28b05396acce 100644 --- a/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java +++ b/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java @@ -39,12 +39,14 @@ import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.ProgressBar; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; + import com.android.hotspot2.R; import java.net.MalformedURLException; import java.net.URL; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; + /** * Online Sign Up Login Web View launched during Provision Process of Hotspot 2.0 rel2. @@ -252,6 +254,10 @@ public class OsuLoginActivity extends Activity { @Override public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { + if (request.getUrl().toString().startsWith("http://127.0.0.1")) { + view.stopLoading(); + } + if (request.isForMainFrame()) { // This happens right after getting HTTP redirect response from an OSU server // since no more Http request is allowed to send to the OSU server. diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java index d400159984a6..305862af505b 100644 --- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java +++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java @@ -165,8 +165,12 @@ public class BarChartPreference extends Preference { private void bindChartDetailsView(PreferenceViewHolder holder) { final Button detailsView = (Button) holder.findViewById(R.id.bar_chart_details); - detailsView.setText(mDetailsId); - detailsView.setOnClickListener(mDetailsOnClickListener); + if (mDetailsId == 0) { + detailsView.setVisibility(View.GONE); + } else { + detailsView.setText(mDetailsId); + detailsView.setOnClickListener(mDetailsOnClickListener); + } } private void updateBarChart(PreferenceViewHolder holder) { diff --git a/packages/SettingsLib/tests/integ/Android.mk b/packages/SettingsLib/tests/integ/Android.mk index c893b6dcffbf..4a814df1dfff 100644 --- a/packages/SettingsLib/tests/integ/Android.mk +++ b/packages/SettingsLib/tests/integ/Android.mk @@ -31,8 +31,8 @@ LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_USE_AAPT2 := true LOCAL_STATIC_JAVA_LIBRARIES := \ - android-support-test \ - espresso-core \ + androidx.test.rules \ + androidx.test.espresso.core \ mockito-target-minus-junit4 \ truth-prebuilt diff --git a/packages/SettingsLib/tests/integ/AndroidManifest.xml b/packages/SettingsLib/tests/integ/AndroidManifest.xml index e8e0b41ffd41..da808dd54141 100644 --- a/packages/SettingsLib/tests/integ/AndroidManifest.xml +++ b/packages/SettingsLib/tests/integ/AndroidManifest.xml @@ -31,7 +31,7 @@ <activity android:name=".drawer.SettingsDrawerActivityTest$TestActivity"/> </application> - <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="com.android.settingslib" android:label="Tests for SettingsLib"> </instrumentation> diff --git a/packages/SettingsLib/tests/integ/AndroidTest.xml b/packages/SettingsLib/tests/integ/AndroidTest.xml index d7ee618207a8..b5d09475269e 100644 --- a/packages/SettingsLib/tests/integ/AndroidTest.xml +++ b/packages/SettingsLib/tests/integ/AndroidTest.xml @@ -22,7 +22,7 @@ <option name="test-tag" value="SettingsLibTests" /> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="com.android.settingslib" /> - <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> <option name="hidden-api-checks" value="false"/> </test> </configuration> diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/bluetooth/BluetoothEventManagerIntegTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/bluetooth/BluetoothEventManagerIntegTest.java index d0ab46a2f30d..50f5b9d81000 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/bluetooth/BluetoothEventManagerIntegTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/bluetooth/BluetoothEventManagerIntegTest.java @@ -16,15 +16,10 @@ package com.android.settingslib.bluetooth; -import static com.google.common.truth.Truth.assertThat; - -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; -import static java.util.concurrent.TimeUnit.SECONDS; - import android.app.ActivityManager; import android.content.Context; import android.content.Intent; @@ -32,15 +27,18 @@ import android.content.pm.UserInfo; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.LargeTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import static java.util.concurrent.TimeUnit.SECONDS; + import java.util.concurrent.CountDownLatch; /** diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawable/UserIconDrawableTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawable/UserIconDrawableTest.java index 3fa2ce5f8312..a436cb515bb8 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawable/UserIconDrawableTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawable/UserIconDrawableTest.java @@ -27,8 +27,9 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.PorterDuff; import android.graphics.PorterDuff.Mode; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; import com.android.settingslib.R; diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java index dddfa7a3a9b4..08484bceb1fb 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java @@ -1,26 +1,29 @@ package com.android.settingslib.graph; +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyFloat; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Rect; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import com.android.settingslib.R; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import static com.google.common.truth.Truth.assertThat; -import static junit.framework.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyFloat; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; - @SmallTest @RunWith(AndroidJUnit4.class) public class BatteryMeterDrawableBaseTest { diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java index 93b038e07b51..9962e1ca438a 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java @@ -20,12 +20,13 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodSubtypePreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodSubtypePreferenceTest.java index e591d8cac9f2..f1c0beae0dc0 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodSubtypePreferenceTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodSubtypePreferenceTest.java @@ -16,11 +16,12 @@ package com.android.settingslib.inputmethod; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.text.TextUtils; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; import org.junit.runner.RunWith; diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java index 54510b27e58d..46557d3106af 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java @@ -18,7 +18,6 @@ package com.android.settingslib.users; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -33,8 +32,9 @@ import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.UserHandle; import android.os.UserManager; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java index ee03d5024351..37f2600e9812 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java @@ -16,19 +16,22 @@ package com.android.settingslib.utils; +import static junit.framework.Assert.assertEquals; + import android.net.NetworkPolicy; import android.net.NetworkPolicyManager; import android.net.NetworkTemplate; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import com.android.settingslib.NetworkPolicyEditor; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import static junit.framework.Assert.assertEquals; - @RunWith(AndroidJUnit4.class) @SmallTest public class NetworkPolicyEditorTest { diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/ZoneGetterTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/ZoneGetterTest.java index 0ec75ecee066..ff8dbda360fc 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/ZoneGetterTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/ZoneGetterTest.java @@ -15,21 +15,23 @@ */ package com.android.settingslib.utils; +import static junit.framework.Assert.assertTrue; + import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; -import android.support.test.filters.SmallTest; import android.text.Spanned; import android.text.style.TtsSpan; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.settingslib.datetime.ZoneGetter; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.*; -import com.android.settingslib.datetime.ZoneGetter; - -import static junit.framework.Assert.assertTrue; @RunWith(AndroidJUnit4.class) @SmallTest diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java index 032479990cae..fc3034efc08b 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java @@ -22,7 +22,6 @@ import static org.junit.Assert.fail; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -44,12 +43,12 @@ import android.net.wifi.hotspot2.pps.HomeSp; import android.os.Bundle; import android.os.Parcelable; import android.os.SystemClock; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.text.SpannableString; import android.text.format.DateUtils; -import android.text.style.TtsSpan; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.android.settingslib.R; import com.android.settingslib.utils.ThreadUtils; diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java index 1860b31081e1..42eb0b940938 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java @@ -53,9 +53,10 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.SystemClock; import android.provider.Settings; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.android.settingslib.utils.ThreadUtils; @@ -79,7 +80,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; // TODO(sghuman): Change these to robolectric tests b/35766684. - @SmallTest @RunWith(AndroidJUnit4.class) public class WifiTrackerTest { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java index d4e74810ea3f..375b45cf46d0 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java @@ -41,6 +41,7 @@ public class BarChartPreferenceTest { private BarView mBarView2; private BarView mBarView3; private BarView mBarView4; + private TextView mDetailsView; private PreferenceViewHolder mHolder; private BarChartPreference mPreference; @@ -51,14 +52,13 @@ public class BarChartPreferenceTest { mHolder = PreferenceViewHolder.createInstanceForTests(mBarChartView); mPreference = new BarChartPreference(mContext, null /* attrs */); mPreference.setBarChartTitle(R.string.debug_app); - mPreference.setBarChartDetails(R.string.debug_app); - mIcon = mContext.getDrawable(R.drawable.ic_menu); mBarView1 = (BarView) mBarChartView.findViewById(R.id.bar_view1); mBarView2 = (BarView) mBarChartView.findViewById(R.id.bar_view2); mBarView3 = (BarView) mBarChartView.findViewById(R.id.bar_view3); mBarView4 = (BarView) mBarChartView.findViewById(R.id.bar_view4); + mDetailsView = (TextView) mBarChartView.findViewById(R.id.bar_chart_details); } @Test @@ -73,26 +73,31 @@ public class BarChartPreferenceTest { } @Test - public void setBarChartDetailsRes_setDetailsRes_showInBarChartDetails() { - final TextView detailsView = (TextView) mBarChartView.findViewById(R.id.bar_chart_details); + public void onBindViewHolder_notSetDetailsRes_barChartDetailsViewIsGone() { + // We don't call BarChartPreference#setBarChartDetails + mPreference.onBindViewHolder(mHolder); + + assertThat(mDetailsView.getVisibility()).isEqualTo(View.GONE); + } + @Test + public void setBarChartDetailsRes_setDetailsRes_showInBarChartDetails() { mPreference.setBarChartDetails(R.string.debug_app); mPreference.onBindViewHolder(mHolder); - assertThat(detailsView.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(detailsView.getText()).isEqualTo(mContext.getText(R.string.debug_app)); + assertThat(mDetailsView.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mDetailsView.getText()).isEqualTo(mContext.getText(R.string.debug_app)); } @Test public void setBarChartDetailsClickListener_setClickListener_detailsViewAttachClickListener() { - final TextView detailsView = (TextView) mBarChartView.findViewById(R.id.bar_chart_details); - + mPreference.setBarChartDetails(R.string.debug_app); mPreference.setBarChartDetailsClickListener(v -> { }); mPreference.onBindViewHolder(mHolder); - assertThat(detailsView.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(detailsView.hasOnClickListeners()).isTrue(); + assertThat(mDetailsView.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mDetailsView.hasOnClickListeners()).isTrue(); } @Test diff --git a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java index 8e8d9f741a81..abe82a885a94 100644 --- a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java +++ b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java @@ -18,7 +18,7 @@ package com.android.simappdialog; import android.app.Activity; import android.content.Intent; import android.os.Bundle; -import android.os.SystemProperties; +import android.sysprop.SetupWizardProperties; import android.text.TextUtils; import android.view.View; import android.widget.Button; @@ -51,7 +51,7 @@ public class InstallCarrierAppActivity extends Activity implements View.OnClickL // Setup theme for aosp/pixel setTheme( WizardManagerHelper.getThemeRes( - SystemProperties.get("setupwizard.theme"), + SetupWizardProperties.theme().orElse(""), R.style.SuwThemeGlif_Light ) ); diff --git a/packages/SystemUI/docs/dagger.md b/packages/SystemUI/docs/dagger.md index f81e8cce1f91..7751bb10c5ce 100644 --- a/packages/SystemUI/docs/dagger.md +++ b/packages/SystemUI/docs/dagger.md @@ -36,7 +36,8 @@ public interface SystemUIRootComponent { The root modules are what provides the global singleton dependencies across SystemUI. ContextHolder is just a wrapper that provides a context. SystemUIFactory @Provide dependencies that need to be overridden by SystemUI -variants (like other form factors). DependencyProvider provides or binds any +variants (like other form factors). DependencyBinder creates the mapping from +interfaces to implementation classes. DependencyProvider provides or binds any remaining depedencies required. ### Adding injection to a new SystemUI object @@ -195,10 +196,5 @@ public CustomView(@Named(VIEW_CONTEXT) Context themedViewContext, AttributeSet a ## TODO List - - Eliminate usages of Depndency#get - - Add support for Fragments to handle injection automatically - - (this could be through dagger2-android or something custom) - - Reduce number of things with @Provide in DependencyProvider (many can be - @Inject instead) - - Migrate as many remaining DependencyProvider instances to @Bind + - Eliminate usages of Dependency#get - Add links in above TODO 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 6135aebb0587..ac6904300f71 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java @@ -36,6 +36,13 @@ public interface ClockPlugin extends Plugin { View getView(); /** + * Get clock view for a large clock that appears behind NSSL. + */ + default View getBigClockView() { + return null; + } + + /** * Set clock paint style. * @param style The new style to set in the paint. */ diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml index ed18dc728402..4e0cbe093c49 100644 --- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml +++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml @@ -24,13 +24,14 @@ android:layout_gravity="@integer/notification_panel_layout_gravity" android:background="@android:color/transparent" android:baselineAligned="false" - android:clickable="false" + android:clickable="true" android:clipChildren="false" android:clipToPadding="false" android:paddingTop="0dp" android:paddingEnd="0dp" android:paddingStart="0dp" - android:elevation="4dp" > + android:elevation="4dp" + android:importantForAccessibility="no" > <include layout="@layout/quick_status_bar_header_system_icons" /> diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index 2674f07c21e4..75c0ec3ced43 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -25,6 +25,12 @@ android:layout_height="match_parent" android:background="@android:color/transparent" > + <FrameLayout + android:id="@+id/big_clock_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:visibility="gone" /> + <include layout="@layout/keyguard_status_view" android:visibility="gone" /> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 07375ad333ed..c94438fa74f1 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2330,4 +2330,7 @@ </plurals> <!-- Text for the quick setting tile for sensor privacy [CHAR LIMIT=30] --> <string name="sensor_privacy_mode">Sensors off</string> + + <!-- Name for device services grouping system uid apps in Ongoing Privacy Dialog [CHAR_LIMIT=NONE] --> + <string name="device_services">Device Services</string> </resources> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java new file mode 100644 index 000000000000..c0a1d891cd5b --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.shared.system; + +import android.graphics.Matrix; +import android.graphics.Rect; +import android.view.Surface; +import android.view.SurfaceControl; +import android.view.SurfaceControl.Transaction; +import android.view.SyncRtSurfaceTransactionApplier; +import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; +import android.view.ThreadedRenderer; +import android.view.View; +import android.view.ViewRootImpl; + +import java.util.function.Consumer; + +/** + * Helper class to apply surface transactions in sync with RenderThread. + */ +public class SyncRtSurfaceTransactionApplierCompat { + + private final SyncRtSurfaceTransactionApplier mApplier; + + /** + * @param targetView The view in the surface that acts as synchronization anchor. + */ + public SyncRtSurfaceTransactionApplierCompat(View targetView) { + mApplier = new SyncRtSurfaceTransactionApplier(targetView); + } + + private SyncRtSurfaceTransactionApplierCompat(SyncRtSurfaceTransactionApplier applier) { + mApplier = applier; + } + + /** + * Schedules applying surface parameters on the next frame. + * + * @param params The surface parameters to apply. DO NOT MODIFY the list after passing into + * this method to avoid synchronization issues. + */ + public void scheduleApply(final SurfaceParams... params) { + mApplier.scheduleApply(convert(params)); + } + + private SyncRtSurfaceTransactionApplier.SurfaceParams[] convert(SurfaceParams[] params) { + SyncRtSurfaceTransactionApplier.SurfaceParams[] result = + new SyncRtSurfaceTransactionApplier.SurfaceParams[params.length]; + for (int i = 0; i < params.length; i++) { + result[i] = params[i].mParams; + } + return result; + } + + public static void applyParams(TransactionCompat t, SurfaceParams params) { + SyncRtSurfaceTransactionApplier.applyParams(t.mTransaction, params.mParams, t.mTmpValues); + } + + public static void create(final View targetView, + final Consumer<SyncRtSurfaceTransactionApplierCompat> callback) { + SyncRtSurfaceTransactionApplier.create(targetView, + new Consumer<SyncRtSurfaceTransactionApplier>() { + @Override + public void accept(SyncRtSurfaceTransactionApplier applier) { + callback.accept(new SyncRtSurfaceTransactionApplierCompat(applier)); + } + }); + } + + public static class SurfaceParams { + + private final SyncRtSurfaceTransactionApplier.SurfaceParams mParams; + + /** + * Constructs surface parameters to be applied when the current view state gets pushed to + * RenderThread. + * + * @param surface The surface to modify. + * @param alpha Alpha to apply. + * @param matrix Matrix to apply. + * @param windowCrop Crop to apply. + */ + public SurfaceParams(SurfaceControlCompat surface, float alpha, Matrix matrix, + Rect windowCrop, int layer, float cornerRadius) { + mParams = new SyncRtSurfaceTransactionApplier.SurfaceParams(surface.mSurfaceControl, + alpha, matrix, windowCrop, layer, cornerRadius); + } + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 22a23a8f8a21..570d351a8b71 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -35,7 +35,11 @@ public class KeyguardClockSwitch extends RelativeLayout { /** * Frame for default and custom clock. */ - private FrameLayout mClockFrame; + private FrameLayout mSmallClockFrame; + /** + * Container for big custom clock. + */ + private ViewGroup mBigClockContainer; /** * Status area (date and other stuff) shown below the clock. Plugin can decide whether * or not to show it below the alternate clock. @@ -46,22 +50,27 @@ public class KeyguardClockSwitch extends RelativeLayout { new PluginListener<ClockPlugin>() { @Override public void onPluginConnected(ClockPlugin plugin, Context pluginContext) { - View view = plugin.getView(); - if (view != null) { - disconnectPlugin(); + disconnectPlugin(); + View smallClockView = plugin.getView(); + if (smallClockView != null) { // For now, assume that the most recently connected plugin is the // selected clock face. In the future, the user should be able to // pick a clock face from the available plugins. - mClockPlugin = plugin; - mClockFrame.addView(view, -1, + mSmallClockFrame.addView(smallClockView, -1, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); initPluginParams(); mClockView.setVisibility(View.GONE); - if (!plugin.shouldShowStatusArea()) { - mKeyguardStatusArea.setVisibility(View.GONE); - } } + View bigClockView = plugin.getBigClockView(); + if (bigClockView != null && mBigClockContainer != null) { + mBigClockContainer.addView(bigClockView); + mBigClockContainer.setVisibility(View.VISIBLE); + } + if (!plugin.shouldShowStatusArea()) { + mKeyguardStatusArea.setVisibility(View.GONE); + } + mClockPlugin = plugin; } @Override @@ -86,7 +95,7 @@ public class KeyguardClockSwitch extends RelativeLayout { protected void onFinishInflate() { super.onFinishInflate(); mClockView = findViewById(R.id.default_clock_view); - mClockFrame = findViewById(R.id.clock_view); + mSmallClockFrame = findViewById(R.id.clock_view); mKeyguardStatusArea = findViewById(R.id.keyguard_status_area); } @@ -104,6 +113,20 @@ public class KeyguardClockSwitch extends RelativeLayout { } /** + * Set container for big clock face appearing behind NSSL and KeyguardStatusView. + */ + public void setBigClockContainer(ViewGroup container) { + if (mClockPlugin != null && container != null) { + View bigClockView = mClockPlugin.getBigClockView(); + if (bigClockView != null) { + container.addView(bigClockView); + container.setVisibility(View.VISIBLE); + } + } + mBigClockContainer = container; + } + + /** * It will also update plugin setStyle if plugin is connected. */ public void setStyle(Style style) { @@ -199,9 +222,13 @@ public class KeyguardClockSwitch extends RelativeLayout { private void disconnectPlugin() { if (mClockPlugin != null) { - View view = mClockPlugin.getView(); - if (view != null) { - mClockFrame.removeView(view); + View smallClockView = mClockPlugin.getView(); + if (smallClockView != null) { + mSmallClockFrame.removeView(smallClockView); + } + if (mBigClockContainer != null) { + mBigClockContainer.removeAllViews(); + mBigClockContainer.setVisibility(View.GONE); } mClockPlugin = null; } diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java index 9e7c5ba1d66c..b461f69f10d6 100644 --- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java @@ -19,14 +19,22 @@ import android.content.Intent; import com.android.systemui.plugins.ActivityStarter; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Single common instance of ActivityStarter that can be gotten and referenced from anywhere, but * delegates to an actual implementation such as StatusBar, assuming it exists. */ +@Singleton public class ActivityStarterDelegate implements ActivityStarter { private ActivityStarter mActualStarter; + @Inject + public ActivityStarterDelegate() { + } + @Override public void startPendingIntentDismissingKeyguard(PendingIntent intent) { if (mActualStarter == null) { diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 6b4d07a44d34..443e389b1d02 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -220,6 +220,7 @@ public class Dependency extends SystemUI { @Inject Lazy<FragmentService> mFragmentService; @Inject Lazy<ExtensionController> mExtensionController; @Inject Lazy<PluginDependencyProvider> mPluginDependencyProvider; + @Nullable @Inject Lazy<LocalBluetoothManager> mLocalBluetoothManager; @Inject Lazy<VolumeDialogController> mVolumeDialogController; @Inject Lazy<MetricsLogger> mMetricsLogger; diff --git a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/DependencyBinder.java new file mode 100644 index 000000000000..23ef0307feb1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/DependencyBinder.java @@ -0,0 +1,227 @@ +/* + * 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; + +import com.android.systemui.appops.AppOpsController; +import com.android.systemui.appops.AppOpsControllerImpl; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.VolumeDialogController; +import com.android.systemui.power.PowerNotificationWarnings; +import com.android.systemui.power.PowerUI; +import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl; +import com.android.systemui.statusbar.phone.ManagedProfileController; +import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl; +import com.android.systemui.statusbar.phone.StatusBarIconController; +import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl; +import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback; +import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.BatteryControllerImpl; +import com.android.systemui.statusbar.policy.BluetoothController; +import com.android.systemui.statusbar.policy.BluetoothControllerImpl; +import com.android.systemui.statusbar.policy.CastController; +import com.android.systemui.statusbar.policy.CastControllerImpl; +import com.android.systemui.statusbar.policy.DarkIconDispatcher; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; +import com.android.systemui.statusbar.policy.ExtensionController; +import com.android.systemui.statusbar.policy.ExtensionControllerImpl; +import com.android.systemui.statusbar.policy.FlashlightController; +import com.android.systemui.statusbar.policy.FlashlightControllerImpl; +import com.android.systemui.statusbar.policy.HotspotController; +import com.android.systemui.statusbar.policy.HotspotControllerImpl; +import com.android.systemui.statusbar.policy.IconLogger; +import com.android.systemui.statusbar.policy.IconLoggerImpl; +import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.KeyguardMonitorImpl; +import com.android.systemui.statusbar.policy.LocationController; +import com.android.systemui.statusbar.policy.LocationControllerImpl; +import com.android.systemui.statusbar.policy.NetworkController; +import com.android.systemui.statusbar.policy.NetworkControllerImpl; +import com.android.systemui.statusbar.policy.NextAlarmController; +import com.android.systemui.statusbar.policy.NextAlarmControllerImpl; +import com.android.systemui.statusbar.policy.RotationLockController; +import com.android.systemui.statusbar.policy.RotationLockControllerImpl; +import com.android.systemui.statusbar.policy.SecurityController; +import com.android.systemui.statusbar.policy.SecurityControllerImpl; +import com.android.systemui.statusbar.policy.UserInfoController; +import com.android.systemui.statusbar.policy.UserInfoControllerImpl; +import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.statusbar.policy.ZenModeControllerImpl; +import com.android.systemui.tuner.TunerService; +import com.android.systemui.tuner.TunerServiceImpl; +import com.android.systemui.volume.VolumeDialogControllerImpl; + +import dagger.Binds; +import dagger.Module; + +/** + * Maps interfaces to implementations for use with Dagger. + */ +@Module +public abstract class DependencyBinder { + + /** + */ + @Binds + public abstract ActivityStarter provideActivityStarter(ActivityStarterDelegate delegate); + + /** + */ + @Binds + public abstract BluetoothController provideBluetoothController( + BluetoothControllerImpl controllerImpl); + + /** + */ + @Binds + public abstract LocationController provideLocationController( + LocationControllerImpl controllerImpl); + + /** + */ + @Binds + public abstract RotationLockController provideRotationLockController( + RotationLockControllerImpl controllerImpl); + + /** + */ + @Binds + public abstract NetworkController provideNetworkController( + NetworkControllerImpl controllerImpl); + + /** + */ + @Binds + public abstract ZenModeController provideZenModeController( + ZenModeControllerImpl controllerImpl); + + /** + */ + @Binds + public abstract HotspotController provideHotspotController( + HotspotControllerImpl controllerImpl); + + /** + */ + @Binds + public abstract AppOpsController provideAppOpsController( + AppOpsControllerImpl controllerImpl); + + /** + */ + @Binds + public abstract NotificationRemoteInputManager.Callback provideNotificationRemoteInputManager( + StatusBarRemoteInputCallback callbackImpl); + + /** + */ + @Binds + public abstract IconLogger provideIconLogger(IconLoggerImpl loggerImpl); + + /** + */ + @Binds + public abstract CastController provideCastController(CastControllerImpl controllerImpl); + + /** + */ + @Binds + public abstract FlashlightController provideFlashlightController( + FlashlightControllerImpl controllerImpl); + + /** + */ + @Binds + public abstract KeyguardMonitor provideKeyguardMonitor(KeyguardMonitorImpl controllerImpl); + + /** + */ + @Binds + public abstract UserInfoController provideUserInfoContrller( + UserInfoControllerImpl controllerImpl); + + /** + */ + @Binds + public abstract BatteryController provideBatteryController( + BatteryControllerImpl controllerImpl); + + /** + */ + @Binds + public abstract ManagedProfileController provideManagedProfileController( + ManagedProfileControllerImpl controllerImpl); + + /** + */ + @Binds + public abstract NextAlarmController provideNextAlarmController( + NextAlarmControllerImpl controllerImpl); + + /** + */ + @Binds + public abstract DeviceProvisionedController provideDeviceProvisionedController( + DeviceProvisionedControllerImpl controllerImpl); + + /** + */ + @Binds + public abstract SecurityController provideSecurityController( + SecurityControllerImpl controllerImpl); + + /** + */ + @Binds + public abstract TunerService provideTunerService(TunerServiceImpl controllerImpl); + + /** + */ + @Binds + public abstract DarkIconDispatcher provideDarkIconDispatcher( + DarkIconDispatcherImpl controllerImpl); + + /** + */ + @Binds + public abstract StatusBarIconController provideStatusBarIconController( + StatusBarIconControllerImpl controllerImpl); + + /** + */ + @Binds + public abstract ExtensionController provideExtensionController( + ExtensionControllerImpl controllerImpl); + + /** + */ + @Binds + public abstract VolumeDialogController provideVolumeDialogController( + VolumeDialogControllerImpl controllerImpl); + + /** + */ + @Binds + public abstract ForegroundServiceController provideForegroundService( + ForegroundServiceControllerImpl controllerImpl); + + /** + */ + @Binds + public abstract PowerUI.WarningsUI provideWarningsUi(PowerNotificationWarnings controllerImpl); +} diff --git a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java index b11e189e3a9c..2b521c53a1e6 100644 --- a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java @@ -18,13 +18,11 @@ package com.android.systemui; import static com.android.systemui.Dependency.BG_HANDLER_NAME; import static com.android.systemui.Dependency.BG_LOOPER_NAME; -import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME; import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME; import android.annotation.Nullable; import android.content.Context; -import android.hardware.SensorManager; import android.hardware.SensorPrivacyManager; import android.os.Handler; import android.os.HandlerThread; @@ -40,82 +38,16 @@ import com.android.internal.app.ColorDisplayController; import com.android.internal.logging.MetricsLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.settingslib.bluetooth.LocalBluetoothManager; -import com.android.systemui.appops.AppOpsController; -import com.android.systemui.appops.AppOpsControllerImpl; -import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.keyguard.ScreenLifecycle; -import com.android.systemui.keyguard.WakefulnessLifecycle; -import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.plugins.PluginDependencyProvider; import com.android.systemui.plugins.PluginInitializerImpl; -import com.android.systemui.plugins.VolumeDialogController; -import com.android.systemui.power.PowerNotificationWarnings; -import com.android.systemui.power.PowerUI; -import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.shared.plugins.PluginManagerImpl; -import com.android.systemui.statusbar.DisplayNavigationBarController; -import com.android.systemui.statusbar.NotificationRemoteInputManager; -import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; -import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl; -import com.android.systemui.statusbar.phone.LightBarController; -import com.android.systemui.statusbar.phone.LockscreenGestureLogger; -import com.android.systemui.statusbar.phone.ManagedProfileController; -import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; -import com.android.systemui.statusbar.phone.StatusBarIconController; -import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl; -import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback; -import com.android.systemui.statusbar.phone.StatusBarWindowController; -import com.android.systemui.statusbar.policy.AccessibilityController; -import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; -import com.android.systemui.statusbar.policy.BatteryController; -import com.android.systemui.statusbar.policy.BatteryControllerImpl; -import com.android.systemui.statusbar.policy.BluetoothController; -import com.android.systemui.statusbar.policy.BluetoothControllerImpl; -import com.android.systemui.statusbar.policy.CastController; -import com.android.systemui.statusbar.policy.CastControllerImpl; import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.DarkIconDispatcher; import com.android.systemui.statusbar.policy.DataSaverController; -import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; -import com.android.systemui.statusbar.policy.ExtensionController; -import com.android.systemui.statusbar.policy.ExtensionControllerImpl; -import com.android.systemui.statusbar.policy.FlashlightController; -import com.android.systemui.statusbar.policy.FlashlightControllerImpl; -import com.android.systemui.statusbar.policy.HotspotController; -import com.android.systemui.statusbar.policy.HotspotControllerImpl; -import com.android.systemui.statusbar.policy.IconLogger; -import com.android.systemui.statusbar.policy.IconLoggerImpl; -import com.android.systemui.statusbar.policy.KeyguardMonitor; -import com.android.systemui.statusbar.policy.KeyguardMonitorImpl; -import com.android.systemui.statusbar.policy.LocationController; -import com.android.systemui.statusbar.policy.LocationControllerImpl; import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.NetworkControllerImpl; -import com.android.systemui.statusbar.policy.NextAlarmController; -import com.android.systemui.statusbar.policy.NextAlarmControllerImpl; -import com.android.systemui.statusbar.policy.RotationLockController; -import com.android.systemui.statusbar.policy.RotationLockControllerImpl; -import com.android.systemui.statusbar.policy.SecurityController; -import com.android.systemui.statusbar.policy.SecurityControllerImpl; -import com.android.systemui.statusbar.policy.UserInfoController; -import com.android.systemui.statusbar.policy.UserInfoControllerImpl; -import com.android.systemui.statusbar.policy.UserSwitcherController; -import com.android.systemui.statusbar.policy.ZenModeController; -import com.android.systemui.statusbar.policy.ZenModeControllerImpl; -import com.android.systemui.tuner.TunablePadding; -import com.android.systemui.tuner.TunablePadding.TunablePaddingService; -import com.android.systemui.tuner.TunerService; -import com.android.systemui.tuner.TunerServiceImpl; -import com.android.systemui.util.AsyncSensorManager; -import com.android.systemui.util.leak.GarbageMonitor; import com.android.systemui.util.leak.LeakDetector; -import com.android.systemui.util.leak.LeakReporter; -import com.android.systemui.volume.VolumeDialogControllerImpl; import javax.inject.Named; import javax.inject.Singleton; @@ -165,256 +97,13 @@ public class DependencyProvider { @Singleton @Provides - public ActivityStarter provideActivityStarter() { - return new ActivityStarterDelegate(); - } - - @Singleton - @Provides - public InitController provideInitController() { - return new InitController(); - } - - @Singleton - @Provides - public ActivityStarterDelegate provideActivityStarterDelegate(ActivityStarter starter) { - return (ActivityStarterDelegate) starter; - } - - @Singleton - @Provides - public AsyncSensorManager provideAsyncSensorManager(Context context, PluginManager manager) { - return new AsyncSensorManager(context.getSystemService(SensorManager.class), - manager); - - } - - @Singleton - @Provides - public BluetoothController provideBluetoothController(Context context, - @Named(BG_LOOPER_NAME) Looper looper) { - return new BluetoothControllerImpl(context, looper); - - } - - @Singleton - @Provides - public LocationController provideLocationController(Context context, - @Named(BG_LOOPER_NAME) Looper bgLooper) { - return new LocationControllerImpl(context, bgLooper); - - } - - @Singleton - @Provides - public RotationLockController provideRotationLockController(Context context) { - return new RotationLockControllerImpl(context); - - } - - @Singleton - @Provides - public NetworkController provideNetworkController(Context context, - @Named(BG_LOOPER_NAME) Looper bgLooper, DeviceProvisionedController controller) { - return new NetworkControllerImpl(context, bgLooper, - controller); - - } - - @Singleton - @Provides - public ZenModeController provideZenModeController(Context context, - @Named(MAIN_HANDLER_NAME) Handler mainHandler) { - return new ZenModeControllerImpl(context, mainHandler); - - } - - @Singleton - @Provides - public HotspotController provideHotspotController(Context context) { - return new HotspotControllerImpl(context); - - } - - @Singleton - @Provides - public CastController provideCastController(Context context) { - return new CastControllerImpl(context); - - } - - @Singleton - @Provides - public FlashlightController provideFlashlightController(Context context) { - return new FlashlightControllerImpl(context); - - } - - @Singleton - @Provides - public KeyguardMonitor provideKeyguardMonitor(Context context) { - return new KeyguardMonitorImpl(context); - - } - - @Singleton - @Provides - public UserSwitcherController provideUserSwitcherController(Context context, - KeyguardMonitor keyguardMonitor, @Named(MAIN_HANDLER_NAME) Handler mainHandler, - ActivityStarter activityStarter) { - return new UserSwitcherController(context, keyguardMonitor, mainHandler, activityStarter); - } - - @Singleton - @Provides - public UserInfoController provideUserInfoContrller(Context context) { - return new UserInfoControllerImpl(context); - - } - - @Singleton - @Provides - public BatteryController provideBatteryController(Context context) { - return new BatteryControllerImpl(context); - - } - - @Singleton - @Provides - public ColorDisplayController provideColorDisplayController(Context context) { - return new ColorDisplayController(context); - - } - - @Singleton - @Provides - public ManagedProfileController provideManagedProfileController(Context context) { - return new ManagedProfileControllerImpl(context); - - } - - @Singleton - @Provides - public NextAlarmController provideNextAlarmController(Context context) { - return new NextAlarmControllerImpl(context); - - } - - @Singleton - @Provides public DataSaverController provideDataSaverController(NetworkController networkController) { return networkController.getDataSaverController(); } @Singleton @Provides - public AccessibilityController provideAccessibilityController(Context context) { - return new AccessibilityController(context); - - } - - @Singleton - @Provides - public DeviceProvisionedController provideDeviceProvisionedController(Context context) { - return new DeviceProvisionedControllerImpl(context); - - } - - @Singleton - @Provides - public PluginManager providePluginManager(Context context) { - return new PluginManagerImpl(context, new PluginInitializerImpl()); - - } - - @Singleton - @Provides - public SecurityController provideSecurityController(Context context) { - return new SecurityControllerImpl(context); - - } - - @Singleton - @Provides - public LeakDetector provideLeakDetector() { - return LeakDetector.create(); - - } - - @Singleton - @Provides - public LeakReporter provideLeakReporter(Context context, LeakDetector detector, - @Nullable @Named(LEAK_REPORT_EMAIL_NAME) String email) { - return new LeakReporter(context, detector, email); - } - - @Singleton - @Provides - public GarbageMonitor provideGarbageMonitor(Context context, - @Named(BG_LOOPER_NAME) Looper bgLooper, LeakDetector detector, LeakReporter reporter) { - return new GarbageMonitor(context, bgLooper, detector, reporter); - } - - @Singleton - @Provides - public TunerService provideTunerService(Context context) { - return new TunerServiceImpl(context); - - } - - @Singleton - @Provides - public StatusBarWindowController provideStatusBarWindowController(Context context) { - return new StatusBarWindowController(context); - - } - - @Singleton - @Provides - public DarkIconDispatcher provideDarkIconDispatcher(Context context) { - return new DarkIconDispatcherImpl(context); - } - - @Singleton - @Provides - public ConfigurationController provideConfigurationController(Context context) { - return new ConfigurationControllerImpl(context); - - } - - @Singleton - @Provides - public StatusBarIconController provideStatusBarIconController(Context context) { - return new StatusBarIconControllerImpl(context); - - } - - @Singleton - @Provides - public ScreenLifecycle provideScreenLifecycle() { - return new ScreenLifecycle(); - } - - @Singleton - @Provides - public WakefulnessLifecycle provideWakefulnessLifecycle() { - return new WakefulnessLifecycle(); - } - - @Singleton - @Provides - public ExtensionController provideExtensionController(Context context) { - return new ExtensionControllerImpl(context); - } - - @Singleton - @Provides - public PluginDependencyProvider providePluginDependency(PluginManager pluginManager) { - return new PluginDependencyProvider(pluginManager); - } - - @Singleton - @Provides + @Nullable public LocalBluetoothManager provideLocalBluetoothController(Context context, @Named(BG_HANDLER_NAME) Handler bgHandler) { return LocalBluetoothManager.create(context, bgHandler, @@ -423,72 +112,8 @@ public class DependencyProvider { @Singleton @Provides - public VolumeDialogController provideVolumeDialogController(Context context) { - return new VolumeDialogControllerImpl(context); - - } - - @Singleton - @Provides public MetricsLogger provideMetricsLogger() { return new MetricsLogger(); - - } - - @Singleton - @Provides - public AccessibilityManagerWrapper provideAccessibilityManagerWrapper(Context context) { - return new AccessibilityManagerWrapper(context); - - } - - @Singleton - @Provides - // Creating a new instance will trigger color extraction. - // Thankfully this only happens once - during boot - and WallpaperManagerService - // loads colors from cache. - public SysuiColorExtractor provideSysuiColorExtractor(Context context) { - return new SysuiColorExtractor(context); - - } - - @Singleton - @Provides - public TunablePadding.TunablePaddingService provideTunablePaddingService() { - return new TunablePaddingService(); - - } - - @Singleton - @Provides - public ForegroundServiceController provideForegroundService(Context context) { - return new ForegroundServiceControllerImpl(context); - - } - - @Singleton - @Provides - public UiOffloadThread provideUiOffloadThread() { - return new UiOffloadThread(); - } - - @Singleton - @Provides - public PowerUI.WarningsUI provideWarningsUi(Context context) { - return new PowerNotificationWarnings(context); - } - - @Singleton - @Provides - public IconLogger provideIconLogger(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper, - MetricsLogger logger) { - return new IconLoggerImpl(context, bgLooper, logger); - } - - @Singleton - @Provides - public LightBarController provideLightBarController(Context context) { - return new LightBarController(context); } @Singleton @@ -499,19 +124,6 @@ public class DependencyProvider { @Singleton @Provides - public OverviewProxyService provideOverviewProxyService(Context context) { - return new OverviewProxyService(context); - } - - @Singleton - @Provides - public VibratorHelper provideVibratorHelper(Context context) { - return new VibratorHelper(context); - - } - - @Singleton - @Provides public IStatusBarService provideIStatusBarService() { return IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); @@ -520,50 +132,45 @@ public class DependencyProvider { @Singleton @Provides // Single instance of DisplayMetrics, gets updated by StatusBar, but can be used -// anywhere it is needed. + // anywhere it is needed. public DisplayMetrics provideDisplayMetrics() { return new DisplayMetrics(); - } @Singleton @Provides - public LockscreenGestureLogger provideLockscreenGestureLogger() { - return new LockscreenGestureLogger(); + public ShadeController provideShadeController(Context context) { + return SysUiServiceProvider.getComponent(context, StatusBar.class); } @Singleton @Provides - public ShadeController provideShadeController(Context context) { - return SysUiServiceProvider.getComponent(context, StatusBar.class); + public SensorPrivacyManager provideSensorPrivacyManager(Context context) { + return context.getSystemService(SensorPrivacyManager.class); } @Singleton @Provides - public NotificationRemoteInputManager.Callback provideNotificationRemoteInputManager( - Context context) { - return new StatusBarRemoteInputCallback(context); + public LeakDetector provideLeakDetector() { + return LeakDetector.create(); } @Singleton @Provides - public AppOpsController provideAppOpsController(Context context, - @Named(BG_LOOPER_NAME) Looper bgLooper) { - return new AppOpsControllerImpl(context, bgLooper); - + public ColorDisplayController provideColorDisplayController(Context context) { + return new ColorDisplayController(context); } @Singleton @Provides - public DisplayNavigationBarController provideDisplayNavigationBarController(Context context, - @Named(MAIN_HANDLER_NAME) Handler mainHandler) { - return new DisplayNavigationBarController(context, mainHandler); + public PluginManager providePluginManager(Context context) { + return new PluginManagerImpl(context, new PluginInitializerImpl()); } @Singleton @Provides - public SensorPrivacyManager provideSensorPrivacyManager(Context context) { - return context.getSystemService(SensorPrivacyManager.class); + public ConfigurationController provideConfigurationController(Context context) { + return new ConfigurationControllerImpl(context); } } diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java index bab472c2e687..ae446ddc91ab 100644 --- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java @@ -29,9 +29,13 @@ import com.android.internal.messages.nano.SystemMessageProto; import java.util.Arrays; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Foreground service controller, a/k/a Dianne's Dungeon. */ +@Singleton public class ForegroundServiceControllerImpl implements ForegroundServiceController { @@ -45,6 +49,7 @@ public class ForegroundServiceControllerImpl private final SparseArray<UserServices> mUserServices = new SparseArray<>(); private final Object mMutex = new Object(); + @Inject public ForegroundServiceControllerImpl(Context context) { mContext = context; } diff --git a/packages/SystemUI/src/com/android/systemui/InitController.java b/packages/SystemUI/src/com/android/systemui/InitController.java index 81d32517b27a..a2dd12389fa9 100644 --- a/packages/SystemUI/src/com/android/systemui/InitController.java +++ b/packages/SystemUI/src/com/android/systemui/InitController.java @@ -16,10 +16,14 @@ package com.android.systemui; import java.util.ArrayList; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Created by {@link Dependency} on SystemUI startup. Add tasks which need to be executed only * after all other dependencies have been created. */ +@Singleton public class InitController { /** @@ -29,6 +33,10 @@ public class InitController { private final ArrayList<Runnable> mTasks = new ArrayList<>(); + @Inject + public InitController() { + } + /** * Add a task to be executed after {@link Dependency#start()} * @param runnable the task to be executed diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 384a14c70e9c..8bac7c590249 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -221,7 +221,8 @@ public class SystemUIFactory { } @Singleton - @Component(modules = {SystemUIFactory.class, DependencyProvider.class, ContextHolder.class}) + @Component(modules = {SystemUIFactory.class, DependencyProvider.class, DependencyBinder.class, + ContextHolder.class}) public interface SystemUIRootComponent { @Singleton Dependency.DependencyInjector createDependency(); diff --git a/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java b/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java index 82fd9b304195..a726b42f714f 100644 --- a/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java +++ b/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java @@ -20,14 +20,22 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Thread that offloads work from the UI thread but that is still perceptible to the user, so the * priority is the same as the main thread. */ +@Singleton public class UiOffloadThread { private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor(); + @Inject + public UiOffloadThread() { + } + public Future<?> submit(Runnable runnable) { return mExecutorService.submit(runnable); } diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index 52d1260b4221..e3bcb37474db 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -16,6 +16,8 @@ package com.android.systemui.appops; +import static com.android.systemui.Dependency.BG_LOOPER_NAME; + import android.app.AppOpsManager; import android.content.Context; import android.os.Handler; @@ -32,12 +34,17 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + /** * Controller to keep track of applications that have requested access to given App Ops * * It can be subscribed to with callbacks. Additionally, it passes on the information to * NotificationPresenter to be displayed to the user. */ +@Singleton public class AppOpsControllerImpl implements AppOpsController, AppOpsManager.OnOpActiveChangedListener, AppOpsManager.OnOpNotedListener { @@ -57,23 +64,16 @@ public class AppOpsControllerImpl implements AppOpsController, @GuardedBy("mNotedItems") private final List<AppOpItem> mNotedItems = new ArrayList<>(); - protected static final int[] OPS; - protected static final String[] OPS_STRING = new String[] { - AppOpsManager.OPSTR_CAMERA, - AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW, - AppOpsManager.OPSTR_RECORD_AUDIO, - AppOpsManager.OPSTR_COARSE_LOCATION, - AppOpsManager.OPSTR_FINE_LOCATION}; - - static { - int numOps = OPS_STRING.length; - OPS = new int[numOps]; - for (int i = 0; i < numOps; i++) { - OPS[i] = AppOpsManager.strOpToOp(OPS_STRING[i]); - } - } + protected static final int[] OPS = new int[] { + AppOpsManager.OP_CAMERA, + AppOpsManager.OP_SYSTEM_ALERT_WINDOW, + AppOpsManager.OP_RECORD_AUDIO, + AppOpsManager.OP_COARSE_LOCATION, + AppOpsManager.OP_FINE_LOCATION + }; - public AppOpsControllerImpl(Context context, Looper bgLooper) { + @Inject + public AppOpsControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper) { mContext = context; mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); mBGHandler = new H(bgLooper); @@ -92,7 +92,7 @@ public class AppOpsControllerImpl implements AppOpsController, protected void setListening(boolean listening) { if (listening) { mAppOps.startWatchingActive(OPS, this); - mAppOps.startWatchingNoted(OPS_STRING, this); + mAppOps.startWatchingNoted(OPS, this); } else { mAppOps.stopWatchingActive(this); mAppOps.stopWatchingNoted(this); @@ -254,14 +254,13 @@ public class AppOpsControllerImpl implements AppOpsController, } @Override - public void onOpNoted(String code, int uid, String packageName, int result) { + public void onOpNoted(int code, int uid, String packageName, int result) { if (DEBUG) { Log.w(TAG, "Op: " + code + " with result " + AppOpsManager.MODE_NAMES[result]); } if (result != AppOpsManager.MODE_ALLOWED) return; - int op_code = AppOpsManager.strOpToOp(code); - addNoted(op_code, uid, packageName); - notifySuscribers(op_code, uid, packageName, true); + addNoted(code, uid, packageName); + notifySuscribers(code, uid, packageName, true); } private void notifySuscribers(int code, int uid, String packageName, boolean active) { diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java index 3c8a461d3f73..fdf18ce24f50 100644 --- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java +++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java @@ -39,9 +39,13 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Arrays; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * ColorExtractor aware of wallpaper visibility */ +@Singleton public class SysuiColorExtractor extends ColorExtractor implements Dumpable { private static final String TAG = "SysuiColorExtractor"; private boolean mWallpaperVisible; @@ -49,6 +53,7 @@ public class SysuiColorExtractor extends ColorExtractor implements Dumpable { // Colors to return when the wallpaper isn't visible private final GradientColors mWpHiddenColors; + @Inject public SysuiColorExtractor(Context context) { this(context, new Tonal(context), true); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index 201c7e69beaf..9a8a6d0c3c3a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -41,6 +41,8 @@ import androidx.slice.builders.ListBuilder.RowBuilder; import androidx.slice.builders.SliceAction; import com.android.internal.annotations.VisibleForTesting; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.R; import com.android.systemui.statusbar.policy.NextAlarmController; import com.android.systemui.statusbar.policy.NextAlarmControllerImpl; @@ -49,6 +51,7 @@ import com.android.systemui.statusbar.policy.ZenModeControllerImpl; import java.util.Date; import java.util.Locale; +import java.util.TimeZone; import java.util.concurrent.TimeUnit; /** @@ -100,21 +103,28 @@ public class KeyguardSliceProvider extends SliceProvider implements @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); - if (Intent.ACTION_TIME_TICK.equals(action) - || Intent.ACTION_DATE_CHANGED.equals(action) - || Intent.ACTION_TIME_CHANGED.equals(action) - || Intent.ACTION_TIMEZONE_CHANGED.equals(action) - || Intent.ACTION_LOCALE_CHANGED.equals(action)) { - if (Intent.ACTION_LOCALE_CHANGED.equals(action) - || Intent.ACTION_TIMEZONE_CHANGED.equals(action)) { - // need to get a fresh date format - mHandler.post(KeyguardSliceProvider.this::cleanDateFormat); - } + if (Intent.ACTION_DATE_CHANGED.equals(action)) { mHandler.post(KeyguardSliceProvider.this::updateClock); + } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) { + mHandler.post(KeyguardSliceProvider.this::cleanDateFormat); } } }; + @VisibleForTesting + final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback = + new KeyguardUpdateMonitorCallback() { + @Override + public void onTimeChanged() { + mHandler.post(KeyguardSliceProvider.this::updateClock); + } + + @Override + public void onTimeZoneChanged(TimeZone timeZone) { + mHandler.post(KeyguardSliceProvider.this::cleanDateFormat); + } + }; + public KeyguardSliceProvider() { this(new Handler()); } @@ -250,11 +260,10 @@ public class KeyguardSliceProvider extends SliceProvider implements IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_DATE_CHANGED); - filter.addAction(Intent.ACTION_TIME_CHANGED); - filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); filter.addAction(Intent.ACTION_LOCALE_CHANGED); getContext().registerReceiver(mIntentReceiver, filter, null /* permission*/, null /* scheduler */); + getKeyguardUpdateMonitor().registerCallback(mKeyguardUpdateMonitorCallback); mRegistered = true; } @@ -300,4 +309,9 @@ public class KeyguardSliceProvider extends SliceProvider implements } updateNextAlarm(); } + + @VisibleForTesting + protected KeyguardUpdateMonitor getKeyguardUpdateMonitor() { + return KeyguardUpdateMonitor.getInstance(getContext()); + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java index b6fce4408bcf..834bca55103a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java @@ -23,9 +23,13 @@ import com.android.systemui.Dumpable; import java.io.FileDescriptor; import java.io.PrintWriter; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Tracks the screen lifecycle. */ +@Singleton public class ScreenLifecycle extends Lifecycle<ScreenLifecycle.Observer> implements Dumpable { public static final int SCREEN_OFF = 0; @@ -35,6 +39,10 @@ public class ScreenLifecycle extends Lifecycle<ScreenLifecycle.Observer> impleme private int mScreenState = SCREEN_OFF; + @Inject + public ScreenLifecycle() { + } + public int getScreenState() { return mScreenState; } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java index 59c7f236caf7..52a0214c492c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java @@ -23,9 +23,13 @@ import com.android.systemui.Dumpable; import java.io.FileDescriptor; import java.io.PrintWriter; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Tracks the wakefulness lifecycle. */ +@Singleton public class WakefulnessLifecycle extends Lifecycle<WakefulnessLifecycle.Observer> implements Dumpable { @@ -36,6 +40,10 @@ public class WakefulnessLifecycle extends Lifecycle<WakefulnessLifecycle.Observe private int mWakefulness = WAKEFULNESS_ASLEEP; + @Inject + public WakefulnessLifecycle() { + } + public int getWakefulness() { return mWakefulness; } diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java index 03daa95567ee..69495319e060 100644 --- a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java @@ -20,11 +20,20 @@ import com.android.systemui.Dependency; import com.android.systemui.plugins.PluginDependency.DependencyProvider; import com.android.systemui.shared.plugins.PluginManager; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + */ +@Singleton public class PluginDependencyProvider extends DependencyProvider { private final ArrayMap<Class<?>, Object> mDependencies = new ArrayMap<>(); private final PluginManager mManager; + /** + */ + @Inject public PluginDependencyProvider(PluginManager manager) { mManager = manager; PluginDependency.sProvider = this; diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java index b722f9f1d1c1..50dda1c1de6b 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java @@ -58,6 +58,12 @@ import java.text.NumberFormat; import java.util.Locale; import java.util.Objects; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + */ +@Singleton public class PowerNotificationWarnings implements PowerUI.WarningsUI { private static final String TAG = PowerUI.TAG + ".Notification"; private static final boolean DEBUG = PowerUI.DEBUG; @@ -136,6 +142,9 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { private SystemUIDialog mHighTempDialog; private SystemUIDialog mThermalShutdownDialog; + /** + */ + @Inject public PowerNotificationWarnings(Context context) { mContext = context; mNoMan = mContext.getSystemService(NotificationManager.class); diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt index bbdae291d81e..3991c19e4d05 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt @@ -106,12 +106,12 @@ class OngoingPrivacyDialog constructor( val appName = item.findViewById(R.id.app_name) as TextView val icons = item.findViewById(R.id.icons) as LinearLayout - var lp = LinearLayout.LayoutParams(iconSize, iconSize).apply { + val lp = LinearLayout.LayoutParams(iconSize, iconSize).apply { gravity = Gravity.CENTER_VERTICAL marginStart = iconMargin } - app.icon?.let { + app.icon.let { appIcon.setImageDrawable(it) } diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt index 519df19f0e20..2894621e7775 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt @@ -29,7 +29,9 @@ class PrivacyDialogBuilder(val context: Context, itemsList: List<PrivacyItem>) { init { appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType }) .toList() - .sortedWith(compareBy({ -it.second.size }, { it.first })) + .sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps + { it.second.min() }, // Sort by "smallest" AppOpp (Location is largest) + { it.first })) // Sort alphabetically bt App Name types = itemsList.map { it.privacyType }.distinct().sorted() val singleApp = appsAndTypes.size == 1 app = if (singleApp) appsAndTypes[0].first else null diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt index 85e99f05f895..9252167d02bd 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt @@ -24,8 +24,8 @@ typealias Privacy = PrivacyType enum class PrivacyType(val nameId: Int, val iconId: Int) { TYPE_CAMERA(R.string.privacy_type_camera, R.drawable.stat_sys_camera), - TYPE_LOCATION(R.string.privacy_type_location, R.drawable.stat_sys_location), - TYPE_MICROPHONE(R.string.privacy_type_microphone, R.drawable.stat_sys_mic_none); + TYPE_MICROPHONE(R.string.privacy_type_microphone, R.drawable.stat_sys_mic_none), + TYPE_LOCATION(R.string.privacy_type_location, R.drawable.stat_sys_location); fun getName(context: Context) = context.resources.getString(nameId) @@ -44,7 +44,7 @@ data class PrivacyApplication(val packageName: String, val context: Context) return applicationName.compareTo(other.applicationName) } - var icon: Drawable? = null + var icon: Drawable = context.getDrawable(android.R.drawable.sym_def_app_icon) var applicationName: String init { diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt index a87d634451cd..d5b807def21f 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt @@ -29,6 +29,7 @@ import com.android.internal.annotations.VisibleForTesting import com.android.systemui.Dependency import com.android.systemui.appops.AppOpItem import com.android.systemui.appops.AppOpsController +import com.android.systemui.R class PrivacyItemController(val context: Context, val callback: Callback) { @@ -41,15 +42,17 @@ class PrivacyItemController(val context: Context, val callback: Callback) { Intent.ACTION_MANAGED_PROFILE_ADDED, Intent.ACTION_MANAGED_PROFILE_REMOVED) const val TAG = "PrivacyItemController" + const val SYSTEM_UID = 1000 } - private var privacyList = emptyList<PrivacyItem>() + private val appOpsController = Dependency.get(AppOpsController::class.java) private val userManager = context.getSystemService(UserManager::class.java) private var currentUserIds = emptyList<Int>() private val bgHandler = Handler(Dependency.get(Dependency.BG_LOOPER)) private val uiHandler = Dependency.get(Dependency.MAIN_HANDLER) private var listening = false + val systemApp = PrivacyApplication(context.getString(R.string.device_services), context) private val notifyChanges = Runnable { callback.privacyChanged(privacyList) @@ -126,6 +129,7 @@ class PrivacyItemController(val context: Context, val callback: Callback) { AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE else -> return null } + if (appOpItem.uid == SYSTEM_UID) return PrivacyItem(type, systemApp) val app = PrivacyApplication(appOpItem.packageName, context) return PrivacyItem(type, app) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java index 8903a38dc600..aba9bb804619 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java @@ -272,10 +272,17 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, public void updateEverything() { post(() -> { updateVisibilities(); + updateClickabilities(); setClickable(false); }); } + private void updateClickabilities() { + mMultiUserSwitch.setClickable(mMultiUserSwitch.getVisibility() == View.VISIBLE); + mEdit.setClickable(mEdit.getVisibility() == View.VISIBLE); + mSettingsButton.setClickable(mSettingsButton.getVisibility() == View.VISIBLE); + } + private void updateVisibilities() { mSettingsContainer.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE); mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility( diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index f2f83c0e0aa5..bec027f902a0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -426,7 +426,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements if (mExpanded == expanded) return; mExpanded = expanded; mHeaderQsPanel.setExpanded(expanded); - updateEverything(); } /** @@ -698,10 +697,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements .start(); } - public void updateEverything() { - post(() -> setClickable(false)); - } - public void setQSPanel(final QSPanel qsPanel) { mQsPanel = qsPanel; setupHost(qsPanel.getHost()); diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 12b6f673de1c..538e0f0de415 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -63,9 +63,13 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Class to send information from overview to launcher with a binder. */ +@Singleton public class OverviewProxyService implements CallbackController<OverviewProxyListener>, Dumpable { private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE"; @@ -338,6 +342,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis private final IBinder.DeathRecipient mOverviewServiceDeathRcpt = this::cleanupAfterDeath; + @Inject public OverviewProxyService(Context context) { mContext = context; mHandler = new Handler(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java index 2f2610998b35..78172f19d291 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java @@ -18,6 +18,8 @@ package com.android.systemui.statusbar; import static android.view.Display.DEFAULT_DISPLAY; +import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; + import android.content.Context; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; @@ -32,9 +34,14 @@ import android.view.WindowManagerGlobal; import com.android.systemui.statusbar.phone.NavigationBarFragment; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + /** * A controller to handle external navigation bars */ +@Singleton public class DisplayNavigationBarController implements DisplayListener { private static final String TAG = DisplayNavigationBarController.class.getName(); @@ -46,7 +53,9 @@ public class DisplayNavigationBarController implements DisplayListener { /** A displayId - nav bar mapping */ private SparseArray<NavigationBarFragment> mExternalNavigationBarMap = new SparseArray<>(); - public DisplayNavigationBarController(Context context, Handler handler) { + @Inject + public DisplayNavigationBarController(Context context, + @Named(MAIN_HANDLER_NAME) Handler handler) { mContext = context; mHandler = handler; mDisplayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 7d1b640f7e40..158430f9ff92 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -19,10 +19,7 @@ package com.android.systemui.statusbar; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.app.admin.DevicePolicyManager; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.res.ColorStateList; import android.content.res.Resources; import android.graphics.Color; @@ -35,7 +32,6 @@ import android.os.Handler; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.UserHandle; import android.os.UserManager; import android.text.TextUtils; import android.text.format.Formatter; @@ -152,10 +148,7 @@ public class KeyguardIndicationController implements StateListener { private void registerCallbacks(KeyguardUpdateMonitor monitor) { monitor.registerCallback(getKeyguardCallback()); - mContext.registerReceiverAsUser(mTickReceiver, UserHandle.SYSTEM, - new IntentFilter(Intent.ACTION_TIME_TICK), null, - Dependency.get(Dependency.TIME_TICK_HANDLER)); - + KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mTickReceiver); Dependency.get(StatusBarStateController.class).addCallback(this); } @@ -166,7 +159,7 @@ public class KeyguardIndicationController implements StateListener { * //TODO: This can probably be converted to a fragment and not have to be manually recreated */ public void destroy() { - mContext.unregisterReceiver(mTickReceiver); + KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mTickReceiver); Dependency.get(StatusBarStateController.class).removeCallback(this); } @@ -477,16 +470,15 @@ public class KeyguardIndicationController implements StateListener { mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; } - private final BroadcastReceiver mTickReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - mHandler.post(() -> { - if (mVisible) { - updateIndication(false); + private final KeyguardUpdateMonitorCallback mTickReceiver = + new KeyguardUpdateMonitorCallback() { + @Override + public void onTimeChanged() { + if (mVisible) { + updateIndication(false /* animate */); + } } - }); - } - }; + }; private final Handler mHandler = new Handler() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java index 6560f8f9c420..442416fd3bea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java @@ -26,6 +26,12 @@ import android.os.VibrationEffect; import android.os.Vibrator; import android.provider.Settings; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + */ +@Singleton public class VibratorHelper { private final Vibrator mVibrator; @@ -44,6 +50,9 @@ public class VibratorHelper { } }; + /** + */ + @Inject public VibratorHelper(Context context) { mContext = context; mVibrator = context.getSystemService(Vibrator.class); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java index f899863dcc6b..e1b231b96693 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java @@ -28,12 +28,12 @@ import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; +import android.view.SyncRtSurfaceTransactionApplier; +import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; import com.android.internal.policy.ScreenDecorationsUtils; import com.android.systemui.Interpolators; import com.android.systemui.shared.system.SurfaceControlCompat; -import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier; -import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier.SurfaceParams; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment; @@ -267,8 +267,8 @@ public class ActivityLaunchAnimator { Matrix m = new Matrix(); m.postTranslate(0, (float) (mParams.top - app.position.y)); mWindowCrop.set(mParams.left, 0, mParams.right, mParams.getHeight()); - SurfaceParams params = new SurfaceParams(new SurfaceControlCompat(app.leash), - 1f /* alpha */, m, mWindowCrop, app.prefixOrderIndex, mCornerRadius); + SurfaceParams params = new SurfaceParams(app.leash, 1f /* alpha */, m, mWindowCrop, + app.prefixOrderIndex, mCornerRadius); mSyncRtTransactionApplier.scheduleApply(params); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java index 5906dc29fa08..5b44a77454b2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java @@ -29,6 +29,12 @@ import com.android.systemui.statusbar.policy.DarkIconDispatcher; import java.io.FileDescriptor; import java.io.PrintWriter; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + */ +@Singleton public class DarkIconDispatcherImpl implements DarkIconDispatcher { private final LightBarTransitionsController mTransitionsController; @@ -40,6 +46,9 @@ public class DarkIconDispatcherImpl implements DarkIconDispatcher { private int mDarkModeIconColorSingleTone; private int mLightModeIconColorSingleTone; + /** + */ + @Inject public DarkIconDispatcherImpl(Context context) { mDarkModeIconColorSingleTone = context.getColor(R.color.dark_mode_icon_color_single_tone); mLightModeIconColorSingleTone = context.getColor(R.color.light_mode_icon_color_single_tone); 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 7c30e48f171a..e156e770cb72 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java @@ -34,9 +34,13 @@ import com.android.systemui.statusbar.policy.DarkIconDispatcher; import java.io.FileDescriptor; import java.io.PrintWriter; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Controls how light status bar flag applies to the icons. */ +@Singleton public class LightBarController implements BatteryController.BatteryStateChangeCallback, Dumpable { private static final float NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD = 0.1f; @@ -78,6 +82,7 @@ public class LightBarController implements BatteryController.BatteryStateChangeC private final Context mContext; + @Inject public LightBarController(Context ctx) { mDarkModeColor = Color.valueOf(ctx.getColor(R.color.dark_mode_icon_color_single_tone)); mStatusBarIconController = Dependency.get(DarkIconDispatcher.class); 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 7876aa5d89d0..dd07ec4c27dd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.statusbar.phone.NavBarTintController.DEFAULT_COLOR_ADAPT_TRANSITION_TIME; 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; @@ -161,7 +162,7 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, public long getTintAnimationDuration() { if (NavBarTintController.isEnabled(mContext)) { return Math.max(Settings.Global.getInt(mContext.getContentResolver(), - NAV_COLOR_TRANSITION_TIME_SETTING, DEFAULT_TINT_ANIMATION_DURATION), + NAV_COLOR_TRANSITION_TIME_SETTING, DEFAULT_COLOR_ADAPT_TRANSITION_TIME), MIN_COLOR_ADAPT_TRANSITION_TIME); } return DEFAULT_TINT_ANIMATION_DURATION; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java index 7621887b4170..12819535cfb6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java @@ -25,16 +25,21 @@ import com.android.systemui.Dependency; import com.android.systemui.EventLogConstants; import com.android.systemui.EventLogTags; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Wrapper that emits both new- and old-style gesture logs. * TODO: delete this once the old logs are no longer needed. */ +@Singleton public class LockscreenGestureLogger { private ArrayMap<Integer, Integer> mLegacyMap; private LogMaker mLogMaker = new LogMaker(MetricsEvent.VIEW_UNKNOWN) .setType(MetricsEvent.TYPE_ACTION); private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); + @Inject public LockscreenGestureLogger() { mLegacyMap = new ArrayMap<>(EventLogConstants.METRICS_GESTURE_TYPE_MAP.length); for (int i = 0; i < EventLogConstants.METRICS_GESTURE_TYPE_MAP.length ; i++) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java index 0f8d59b12ddb..fbd8d8a76d26 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java @@ -28,6 +28,12 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + */ +@Singleton public class ManagedProfileControllerImpl implements ManagedProfileController { private final List<Callback> mCallbacks = new ArrayList<>(); @@ -38,6 +44,9 @@ public class ManagedProfileControllerImpl implements ManagedProfileController { private boolean mListening; private int mCurrentUser; + /** + */ + @Inject public ManagedProfileControllerImpl(Context context) { mContext = context; mUserManager = UserManager.get(mContext); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java index 9ecee1825f07..b4f850b033e8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java @@ -31,6 +31,7 @@ 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; + public static final int DEFAULT_COLOR_ADAPT_TRANSITION_TIME = 1500; private final HandlerThread mColorAdaptHandlerThread = new HandlerThread("ColorExtractThread"); private Handler mColorAdaptionHandler; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index c7e4d340b7d8..c0909e3e5bd2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -53,6 +53,7 @@ import android.widget.FrameLayout; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.keyguard.KeyguardClockSwitch; import com.android.keyguard.KeyguardStatusView; import com.android.systemui.DejankUtils; import com.android.systemui.Dependency; @@ -133,7 +134,7 @@ public class NotificationPanelView extends PanelView implements public static final int FLING_COLLAPSE = 1; /** - * Fing until QS is completely hidden. + * Fling until QS is completely hidden. */ public static final int FLING_HIDE = 2; @@ -359,6 +360,10 @@ public class NotificationPanelView extends PanelView implements mKeyguardStatusBar = findViewById(R.id.keyguard_header); mKeyguardStatusView = findViewById(R.id.keyguard_status_view); + KeyguardClockSwitch keyguardClockSwitch = findViewById(R.id.keyguard_clock_container); + ViewGroup bigClockContainer = findViewById(R.id.big_clock_container); + keyguardClockSwitch.setBigClockContainer(bigClockContainer); + mNotificationContainerParent = findViewById(R.id.notification_container_parent); mNotificationStackScroller = findViewById(R.id.notification_stack_scroller); mNotificationStackScroller.setOnHeightChangedListener(this); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index ee1eb42a07f0..d6f2fd7a4eee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -85,7 +85,6 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceP import com.android.systemui.statusbar.policy.HotspotController; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.LocationController; -import com.android.systemui.statusbar.policy.LocationController.LocationChangeCallback; import com.android.systemui.statusbar.policy.NextAlarmController; import com.android.systemui.statusbar.policy.RotationLockController; import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback; @@ -102,9 +101,8 @@ import java.util.Locale; * strictly doesn't need to. */ public class PhoneStatusBarPolicy implements Callback, Callbacks, - RotationLockControllerCallback, Listener, LocationChangeCallback, - ZenModeController.Callback, DeviceProvisionedListener, KeyguardMonitor.Callback, - PrivacyItemController.Callback { + RotationLockControllerCallback, Listener, ZenModeController.Callback, + DeviceProvisionedListener, KeyguardMonitor.Callback, PrivacyItemController.Callback { private static final String TAG = "PhoneStatusBarPolicy"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -255,6 +253,9 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, mIconController.setIconVisibility(mSlotMicrophone, false); mIconController.setIcon(mSlotCamera, R.drawable.stat_sys_camera, null); mIconController.setIconVisibility(mSlotCamera, false); + mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID, + mContext.getString(R.string.accessibility_location_active)); + mIconController.setIconVisibility(mSlotLocation, false); mRotationLockController.addCallback(this); mBluetooth.addCallback(this); @@ -265,7 +266,6 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, mNextAlarmController.addCallback(mNextAlarmCallback); mDataSaver.addCallback(this); mKeyguardMonitor.addCallback(this); - mLocationController.addCallback(this); mPrivacyItemController.setListening(true); SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallback(this); @@ -294,7 +294,6 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, mNextAlarmController.removeCallback(mNextAlarmCallback); mDataSaver.removeCallback(this); mKeyguardMonitor.removeCallback(this); - mLocationController.removeCallback(this); mPrivacyItemController.setListening(false); SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallback(this); mContext.unregisterReceiver(mIntentReceiver); @@ -314,21 +313,6 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, updateVolumeZen(); } - @Override - public void onLocationActiveChanged(boolean active) { - updateLocation(); - } - - // Updates the status view based on the current state of location requests. - private void updateLocation() { - if (mLocationController.isLocationActive()) { - mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID, - mContext.getString(R.string.accessibility_location_active)); - } else { - mIconController.removeAllIconsForSlot(mSlotLocation); - } - } - private void updateAlarm() { final AlarmClockInfo alarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT); final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java index 4f25349ef7d2..db7589d0f333 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java @@ -45,11 +45,15 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Receives the callbacks from CommandQueue related to icons and tracks the state of * all the icons. Dispatches this state to any IconManagers that are currently * registered with it. */ +@Singleton public class StatusBarIconControllerImpl extends StatusBarIconList implements Tunable, ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController { @@ -66,6 +70,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu private boolean mIsDark = false; + @Inject public StatusBarIconControllerImpl(Context context) { super(context.getResources().getStringArray( com.android.internal.R.array.config_statusBarIcons)); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java index 78f537459d0a..116ecc8ddeb5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java @@ -47,6 +47,12 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.PreviewInflater; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + */ +@Singleton public class StatusBarRemoteInputCallback implements Callback, Callbacks { private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); @@ -65,6 +71,9 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks { private int mDisabled2; protected BroadcastReceiver mChallengeReceiver = new ChallengeReceiver(); + /** + */ + @Inject public StatusBarRemoteInputCallback(Context context) { mContext = context; mContext.registerReceiverAsUser(mChallengeReceiver, UserHandle.ALL, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java index cb6e30070fdd..88f904882c8f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java @@ -54,9 +54,13 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.reflect.Field; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Encapsulates all logic for the status bar window state management. */ +@Singleton public class StatusBarWindowController implements Callback, Dumpable, ConfigurationListener { private static final String TAG = "StatusBarWindowController"; @@ -78,6 +82,7 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class); + @Inject public StatusBarWindowController(Context context) { this(context, context.getSystemService(WindowManager.class), ActivityManager.getService(), DozeParameters.getInstance(context)); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityController.java index cc431dd3c34a..ebfdb3f9aa76 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityController.java @@ -23,6 +23,12 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + */ +@Singleton public class AccessibilityController implements AccessibilityManager.AccessibilityStateChangeListener, AccessibilityManager.TouchExplorationStateChangeListener { @@ -32,6 +38,9 @@ public class AccessibilityController implements private boolean mAccessibilityEnabled; private boolean mTouchExplorationEnabled; + /** + */ + @Inject public AccessibilityController(Context context) { AccessibilityManager am = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java index 67da8a54b68a..1395e1377529 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java @@ -19,14 +19,19 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * For mocking because AccessibilityManager is final for some reason... */ +@Singleton public class AccessibilityManagerWrapper implements CallbackController<AccessibilityServicesStateChangeListener> { private final AccessibilityManager mAccessibilityManager; + @Inject public AccessibilityManagerWrapper(Context context) { mAccessibilityManager = context.getSystemService(AccessibilityManager.class); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java index ddcfbf6f3c31..5d61f4aa7fcb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java @@ -39,10 +39,14 @@ import java.io.PrintWriter; import java.text.NumberFormat; import java.util.ArrayList; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Default implementation of a {@link BatteryController}. This controller monitors for battery * level change events that are broadcasted by the system. */ +@Singleton public class BatteryControllerImpl extends BroadcastReceiver implements BatteryController { private static final String TAG = "BatteryController"; @@ -68,6 +72,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC private Estimate mEstimate; private long mLastEstimateTimestamp = -1; + @Inject public BatteryControllerImpl(Context context) { this(context, context.getSystemService(PowerManager.class)); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java index 6f64a563289e..c8550003d2f4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.policy; +import static com.android.systemui.Dependency.BG_LOOPER_NAME; + import android.app.ActivityManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; @@ -42,6 +44,13 @@ import java.util.Collection; import java.util.List; import java.util.WeakHashMap; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +/** + */ +@Singleton public class BluetoothControllerImpl implements BluetoothController, BluetoothCallback, CachedBluetoothDevice.Callback, LocalBluetoothProfileManager.ServiceListener { private static final String TAG = "BluetoothController"; @@ -61,7 +70,10 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa private final H mHandler = new H(Looper.getMainLooper()); private int mState; - public BluetoothControllerImpl(Context context, Looper bgLooper) { + /** + */ + @Inject + public BluetoothControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper) { mLocalBluetoothManager = Dependency.get(LocalBluetoothManager.class); mBgHandler = new Handler(bgLooper); if (mLocalBluetoothManager != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java index ccfe073d5cb8..c7d337ad46d6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java @@ -44,8 +44,12 @@ import java.util.Objects; import java.util.Set; import java.util.UUID; +import javax.inject.Inject; +import javax.inject.Singleton; + /** Platform implementation of the cast controller. **/ +@Singleton public class CastControllerImpl implements CastController { private static final String TAG = "CastController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -63,6 +67,7 @@ public class CastControllerImpl implements CastController { private boolean mCallbackRegistered; private MediaProjectionInfo mProjection; + @Inject public CastControllerImpl(Context context) { mContext = context; mMediaRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java index f2283a53a608..c995162c8bb1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java @@ -27,6 +27,12 @@ import com.android.systemui.settings.CurrentUserTracker; import java.util.ArrayList; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + */ +@Singleton public class DeviceProvisionedControllerImpl extends CurrentUserTracker implements DeviceProvisionedController { @@ -36,6 +42,9 @@ public class DeviceProvisionedControllerImpl extends CurrentUserTracker implemen private final Uri mDeviceProvisionedUri; private final Uri mUserSetupUri; + /** + */ + @Inject public DeviceProvisionedControllerImpl(Context context) { super(context); mContext = context; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java index a6146a625193..2305db0dbf8d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java @@ -34,6 +34,12 @@ import java.util.Comparator; import java.util.function.Consumer; import java.util.function.Supplier; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + */ +@Singleton public class ExtensionControllerImpl implements ExtensionController { public static final int SORT_ORDER_PLUGIN = 0; @@ -44,6 +50,9 @@ public class ExtensionControllerImpl implements ExtensionController { private final Context mDefaultContext; + /** + */ + @Inject public ExtensionControllerImpl(Context context) { mDefaultContext = context; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java index e23ce2d022b3..41ff9d1029b2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java @@ -35,9 +35,13 @@ import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Manages the flashlight. */ +@Singleton public class FlashlightControllerImpl implements FlashlightController { private static final String TAG = "FlashlightController"; @@ -64,6 +68,7 @@ public class FlashlightControllerImpl implements FlashlightController { private String mCameraId; private boolean mTorchAvailable; + @Inject public FlashlightControllerImpl(Context context) { mContext = context; mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java index 0a72c3f9e8d8..420abe88f455 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java @@ -29,6 +29,12 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + */ +@Singleton public class HotspotControllerImpl implements HotspotController, WifiManager.SoftApCallback { private static final String TAG = "HotspotController"; @@ -43,6 +49,9 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof private int mNumConnectedDevices; private boolean mWaitingForTerminalState; + /** + */ + @Inject public HotspotControllerImpl(Context context) { mContext = context; mConnectivityManager = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLoggerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLoggerImpl.java index aee021ce4688..ba6369e2c90e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLoggerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLoggerImpl.java @@ -18,6 +18,7 @@ import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_STATUS_ICONS; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.STATUS_BAR_ICONS_CHANGED; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION; +import static com.android.systemui.Dependency.BG_LOOPER_NAME; import android.content.Context; import android.metrics.LogMaker; @@ -32,6 +33,9 @@ import com.android.internal.logging.MetricsLogger; import java.util.Arrays; import java.util.List; +import javax.inject.Inject; +import javax.inject.Named; + public class IconLoggerImpl implements IconLogger { // Minimum ms between log statements. @@ -46,7 +50,9 @@ public class IconLoggerImpl implements IconLogger { private final List<String> mIconIndex; private long mLastLog = System.currentTimeMillis(); - public IconLoggerImpl(Context context, Looper bgLooper, MetricsLogger logger) { + @Inject + public IconLoggerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper, + MetricsLogger logger) { mContext = context; mHandler = new Handler(bgLooper); mLogger = logger; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java index 5eb0fb76794d..3c8ed6ebf279 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java @@ -25,6 +25,12 @@ import com.android.systemui.settings.CurrentUserTracker; import java.util.ArrayList; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + */ +@Singleton public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback implements KeyguardMonitor { @@ -47,6 +53,9 @@ public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback private boolean mKeyguardGoingAway; private boolean mLaunchTransitionFadingAway; + /** + */ + @Inject public KeyguardMonitorImpl(Context context) { mContext = context; mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java index 840e77ea85a2..683cdbb326dc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.policy; import static com.android.settingslib.Utils.updateLocationEnabled; +import static com.android.systemui.Dependency.BG_LOOPER_NAME; import android.app.ActivityManager; import android.app.AppOpsManager; @@ -40,9 +41,14 @@ import com.android.systemui.util.Utils; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + /** * A controller to manage changes of location related states and update the views accordingly. */ +@Singleton public class LocationControllerImpl extends BroadcastReceiver implements LocationController { private static final int[] mHighPowerRequestAppOpArray @@ -59,7 +65,8 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio new ArrayList<LocationChangeCallback>(); private final H mHandler = new H(); - public LocationControllerImpl(Context context, Looper bgLooper) { + @Inject + public LocationControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper) { mContext = context; // Register to listen for changes in location settings. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index bc43120fcefa..9422101c76dd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -22,6 +22,8 @@ import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE; import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT; +import static com.android.systemui.Dependency.BG_LOOPER_NAME; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -65,7 +67,6 @@ import com.android.systemui.statusbar.policy.MobileSignalController.MobileIconGr import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Arrays; import java.util.BitSet; import java.util.Collections; import java.util.Comparator; @@ -74,7 +75,12 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + /** Platform implementation of the network controller. **/ +@Singleton public class NetworkControllerImpl extends BroadcastReceiver implements NetworkController, DemoMode, DataUsageController.NetworkNameProvider, ConfigurationChangedReceiver, Dumpable { @@ -154,7 +160,8 @@ public class NetworkControllerImpl extends BroadcastReceiver /** * Construct this controller object and register for updates. */ - public NetworkControllerImpl(Context context, Looper bgLooper, + @Inject + public NetworkControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper, DeviceProvisionedController deviceProvisionedController) { this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE), (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java index dac878c69313..288b3aff2af6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java @@ -27,6 +27,13 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Implementation of {@link NextAlarmController} + */ +@Singleton public class NextAlarmControllerImpl extends BroadcastReceiver implements NextAlarmController { @@ -35,6 +42,9 @@ public class NextAlarmControllerImpl extends BroadcastReceiver private AlarmManager mAlarmManager; private AlarmManager.AlarmClockInfo mNextAlarm; + /** + */ + @Inject public NextAlarmControllerImpl(Context context) { mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); IntentFilter filter = new IntentFilter(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java index 5418dc14e0bb..1f368e164678 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java @@ -23,7 +23,11 @@ import com.android.internal.view.RotationPolicy; import java.util.concurrent.CopyOnWriteArrayList; +import javax.inject.Inject; +import javax.inject.Singleton; + /** Platform implementation of the rotation lock controller. **/ +@Singleton public final class RotationLockControllerImpl implements RotationLockController { private final Context mContext; private final CopyOnWriteArrayList<RotationLockControllerCallback> mCallbacks = @@ -37,6 +41,7 @@ public final class RotationLockControllerImpl implements RotationLockController } }; + @Inject public RotationLockControllerImpl(Context context) { mContext = context; setListening(true); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java index 29a6b956ed1e..e0259c9f5af7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java @@ -55,6 +55,12 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + */ +@Singleton public class SecurityControllerImpl extends CurrentUserTracker implements SecurityController { private static final String TAG = "SecurityController"; @@ -90,6 +96,9 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi // Needs to be cached here since the query has to be asynchronous private ArrayMap<Integer, Boolean> mHasCACerts = new ArrayMap<Integer, Boolean>(); + /** + */ + @Inject public SecurityControllerImpl(Context context) { this(context, null); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java index fed803217568..0ca6ff6ec66e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java @@ -40,6 +40,12 @@ import com.android.systemui.R; import java.util.ArrayList; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + */ +@Singleton public class UserInfoControllerImpl implements UserInfoController { private static final String TAG = "UserInfoController"; @@ -53,6 +59,9 @@ public class UserInfoControllerImpl implements UserInfoController { private Drawable mUserDrawable; private String mUserAccount; + /** + */ + @Inject public UserInfoControllerImpl(Context context) { mContext = context; IntentFilter filter = new IntentFilter(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index 15b2f2bcb755..e412e09fe519 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.policy; import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; +import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; import android.app.ActivityManager; import android.app.Dialog; @@ -68,9 +69,14 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + /** * Keeps a list of all users on the device for user switching. */ +@Singleton public class UserSwitcherController implements Dumpable { private static final String TAG = "UserSwitcherController"; @@ -102,8 +108,9 @@ public class UserSwitcherController implements Dumpable { private Intent mSecondaryUserServiceIntent; private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2); + @Inject public UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor, - Handler handler, ActivityStarter activityStarter) { + @Named(MAIN_HANDLER_NAME) Handler handler, ActivityStarter activityStarter) { mContext = context; mGuestResumeSessionReceiver.register(context); mKeyguardMonitor = keyguardMonitor; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java index 89ccff03133d..7198165456c3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.policy; +import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; + import android.app.ActivityManager; import android.app.AlarmManager; import android.app.NotificationManager; @@ -48,7 +50,12 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Objects; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + /** Platform implementation of the zen mode controller. **/ +@Singleton public class ZenModeControllerImpl extends CurrentUserTracker implements ZenModeController, Dumpable { private static final String TAG = "ZenModeController"; @@ -71,7 +78,8 @@ public class ZenModeControllerImpl extends CurrentUserTracker private long mZenUpdateTime; private NotificationManager.Policy mConsolidatedNotificationPolicy; - public ZenModeControllerImpl(Context context, Handler handler) { + @Inject + public ZenModeControllerImpl(Context context, @Named(MAIN_HANDLER_NAME) Handler handler) { super(context); mContext = context; mModeSetting = new GlobalSetting(mContext, handler, Global.ZEN_MODE) { diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java b/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java index e85dee841715..81d77a64c1f0 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java @@ -21,6 +21,9 @@ import android.view.WindowManager; import com.android.systemui.Dependency; import com.android.systemui.tuner.TunerService.Tunable; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Version of Space that can be resized by a tunable setting. */ @@ -69,8 +72,18 @@ public class TunablePadding implements Tunable { Dependency.get(TunerService.class).removeTunable(this); } - // Exists for easy injecting in tests. + /** + * Exists for easy injecting in tests. + */ + @Singleton public static class TunablePaddingService { + + /** + */ + @Inject + public TunablePaddingService() { + } + public TunablePadding add(View view, String key, int defaultSize, int flags) { if (view == null) { throw new IllegalArgumentException(); diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java index 196d9bc32e75..0a47f19ac2f5 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java @@ -43,7 +43,13 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Set; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + */ +@Singleton public class TunerServiceImpl extends TunerService { private static final String TUNER_VERSION = "sysui_tuner_version"; @@ -70,6 +76,9 @@ public class TunerServiceImpl extends TunerService { private int mCurrentUser; private CurrentUserTracker mUserTracker; + /** + */ + @Inject public TunerServiceImpl(Context context) { mContext = context; mContentResolver = mContext.getContentResolver(); diff --git a/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java b/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java index 88cbbb574aff..31f4991a82b5 100644 --- a/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java +++ b/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java @@ -38,6 +38,9 @@ import com.android.systemui.shared.plugins.PluginManager; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Wrapper around sensor manager that hides potential sources of latency. * @@ -45,6 +48,7 @@ import java.util.List; * without blocking. Note that this means registering listeners now always appears successful even * if it is not. */ +@Singleton public class AsyncSensorManager extends SensorManager implements PluginListener<SensorManagerPlugin> { @@ -56,8 +60,14 @@ public class AsyncSensorManager extends SensorManager @VisibleForTesting final Handler mHandler; private final List<SensorManagerPlugin> mPlugins; - public AsyncSensorManager(SensorManager inner, PluginManager pluginManager) { - mInner = inner; + @Inject + public AsyncSensorManager(Context context, PluginManager pluginManager) { + this(context.getSystemService(SensorManager.class), pluginManager); + } + + @VisibleForTesting + AsyncSensorManager(SensorManager sensorManager, PluginManager pluginManager) { + mInner = sensorManager; mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); mSensorCache = mInner.getSensorList(Sensor.TYPE_ALL); diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java index b2cc2694916e..7bc96261782d 100644 --- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java @@ -17,6 +17,7 @@ package com.android.systemui.util.leak; import static com.android.internal.logging.MetricsLogger.VIEW_UNKNOWN; +import static com.android.systemui.Dependency.BG_LOOPER_NAME; import android.app.ActivityManager; import android.content.Context; @@ -51,6 +52,13 @@ import com.android.systemui.qs.tileimpl.QSTileImpl; import java.util.ArrayList; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +/** + */ +@Singleton public class GarbageMonitor { private static final boolean LEAK_REPORTING_ENABLED = Build.IS_DEBUGGABLE @@ -85,9 +93,12 @@ public class GarbageMonitor { private long mHeapLimit; + /** + */ + @Inject public GarbageMonitor( Context context, - Looper bgLooper, + @Named(BG_LOOPER_NAME) Looper bgLooper, LeakDetector leakDetector, LeakReporter leakReporter) { mContext = context.getApplicationContext(); diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java b/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java index a47e99d1e84d..b25df5f9c07f 100644 --- a/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java +++ b/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java @@ -16,6 +16,9 @@ package com.android.systemui.util.leak; +import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME; + +import android.annotation.Nullable; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; @@ -39,9 +42,14 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + /** * Dumps data to debug leaks and posts a notification to share the data. */ +@Singleton public class LeakReporter { static final String TAG = "LeakReporter"; @@ -56,7 +64,9 @@ public class LeakReporter { private final LeakDetector mLeakDetector; private final String mLeakReportEmail; - public LeakReporter(Context context, LeakDetector leakDetector, String leakReportEmail) { + @Inject + public LeakReporter(Context context, LeakDetector leakDetector, + @Nullable @Named(LEAK_REPORT_EMAIL_NAME) String leakReportEmail) { mContext = context; mLeakDetector = leakDetector; mLeakReportEmail = leakReportEmail; diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index e20e267336ea..a86970b90bff 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -67,6 +67,9 @@ import java.util.HashMap; import java.util.Map; import java.util.Objects; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Source of truth for all state / events related to the volume dialog. No presentation. * @@ -74,6 +77,7 @@ import java.util.Objects; * * Methods ending in "W" must be called on the worker thread. */ +@Singleton public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpable { private static final String TAG = Util.logTag(VolumeDialogControllerImpl.class); @@ -131,6 +135,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa protected final VC mVolumeController = new VC(); + @Inject public VolumeDialogControllerImpl(Context context) { mContext = context.getApplicationContext(); mNotificationManager = (NotificationManager) mContext.getSystemService( diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java index fb2ceac4b810..415060244243 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java @@ -107,6 +107,25 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { } @Test + public void onPluginConnected_showPluginBigClock() { + // GIVEN that the container for the big clock has visibility GONE + FrameLayout bigClockContainer = new FrameLayout(getContext()); + bigClockContainer.setVisibility(GONE); + mKeyguardClockSwitch.setBigClockContainer(bigClockContainer); + // AND the plugin returns a view for the big clock + ClockPlugin plugin = mock(ClockPlugin.class); + TextClock pluginView = new TextClock(getContext()); + when(plugin.getBigClockView()).thenReturn(pluginView); + PluginListener listener = mKeyguardClockSwitch.getClockPluginListener(); + // WHEN the plugin is connected + listener.onPluginConnected(plugin, null); + // THEN the big clock container is visible and it is the parent of the + // big clock view. + assertThat(bigClockContainer.getVisibility()).isEqualTo(VISIBLE); + assertThat(pluginView.getParent()).isEqualTo(bigClockContainer); + } + + @Test public void onPluginConnected_nullView() { ClockPlugin plugin = mock(ClockPlugin.class); PluginListener listener = mKeyguardClockSwitch.getClockPluginListener(); @@ -146,6 +165,26 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { } @Test + public void onPluginDisconnected_hidePluginBigClock() { + // GIVEN that the big clock container is visible + FrameLayout bigClockContainer = new FrameLayout(getContext()); + bigClockContainer.setVisibility(VISIBLE); + mKeyguardClockSwitch.setBigClockContainer(bigClockContainer); + // AND the plugin returns a view for the big clock + ClockPlugin plugin = mock(ClockPlugin.class); + TextClock pluginView = new TextClock(getContext()); + when(plugin.getBigClockView()).thenReturn(pluginView); + PluginListener listener = mKeyguardClockSwitch.getClockPluginListener(); + listener.onPluginConnected(plugin, null); + // WHEN the plugin is disconnected + listener.onPluginDisconnected(plugin); + // THEN the big lock container is GONE and the big clock view doesn't have + // a parent. + assertThat(bigClockContainer.getVisibility()).isEqualTo(GONE); + assertThat(pluginView.getParent()).isNull(); + } + + @Test public void onPluginDisconnected_nullView() { ClockPlugin plugin = mock(ClockPlugin.class); PluginListener listener = mKeyguardClockSwitch.getClockPluginListener(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java index bb445483c966..2582946333c0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java @@ -86,7 +86,7 @@ public class AppOpsControllerTest extends SysuiTestCase { mCallback); mController.onOpActiveChanged( AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); - mController.onOpNoted(AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, + mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED); verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); @@ -136,7 +136,7 @@ public class AppOpsControllerTest extends SysuiTestCase { TEST_UID, TEST_PACKAGE_NAME, true); mController.onOpActiveChanged(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, true); - mController.onOpNoted(AppOpsManager.OPSTR_FINE_LOCATION, + mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED); assertEquals(3, mController.getActiveAppOps().size()); } @@ -147,7 +147,7 @@ public class AppOpsControllerTest extends SysuiTestCase { TEST_UID, TEST_PACKAGE_NAME, true); mController.onOpActiveChanged(AppOpsManager.OP_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); - mController.onOpNoted(AppOpsManager.OPSTR_FINE_LOCATION, + mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED); assertEquals(2, mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID)).size()); @@ -158,7 +158,7 @@ public class AppOpsControllerTest extends SysuiTestCase { @Test public void opNotedScheduledForRemoval() { mController.setBGHandler(mMockHandler); - mController.onOpNoted(AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, + mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED); verify(mMockHandler).scheduleRemoval(any(AppOpItem.class), anyLong()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java index c1c80ce4a70a..7d94635ab548 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java @@ -19,13 +19,13 @@ package com.android.systemui.keyguard; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.app.AlarmManager; import android.content.ContentResolver; -import android.content.Intent; import android.net.Uri; import android.provider.Settings; import android.support.test.filters.SmallTest; @@ -40,6 +40,7 @@ import androidx.slice.SliceSpecs; import androidx.slice.builders.ListBuilder; import androidx.slice.core.SliceQuery; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; import org.junit.Assert; @@ -91,7 +92,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { @Test public void cleansDateFormat() { - mProvider.mIntentReceiver.onReceive(getContext(), new Intent(Intent.ACTION_TIMEZONE_CHANGED)); + mProvider.mKeyguardUpdateMonitorCallback.onTimeZoneChanged(null); TestableLooper.get(this).processAllMessages(); Assert.assertEquals("Date format should have been cleaned.", 1 /* expected */, mProvider.mCleanDateFormatInvokations); @@ -99,7 +100,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { @Test public void updatesClock() { - mProvider.mIntentReceiver.onReceive(getContext(), new Intent(Intent.ACTION_TIME_TICK)); + mProvider.mKeyguardUpdateMonitorCallback.onTimeChanged(); TestableLooper.get(this).processAllMessages(); verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null)); } @@ -171,6 +172,11 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { } @Override + public KeyguardUpdateMonitor getKeyguardUpdateMonitor() { + return mock(KeyguardUpdateMonitor.class); + } + + @Override protected String getFormattedDate() { return super.getFormattedDate() + mCounter++; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt index b23f667e4388..d3b3dae2c1cd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt @@ -51,4 +51,19 @@ class PrivacyDialogBuilderTest : SysuiTestCase() { assertEquals(listOf(Privacy.TYPE_CAMERA), typesList[1]) assertEquals(listOf(Privacy.TYPE_CAMERA), typesList[2]) } + + @Test + fun testOrder() { + // We want location to always go last, so it will go in the "+ other apps" + val appCamera = PrivacyItem(PrivacyType.TYPE_CAMERA, PrivacyApplication("Camera", context)) + val appMicrophone = + PrivacyItem(PrivacyType.TYPE_MICROPHONE, PrivacyApplication("Microphone", context)) + val appLocation = + PrivacyItem(PrivacyType.TYPE_LOCATION, PrivacyApplication("Location", context)) + + val items = listOf(appLocation, appMicrophone, appCamera) + val textBuilder = PrivacyDialogBuilder(context, items) + val appList = textBuilder.appsAndTypes.map { it.first }.map { it.packageName } + assertEquals(listOf("Camera", "Microphone", "Location"), appList) + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt index 563599b32d60..e6d7ee7407d4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt @@ -28,6 +28,7 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper import com.android.systemui.Dependency +import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.appops.AppOpItem import com.android.systemui.appops.AppOpsController @@ -56,6 +57,10 @@ class PrivacyItemControllerTest : SysuiTestCase() { companion object { val CURRENT_USER_ID = ActivityManager.getCurrentUser() + val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE + const val SYSTEM_UID = 1000 + const val TEST_PACKAGE_NAME = "test" + const val DEVICE_SERVICES_STRING = "Device services" const val TAG = "PrivacyItemControllerTest" fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() } @@ -81,6 +86,8 @@ class PrivacyItemControllerTest : SysuiTestCase() { mDependency.injectTestDependency(Dependency.BG_LOOPER, testableLooper.looper) mDependency.injectTestDependency(Dependency.MAIN_HANDLER, Handler(testableLooper.looper)) mContext.addMockSystemService(UserManager::class.java, userManager) + mContext.getOrCreateTestableResources().addOverride(R.string.device_services, + DEVICE_SERVICES_STRING) doReturn(listOf(object : UserInfo() { init { @@ -110,8 +117,8 @@ class PrivacyItemControllerTest : SysuiTestCase() { @Test fun testDistinctItems() { - doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, CURRENT_USER_ID, "", 0), - AppOpItem(AppOpsManager.OP_CAMERA, CURRENT_USER_ID, "", 1))) + doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0), + AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 1))) .`when`(appOpsController).getActiveAppOpsForUser(anyInt()) privacyItemController.setListening(true) @@ -121,6 +128,18 @@ class PrivacyItemControllerTest : SysuiTestCase() { } @Test + fun testSystemApps() { + doReturn(listOf(AppOpItem(AppOpsManager.OP_COARSE_LOCATION, SYSTEM_UID, TEST_PACKAGE_NAME, + 0))).`when`(appOpsController).getActiveAppOpsForUser(anyInt()) + privacyItemController.setListening(true) + testableLooper.processAllMessages() + verify(callback).privacyChanged(capture(argCaptor)) + assertEquals(1, argCaptor.value.size) + assertEquals(context.getString(R.string.device_services), + argCaptor.value[0].application.applicationName) + } + + @Test fun testRegisterReceiver_allUsers() { val spiedContext = spy(mContext) val itemController = PrivacyItemController(spiedContext, callback) diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index a9f4e46395f3..704974417f87 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -124,19 +124,9 @@ public class BackupManagerService { } } - /** - * Called through Trampoline from {@link Lifecycle#onUnlockUser(int)}. We run the heavy work on - * a background thread to keep the unlock time down. - */ - public void unlockSystemUser() { - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable"); - try { - sInstance.setBackupEnabled(readBackupEnableState(UserHandle.USER_SYSTEM)); - } catch (RemoteException e) { - // can't happen; it's a local object - } - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); - } + // --------------------------------------------- + // USER LIFECYCLE CALLBACKS + // --------------------------------------------- /** * Starts the backup service for user {@code userId} by creating a new instance of {@link @@ -152,10 +142,25 @@ public class BackupManagerService { /** * Starts the backup service for user {@code userId} by registering its instance of {@link - * UserBackupManagerService} with this service. + * UserBackupManagerService} with this service and setting enabled state. */ void startServiceForUser(int userId, UserBackupManagerService userBackupManagerService) { mServiceUsers.put(userId, userBackupManagerService); + + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable"); + try { + // TODO(b/121198604): Make enable file per-user and clean up indirection. + mTrampoline.setBackupEnabledForUser(userId, readBackupEnableState(userId)); + } catch (RemoteException e) { + // Can't happen, it's a local object. + } + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } + + /** Stops the backup service for user {@code userId} when the user is stopped. */ + @VisibleForTesting + protected void stopServiceForUser(int userId) { + mServiceUsers.remove(userId); } SparseArray<UserBackupManagerService> getServiceUsers() { @@ -171,7 +176,7 @@ public class BackupManagerService { * UserBackupManagerService}. * @param caller A {@link String} identifying the caller for logging purposes. * @throws SecurityException if {@code userId} is different from the calling user id and the - * caller does NOT have the android.permission.INTERACT_ACROSS_USERS_FULL permission. + * caller does NOT have the android.permission.INTERACT_ACROSS_USERS_FULL permission. */ @Nullable @VisibleForTesting @@ -821,10 +826,14 @@ public class BackupManagerService { @Override public void onUnlockUser(int userId) { if (userId == UserHandle.USER_SYSTEM) { - sInstance.initializeServiceAndUnlockSystemUser(); - } else { - sInstance.unlockUser(userId); + sInstance.initializeService(); } + sInstance.unlockUser(userId); + } + + @Override + public void onStopUser(int userId) { + sInstance.stopUser(userId); } } } diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java index d40368025c0c..8eb5207e4dca 100644 --- a/services/backup/java/com/android/server/backup/Trampoline.java +++ b/services/backup/java/com/android/server/backup/Trampoline.java @@ -150,61 +150,38 @@ public class Trampoline extends IBackupManager.Stub { } /** - * Initialize {@link BackupManagerService} if the backup service is not disabled. Only the - * system user can initialize the service. - */ - /* package */ void initializeService(int userId) { - if (mGlobalDisable) { - Slog.i(TAG, "Backup service not supported"); - return; - } - - if (userId != UserHandle.USER_SYSTEM) { - Slog.i(TAG, "Cannot initialize backup service for non-system user: " + userId); - return; - } - - synchronized (mStateLock) { - if (!mSuppressFile.exists()) { - mService = createBackupManagerService(); - } else { - Slog.i(TAG, "Backup service inactive"); - } - } - } - - /** * Called from {@link BackupManagerService.Lifecycle} when the system user is unlocked. Attempts - * to initialize {@link BackupManagerService} and set backup state for the system user. + * to initialize {@link BackupManagerService}. Offloads work onto the handler thread {@link + * #mHandlerThread} to keep unlock time low. */ - void initializeServiceAndUnlockSystemUser() { + void initializeService() { 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); + if (mGlobalDisable) { + Slog.i(TAG, "Backup service not supported"); + return; + } - // 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(); + synchronized (mStateLock) { + if (!mSuppressFile.exists()) { + mService = createBackupManagerService(); + } else { + Slog.i(TAG, "Backup service inactive"); + } } + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); }); } /** - * Called from {@link BackupManagerService.Lifecycle} when a non-system user {@code userId} is - * unlocked. Starts the backup service for this user if the service supports multi-user. - * Offloads work onto the handler thread {@link #mHandlerThread} to keep unlock time low. + * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is unlocked. + * Starts the backup service for this user if it's the system user or if the service supports + * multi-user. Offloads work onto the handler thread {@link #mHandlerThread} to keep unlock time + * low. */ - // TODO(b/120212806): Consolidate service start for system and non-system users when system - // user-only logic is removed. void unlockUser(int userId) { - if (!isMultiUserEnabled()) { + if (userId != UserHandle.USER_SYSTEM && !isMultiUserEnabled()) { Slog.i(TAG, "Multi-user disabled, cannot start service for user: " + userId); return; } @@ -221,6 +198,26 @@ public class Trampoline extends IBackupManager.Stub { } /** + * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is stopped. + * Offloads work onto the handler thread {@link #mHandlerThread} to keep stopping time low. + */ + void stopUser(int userId) { + if (userId != UserHandle.USER_SYSTEM && !isMultiUserEnabled()) { + Slog.i(TAG, "Multi-user disabled, cannot stop service for user: " + userId); + return; + } + + postToHandler( + () -> { + BackupManagerService service = mService; + if (service != null) { + Slog.i(TAG, "Stopping service for user: " + userId); + service.stopServiceForUser(userId); + } + }); + } + + /** * Only privileged callers should be changing the backup state. This method only acts on {@link * UserHandle#USER_SYSTEM} and is a no-op if passed non-system users. Deactivating backup in the * system user also deactivates backup in all users. diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index e8820ae4e32a..0b3fa0269bd3 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -20,7 +20,6 @@ import static android.Manifest.permission.MANAGE_CONTENT_CAPTURE; import static android.content.Context.CONTENT_CAPTURE_MANAGER_SERVICE; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManagerInternal; import android.content.ComponentName; @@ -34,7 +33,6 @@ import android.os.ShellCallback; import android.os.UserHandle; import android.os.UserManager; import android.util.Slog; -import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.IContentCaptureManager; import com.android.internal.annotations.GuardedBy; @@ -164,8 +162,7 @@ public final class ContentCaptureManagerService extends @Override public void startSession(@UserIdInt int userId, @NonNull IBinder activityToken, - @NonNull ComponentName componentName, @NonNull String sessionId, - @Nullable ContentCaptureContext clientContext, int flags, + @NonNull ComponentName componentName, @NonNull String sessionId, int flags, @NonNull IResultReceiver result) { Preconditions.checkNotNull(activityToken); Preconditions.checkNotNull(componentName); @@ -175,14 +172,13 @@ public final class ContentCaptureManagerService extends // so we don't pass it on startSession (same for Autofill) final int taskId = getAmInternal().getTaskIdForActivity(activityToken, false); - // TODO(b/111276913): get from AM as well + // TODO(b/121260224): get from AM as well final int displayId = 0; synchronized (mLock) { final ContentCapturePerUserService service = getServiceForUserLocked(userId); service.startSessionLocked(activityToken, componentName, taskId, displayId, - sessionId, Binder.getCallingUid(), clientContext, flags, - mAllowInstantService, result); + sessionId, Binder.getCallingUid(), flags, mAllowInstantService, result); } } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java index f21b0d810411..03257e3e8514 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -24,7 +24,6 @@ import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUC import android.Manifest; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.AppGlobals; import android.app.assist.AssistContent; @@ -39,7 +38,6 @@ import android.os.RemoteException; import android.service.contentcapture.SnapshotData; import android.util.ArrayMap; import android.util.Slog; -import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.ContentCaptureSession; import com.android.internal.annotations.GuardedBy; @@ -114,8 +112,8 @@ final class ContentCapturePerUserService @GuardedBy("mLock") public void startSessionLocked(@NonNull IBinder activityToken, @NonNull ComponentName componentName, int taskId, int displayId, - @NonNull String sessionId, int uid, @Nullable ContentCaptureContext clientContext, - int flags, boolean bindInstantServiceAllowed, @NonNull IResultReceiver clientReceiver) { + @NonNull String sessionId, int uid, int flags, boolean bindInstantServiceAllowed, + @NonNull IResultReceiver clientReceiver) { if (!isEnabledLocked()) { setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED, /* binder=*/ null); return; @@ -142,7 +140,7 @@ final class ContentCapturePerUserService final ContentCaptureServerSession newSession = new ContentCaptureServerSession(getContext(), mUserId, mLock, activityToken, this, serviceComponentName, componentName, taskId, - displayId, sessionId, uid, clientContext, flags, bindInstantServiceAllowed, + displayId, sessionId, uid, flags, bindInstantServiceAllowed, mMaster.verbose); if (mMaster.verbose) { Slog.v(TAG, "startSession(): new session for " diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java index ba98b95082f7..f59636b92278 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java @@ -16,7 +16,6 @@ package com.android.server.contentcapture; import android.annotation.NonNull; -import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.os.IBinder; @@ -56,8 +55,7 @@ final class ContentCaptureServerSession implements ContentCaptureServiceCallback 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 uid, - @Nullable ContentCaptureContext clientContext, int flags, + int taskId, int displayId, @NonNull String sessionId, int uid, int flags, boolean bindInstantServiceAllowed, boolean verbose) { mLock = lock; mActivityToken = activityToken; @@ -67,8 +65,8 @@ final class ContentCaptureServerSession implements ContentCaptureServiceCallback mRemoteService = new RemoteContentCaptureService(context, ContentCaptureService.SERVICE_INTERFACE, serviceComponentName, userId, this, bindInstantServiceAllowed, verbose); - mContentCaptureContext = new ContentCaptureContext(clientContext, appComponentName, taskId, - displayId, flags); + mContentCaptureContext = new ContentCaptureContext(/* clientContext= */ null, + appComponentName, taskId, displayId, flags); } /** diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index f027253651ee..3cdf09e967fe 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -4096,10 +4096,15 @@ public class AppOpsService extends IAppOpsService.Stub { private static String[] getPackagesForUid(int uid) { String[] packageNames = null; - try { - packageNames = AppGlobals.getPackageManager().getPackagesForUid(uid); - } catch (RemoteException e) { - /* ignore - local call */ + + // Very early during boot the package manager is not yet or not yet fully started. At this + // time there are no packages yet. + if (AppGlobals.getPackageManager() != null) { + try { + packageNames = AppGlobals.getPackageManager().getPackagesForUid(uid); + } catch (RemoteException e) { + /* ignore - local call */ + } } if (packageNames == null) { return EmptyArray.STRING; @@ -4269,9 +4274,8 @@ public class AppOpsService extends IAppOpsService.Stub { } @Override - public void setMode(int code, int uid, @NonNull String packageName, int mode, - boolean isPrivileged) { - AppOpsService.this.setMode(code, uid, packageName, mode, false, isPrivileged); + public void setUidMode(int code, int uid, int mode) { + AppOpsService.this.setUidMode(code, uid, mode); } } } diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index 08cb7a2f5047..121a830f05f5 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -87,6 +87,10 @@ import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; import com.android.server.am.BatteryStatsService; +import com.android.server.deviceidle.ConstraintController; +import com.android.server.deviceidle.DeviceIdleConstraintTracker; +import com.android.server.deviceidle.IDeviceIdleConstraint; +import com.android.server.deviceidle.TvConstraintController; import com.android.server.net.NetworkPolicyManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; @@ -104,6 +108,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.stream.Collectors; /** * Keeps track of device idleness and drives low power mode based on that. @@ -296,6 +301,17 @@ public class DeviceIdleController extends SystemService private Location mLastGpsLocation; // Current locked state of the screen private boolean mScreenLocked; + private int mNumBlockingConstraints = 0; + + /** + * Constraints are the "handbrakes" that stop the device from moving into a lower state until + * every one is released at the same time. + * + * @see #registerDeviceIdleConstraintInternal(IDeviceIdleConstraint, String, int) + */ + private final ArrayMap<IDeviceIdleConstraint, DeviceIdleConstraintTracker> + mConstraints = new ArrayMap<>(); + private ConstraintController mConstraintController; /** Device is currently active. */ @VisibleForTesting @@ -703,8 +719,7 @@ public class DeviceIdleController extends SystemService * global Settings. Any access to this class or its fields should be done while * holding the DeviceIdleController lock. */ - @VisibleForTesting - final class Constants extends ContentObserver { + public final class Constants extends ContentObserver { // Key names stored in the settings value. private static final String KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = "light_after_inactive_to"; @@ -1031,9 +1046,9 @@ public class DeviceIdleController extends SystemService INACTIVE_TIMEOUT = mParser.getDurationMillis(KEY_INACTIVE_TIMEOUT, !COMPRESS_TIME ? inactiveTimeoutDefault : (inactiveTimeoutDefault / 10)); SENSING_TIMEOUT = mParser.getDurationMillis(KEY_SENSING_TIMEOUT, - !DEBUG ? 4 * 60 * 1000L : 60 * 1000L); + !COMPRESS_TIME ? 4 * 60 * 1000L : 60 * 1000L); LOCATING_TIMEOUT = mParser.getDurationMillis(KEY_LOCATING_TIMEOUT, - !DEBUG ? 30 * 1000L : 15 * 1000L); + !COMPRESS_TIME ? 30 * 1000L : 15 * 1000L); LOCATION_ACCURACY = mParser.getFloat(KEY_LOCATION_ACCURACY, 20); MOTION_INACTIVE_TIMEOUT = mParser.getDurationMillis(KEY_MOTION_INACTIVE_TIMEOUT, !COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L); @@ -1228,6 +1243,7 @@ public class DeviceIdleController extends SystemService private static final int MSG_REPORT_MAINTENANCE_ACTIVITY = 7; private static final int MSG_FINISH_IDLE_OP = 8; private static final int MSG_REPORT_TEMP_APP_WHITELIST_CHANGED = 9; + private static final int MSG_SEND_CONSTRAINT_MONITORING = 10; final class MyHandler extends Handler { MyHandler(Looper looper) { @@ -1348,6 +1364,15 @@ public class DeviceIdleController extends SystemService final boolean added = (msg.arg2 == 1); mNetworkPolicyManagerInternal.onTempPowerSaveWhitelistChange(appId, added); } break; + case MSG_SEND_CONSTRAINT_MONITORING: { + final IDeviceIdleConstraint constraint = (IDeviceIdleConstraint) msg.obj; + final boolean monitoring = (msg.arg1 == 1); + if (monitoring) { + constraint.startMonitoring(); + } else { + constraint.stopMonitoring(); + } + } break; } } } @@ -1512,6 +1537,25 @@ public class DeviceIdleController extends SystemService } public class LocalService { + public void onConstraintStateChanged(IDeviceIdleConstraint constraint, boolean active) { + synchronized (DeviceIdleController.this) { + onConstraintStateChangedLocked(constraint, active); + } + } + + public void registerDeviceIdleConstraint(IDeviceIdleConstraint constraint, String name, + @IDeviceIdleConstraint.MinimumState int minState) { + registerDeviceIdleConstraintInternal(constraint, name, minState); + } + + public void unregisterDeviceIdleConstraint(IDeviceIdleConstraint constraint) { + unregisterDeviceIdleConstraintInternal(constraint); + } + + public void exitIdle(String reason) { + exitIdleInternal(reason); + } + // duration in milliseconds public void addPowerSaveTempWhitelistApp(int callingUid, String packageName, long duration, int userId, boolean sync, String reason) { @@ -1562,6 +1606,7 @@ public class DeviceIdleController extends SystemService static class Injector { private final Context mContext; private ConnectivityService mConnectivityService; + private Constants mConstants; private LocationManager mLocationManager; Injector(Context ctx) { @@ -1591,7 +1636,10 @@ public class DeviceIdleController extends SystemService Constants getConstants(DeviceIdleController controller, Handler handler, ContentResolver resolver) { - return controller.new Constants(handler, resolver); + if (mConstants == null) { + mConstants = controller.new Constants(handler, resolver); + } + return mConstants; } LocationManager getLocationManager() { @@ -1608,6 +1656,23 @@ public class DeviceIdleController extends SystemService PowerManager getPowerManager() { return mContext.getSystemService(PowerManager.class); } + + SensorManager getSensorManager() { + return mContext.getSystemService(SensorManager.class); + } + + ConstraintController getConstraintController(Handler handler, LocalService localService) { + if (mContext.getPackageManager() + .hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY)) { + return new TvConstraintController(mContext, handler); + } + return null; + } + + boolean useMotionSensor() { + return mContext.getResources().getBoolean( + com.android.internal.R.bool.config_autoPowerModeUseMotionSensor); + } } private final Injector mInjector; @@ -1632,9 +1697,7 @@ public class DeviceIdleController extends SystemService mHandler = mInjector.getHandler(this); mAppStateTracker = mInjector.getAppStateTracker(context, FgThread.get().getLooper()); LocalServices.addService(AppStateTracker.class, mAppStateTracker); - - mUseMotionSensor = context.getResources().getBoolean( - com.android.internal.R.bool.config_autoPowerModeUseMotionSensor); + mUseMotionSensor = mInjector.useMotionSensor(); } public DeviceIdleController(Context context) { @@ -1734,7 +1797,7 @@ public class DeviceIdleController extends SystemService mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface( ServiceManager.getService(Context.NETWORK_POLICY_SERVICE)); mNetworkPolicyManagerInternal = getLocalService(NetworkPolicyManagerInternal.class); - mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE); + mSensorManager = mInjector.getSensorManager(); if (mUseMotionSensor) { int sigMotionSensorId = getContext().getResources().getInteger( @@ -1763,6 +1826,12 @@ public class DeviceIdleController extends SystemService .setNumUpdates(1); } + mConstraintController = mInjector.getConstraintController( + mHandler, getLocalService(LocalService.class)); + if (mConstraintController != null) { + mConstraintController.start(); + } + float angleThreshold = getContext().getResources().getInteger( com.android.internal.R.integer.config_autoPowerModeThresholdAngle) / 100f; mAnyMotionDetector = mInjector.getAnyMotionDetector(mHandler, mSensorManager, this, @@ -1818,6 +1887,99 @@ public class DeviceIdleController extends SystemService } } + @VisibleForTesting + boolean hasMotionSensor() { + return mUseMotionSensor && mMotionSensor != null; + } + + private void registerDeviceIdleConstraintInternal(IDeviceIdleConstraint constraint, + final String name, final int type) { + final int minState; + switch (type) { + case IDeviceIdleConstraint.ACTIVE: + minState = STATE_ACTIVE; + break; + case IDeviceIdleConstraint.SENSING_OR_ABOVE: + minState = STATE_SENSING; + break; + default: + Slog.wtf(TAG, "Registering device-idle constraint with invalid type: " + type); + return; + } + synchronized (this) { + if (mConstraints.containsKey(constraint)) { + Slog.e(TAG, "Re-registering device-idle constraint: " + constraint + "."); + return; + } + DeviceIdleConstraintTracker tracker = new DeviceIdleConstraintTracker(name, minState); + mConstraints.put(constraint, tracker); + updateActiveConstraintsLocked(); + } + } + + private void unregisterDeviceIdleConstraintInternal(IDeviceIdleConstraint constraint) { + synchronized (this) { + // Artifically force the constraint to inactive to unblock anything waiting for it. + onConstraintStateChangedLocked(constraint, /* active= */ false); + + // Let the constraint know that we are not listening to it any more. + setConstraintMonitoringLocked(constraint, /* monitoring= */ false); + mConstraints.remove(constraint); + } + } + + @GuardedBy("this") + private void onConstraintStateChangedLocked(IDeviceIdleConstraint constraint, boolean active) { + DeviceIdleConstraintTracker tracker = mConstraints.get(constraint); + if (tracker == null) { + Slog.e(TAG, "device-idle constraint " + constraint + " has not been registered."); + return; + } + if (active != tracker.active && tracker.monitoring) { + tracker.active = active; + mNumBlockingConstraints += (tracker.active ? +1 : -1); + if (mNumBlockingConstraints == 0) { + if (mState == STATE_ACTIVE) { + becomeInactiveIfAppropriateLocked(); + } else if (mNextAlarmTime == 0 || mNextAlarmTime < SystemClock.elapsedRealtime()) { + stepIdleStateLocked("s:" + tracker.name); + } + } + } + } + + @GuardedBy("this") + private void setConstraintMonitoringLocked(IDeviceIdleConstraint constraint, boolean monitor) { + DeviceIdleConstraintTracker tracker = mConstraints.get(constraint); + if (tracker.monitoring != monitor) { + tracker.monitoring = monitor; + updateActiveConstraintsLocked(); + // We send the callback on a separate thread instead of just relying on oneway as + // the client could be in the system server with us and cause re-entry problems. + mHandler.obtainMessage(MSG_SEND_CONSTRAINT_MONITORING, + /* monitoring= */ monitor ? 1 : 0, + /* <not used>= */ -1, + /* constraint= */ constraint).sendToTarget(); + } + } + + @GuardedBy("this") + private void updateActiveConstraintsLocked() { + mNumBlockingConstraints = 0; + for (int i = 0; i < mConstraints.size(); i++) { + final IDeviceIdleConstraint constraint = mConstraints.keyAt(i); + final DeviceIdleConstraintTracker tracker = mConstraints.valueAt(i); + final boolean monitoring = (tracker.minState == mState); + if (monitoring != tracker.monitoring) { + setConstraintMonitoringLocked(constraint, monitoring); + tracker.active = monitoring; + } + if (tracker.monitoring && tracker.active) { + mNumBlockingConstraints++; + } + } + } + public boolean addPowerSaveWhitelistAppInternal(String name) { synchronized (this) { try { @@ -2448,6 +2610,7 @@ public class DeviceIdleController extends SystemService cancelLocatingLocked(); stopMonitoringMotionLocked(); mAnyMotionDetector.stop(); + updateActiveConstraintsLocked(); } private void resetLightIdleManagementLocked() { @@ -2583,40 +2746,50 @@ public class DeviceIdleController extends SystemService return; } + if (mNumBlockingConstraints != 0 && !mForceIdle) { + // We have some constraints from other parts of the system server preventing + // us from moving to the next state. + if (DEBUG) { + Slog.i(TAG, "Cannot step idle state. Blocked by: " + mConstraints.values().stream() + .filter(x -> x.active) + .map(x -> x.name) + .collect(Collectors.joining(","))); + } + return; + } + switch (mState) { case STATE_INACTIVE: // We have now been inactive long enough, it is time to start looking // for motion and sleep some more while doing so. startMonitoringMotionLocked(); scheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUT, false); - mState = STATE_IDLE_PENDING; - if (DEBUG) Slog.d(TAG, "Moved from STATE_INACTIVE to STATE_IDLE_PENDING."); - EventLogTags.writeDeviceIdle(mState, reason); + moveToStateLocked(STATE_IDLE_PENDING, reason); break; case STATE_IDLE_PENDING: - mState = STATE_SENSING; - if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE_PENDING to STATE_SENSING."); - EventLogTags.writeDeviceIdle(mState, reason); + moveToStateLocked(STATE_SENSING, reason); cancelLocatingLocked(); mLocated = false; mLastGenericLocation = null; mLastGpsLocation = null; + updateActiveConstraintsLocked(); - // If we have an accelerometer, wait to find out whether we are moving. + // Wait for open constraints and an accelerometer reading before moving on. if (mUseMotionSensor && mAnyMotionDetector.hasSensor()) { scheduleSensingTimeoutAlarmLocked(mConstants.SENSING_TIMEOUT); mNotMoving = false; mAnyMotionDetector.checkForAnyMotion(); break; + } else if (mNumBlockingConstraints != 0) { + cancelAlarmLocked(); + break; } mNotMoving = true; // Otherwise, fall through and check this off the list of requirements. case STATE_SENSING: cancelSensingTimeoutAlarmLocked(); - mState = STATE_LOCATING; - if (DEBUG) Slog.d(TAG, "Moved from STATE_SENSING to STATE_LOCATING."); - EventLogTags.writeDeviceIdle(mState, reason); + moveToStateLocked(STATE_LOCATING, reason); scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT, false); LocationManager locationManager = mInjector.getLocationManager(); if (locationManager != null @@ -2665,12 +2838,11 @@ public class DeviceIdleController extends SystemService if (mNextIdleDelay < mConstants.IDLE_TIMEOUT) { mNextIdleDelay = mConstants.IDLE_TIMEOUT; } - mState = STATE_IDLE; + moveToStateLocked(STATE_IDLE, reason); if (mLightState != LIGHT_STATE_OVERRIDE) { mLightState = LIGHT_STATE_OVERRIDE; cancelLightAlarmLocked(); } - EventLogTags.writeDeviceIdle(mState, reason); addEvent(EVENT_DEEP_IDLE, null); mGoingIdleWakeLock.acquire(); mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON); @@ -2688,14 +2860,24 @@ public class DeviceIdleController extends SystemService if (mNextIdlePendingDelay < mConstants.IDLE_PENDING_TIMEOUT) { mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT; } - mState = STATE_IDLE_MAINTENANCE; - EventLogTags.writeDeviceIdle(mState, reason); + moveToStateLocked(STATE_IDLE_MAINTENANCE, reason); addEvent(EVENT_DEEP_MAINTENANCE, null); mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF); break; } } + private void moveToStateLocked(int state, String reason) { + final int oldState = mState; + mState = state; + if (DEBUG) { + Slog.d(TAG, String.format("Moved from STATE_%s to STATE_%s.", + stateToString(oldState), stateToString(mState))); + } + EventLogTags.writeDeviceIdle(mState, reason); + updateActiveConstraintsLocked(); + } + void incActiveIdleOps() { synchronized (this) { mActiveIdleOpCount++; @@ -2818,6 +3000,7 @@ public class DeviceIdleController extends SystemService mMaintenanceStartTime = 0; EventLogTags.writeDeviceIdle(mState, type); becomeInactive = true; + updateActiveConstraintsLocked(); } if (mLightState == LIGHT_STATE_OVERRIDE) { // We went out of light idle mode because we had started deep idle mode... let's @@ -3794,8 +3977,22 @@ public class DeviceIdleController extends SystemService pw.print(" mScreenLocked="); pw.println(mScreenLocked); pw.print(" mNetworkConnected="); pw.println(mNetworkConnected); pw.print(" mCharging="); pw.println(mCharging); - pw.print(" mMotionActive="); pw.println(mMotionListener.active); + if (mConstraints.size() != 0) { + pw.println(" mConstraints={"); + for (int i = 0; i < mConstraints.size(); i++) { + final DeviceIdleConstraintTracker tracker = mConstraints.valueAt(i); + pw.print(" \""); pw.print(tracker.name); pw.print("\"="); + if (tracker.minState == mState) { + pw.println(tracker.active); + } else { + pw.print("ignored <mMinState="); pw.print(stateToString(tracker.minState)); + pw.println(">"); + } + } + pw.println(" }"); + } if (mUseMotionSensor) { + pw.print(" mMotionActive="); pw.println(mMotionListener.active); pw.print(" mNotMoving="); pw.println(mNotMoving); } pw.print(" mLocating="); pw.print(mLocating); pw.print(" mHasGps="); diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index d33b61757141..2346cfc07a83 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -47,6 +47,7 @@ import android.location.Address; import android.location.Criteria; import android.location.GeocoderParams; import android.location.Geofence; +import android.location.GnssMeasurementCorrections; import android.location.IBatchedLocationCallback; import android.location.IGnssMeasurementsListener; import android.location.IGnssNavigationMessageListener; @@ -2594,6 +2595,30 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override + public void injectGnssMeasurementCorrections( + GnssMeasurementCorrections measurementCorrections, String packageName) { + mContext.enforceCallingPermission( + android.Manifest.permission.LOCATION_HARDWARE, + "Location Hardware permission not granted to inject GNSS measurement corrections."); + if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) { + mGnssMeasurementsProvider.injectGnssMeasurementCorrections(measurementCorrections); + } else { + Slog.e(TAG, "Can not inject GNSS corrections due to no permission."); + } + } + + @Override + public int getGnssCapabilities(String packageName) { + mContext.enforceCallingPermission( + android.Manifest.permission.LOCATION_HARDWARE, + "Location Hardware permission not granted to obrain GNSS chipset capabilities."); + if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) { + return -1; + } + return mGnssMeasurementsProvider.getGnssCapabilities(); + } + + @Override public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) { if (mGnssMeasurementsProvider == null) { return; diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 7adcabac26ee..2a806447d605 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -129,6 +129,7 @@ import android.util.TimeUtils; import android.util.Xml; import com.android.internal.annotations.GuardedBy; +import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; import com.android.internal.os.AppFuseMount; import com.android.internal.os.BackgroundThread; @@ -1709,6 +1710,10 @@ class StorageManagerService extends IStorageManager.Stub ServiceManager.getService("package")); mIAppOpsService = IAppOpsService.Stub.asInterface( ServiceManager.getService(Context.APP_OPS_SERVICE)); + try { + mIAppOpsService.startWatchingMode(OP_REQUEST_INSTALL_PACKAGES, null, mAppOpsCallback); + } catch (RemoteException e) { + } mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget(); } @@ -3240,6 +3245,15 @@ class StorageManagerService extends IStorageManager.Stub } } + private IAppOpsCallback.Stub mAppOpsCallback = new IAppOpsCallback.Stub() { + @Override + public void opChanged(int op, int uid, String packageName) throws RemoteException { + if (!ENABLE_ISOLATED_STORAGE) return; + + remountUidExternalStorage(uid, getMountMode(uid, packageName)); + } + }; + private static final Pattern PATTERN_TRANSLATE = Pattern.compile( "(?i)^(/storage/[^/]+/(?:[0-9]+/)?)(.*)"); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 983ec4be328d..1a5dd90b918a 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -647,6 +647,11 @@ public class ActivityManagerService extends IActivityManager.Stub final ProcessStatsService mProcessStats; /** + * Service for compacting background apps. + */ + final AppCompactor mAppCompact; + + /** * Non-persistent appId whitelist for background restrictions */ int[] mBackgroundAppIdWhitelist = new int[] { @@ -796,11 +801,6 @@ public class ActivityManagerService extends IActivityManager.Stub */ final ArrayList<ProcessRecord> mPendingPssProcesses = new ArrayList<ProcessRecord>(); - /** - * Processes to compact. - */ - final ArrayList<ProcessRecord> mPendingCompactionProcesses = new ArrayList<ProcessRecord>(); - private boolean mBinderTransactionTrackingEnabled = false; /** @@ -1458,7 +1458,6 @@ public class ActivityManagerService extends IActivityManager.Stub final Handler mUiHandler; final ServiceThread mProcStartHandlerThread; final Handler mProcStartHandler; - final ServiceThread mCompactionThread; final ActivityManagerConstants mConstants; @@ -1796,11 +1795,6 @@ public class ActivityManagerService extends IActivityManager.Stub } }; - static final int COMPACT_PROCESS_SOME = 1; - static final int COMPACT_PROCESS_FULL = 2; - static final int COMPACT_PROCESS_MSG = 1; - final Handler mCompactionHandler; - static final int COLLECT_PSS_BG_MSG = 1; final Handler mBgHandler = new Handler(BackgroundThread.getHandler().getLooper()) { @@ -2236,8 +2230,7 @@ public class ActivityManagerService extends IActivityManager.Stub ? new PendingIntentController(handlerThread.getLooper(), mUserController) : null; mProcStartHandlerThread = null; mProcStartHandler = null; - mCompactionThread = null; - mCompactionHandler = null; + mAppCompact = null; mHiddenApiBlacklist = null; mFactoryTest = FACTORY_TEST_OFF; } @@ -2266,95 +2259,6 @@ public class ActivityManagerService extends IActivityManager.Stub mProcStartHandlerThread.start(); mProcStartHandler = new Handler(mProcStartHandlerThread.getLooper()); - mCompactionThread = new ServiceThread("CompactionThread", - THREAD_PRIORITY_FOREGROUND, true); - mCompactionThread.start(); - mCompactionHandler = new Handler(mCompactionThread.getLooper()) { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case COMPACT_PROCESS_MSG: { - long start = SystemClock.uptimeMillis(); - ProcessRecord proc; - int pid; - String action; - final String name; - int pendingAction, lastCompactAction; - long lastCompactTime; - synchronized(ActivityManagerService.this) { - proc = mPendingCompactionProcesses.remove(0); - - // don't compact if the process has returned to perceptible - if (proc.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) { - return; - } - - pid = proc.pid; - name = proc.processName; - pendingAction = proc.reqCompactAction; - lastCompactAction = proc.lastCompactAction; - lastCompactTime = proc.lastCompactTime; - } - if (pid == 0) { - // not a real process, either one being launched or one being killed - return; - } - - // basic throttling - if (pendingAction == COMPACT_PROCESS_SOME) { - // if we're compacting some, then compact if >10s after last full - // or >5s after last some - if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < 5000)) || - (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < 10000))) - return; - } else { - // if we're compacting full, then compact if >10s after last full - // or >.5s after last some - if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < 500)) || - (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < 10000))) - return; - } - - try { - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Compact " + - ((pendingAction == COMPACT_PROCESS_SOME) ? "some" : "full") + - ": " + name); - long[] rssBefore = Process.getRss(pid); - FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim"); - if (pendingAction == COMPACT_PROCESS_SOME) { - action = "file"; - } else { - action = "all"; - } - fos.write(action.getBytes()); - 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], 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; - } - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); - } catch (Exception e) { - // nothing to do, presumably the process died - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); - } - } - } - } - }; - - mConstants = new ActivityManagerConstants(this, mHandler); mProcessList.init(this); @@ -2409,6 +2313,8 @@ public class ActivityManagerService extends IActivityManager.Stub DisplayThread.get().getLooper()); mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class); + mAppCompact = new AppCompactor(this); + mProcessCpuThread = new Thread("CpuTracker") { @Override public void run() { @@ -2455,7 +2361,7 @@ public class ActivityManagerService extends IActivityManager.Stub try { Process.setThreadGroupAndCpuset(BackgroundThread.get().getThreadId(), Process.THREAD_GROUP_SYSTEM); - Process.setThreadGroupAndCpuset(mCompactionThread.getThreadId(), + Process.setThreadGroupAndCpuset(mAppCompact.mCompactionThread.getThreadId(), Process.THREAD_GROUP_SYSTEM); } catch (Exception e) { Slog.w(TAG, "Setting background thread cpuset failed"); @@ -17084,18 +16990,10 @@ public class ActivityManagerService extends IActivityManager.Stub if (app.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ && (app.curAdj == ProcessList.PREVIOUS_APP_ADJ || app.curAdj == ProcessList.HOME_APP_ADJ)) { - app.reqCompactAction = COMPACT_PROCESS_SOME; - mPendingCompactionProcesses.add(app); - mCompactionHandler.sendMessage( - mCompactionHandler.obtainMessage( - COMPACT_PROCESS_MSG, app.curAdj, app.setProcState)); + mAppCompact.compactAppSome(app); } 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.sendMessage( - mCompactionHandler.obtainMessage( - COMPACT_PROCESS_MSG, app.curAdj, app.setProcState)); + mAppCompact.compactAppFull(app); } } ProcessList.setOomAdj(app.pid, app.uid, app.curAdj); diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/AppCompactor.java new file mode 100644 index 000000000000..aee16c3b9ddc --- /dev/null +++ b/services/core/java/com/android/server/am/AppCompactor.java @@ -0,0 +1,187 @@ +/* + * 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.am; + +import com.android.internal.annotations.GuardedBy; + +import android.app.ActivityManager; + +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Process; +import android.os.SystemClock; +import android.os.Trace; + +import android.util.EventLog; +import android.util.StatsLog; + +import static android.os.Process.THREAD_PRIORITY_FOREGROUND; + +import com.android.server.ServiceThread; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; + +public final class AppCompactor { + /** + * Processes to compact. + */ + final ArrayList<ProcessRecord> mPendingCompactionProcesses = new ArrayList<ProcessRecord>(); + + /* + * This thread must be moved to the system background cpuset. + * If that doesn't happen, it's probably going to draw a lot of power. + * However, this has to happen after the first updateOomAdjLocked, because + * that will wipe out the cpuset assignment for system_server threads. + * Accordingly, this is in the AMS constructor. + */ + final ServiceThread mCompactionThread; + + static final int COMPACT_PROCESS_SOME = 1; + static final int COMPACT_PROCESS_FULL = 2; + static final int COMPACT_PROCESS_MSG = 1; + final Handler mCompactionHandler; + + final ActivityManagerService mAm; + final ActivityManagerConstants mConstants; + + public AppCompactor(ActivityManagerService am) { + mAm = am; + mConstants = am.mConstants; + + mCompactionThread = new ServiceThread("CompactionThread", + THREAD_PRIORITY_FOREGROUND, true); + mCompactionThread.start(); + mCompactionHandler = new MemCompactionHandler(this); + } + + // Must be called while holding AMS lock. + final void compactAppSome(ProcessRecord app) { + app.reqCompactAction = COMPACT_PROCESS_SOME; + mPendingCompactionProcesses.add(app); + mCompactionHandler.sendMessage( + mCompactionHandler.obtainMessage( + COMPACT_PROCESS_MSG, app.curAdj, app.setProcState)); + } + + // Must be called while holding AMS lock. + final void compactAppFull(ProcessRecord app) { + app.reqCompactAction = COMPACT_PROCESS_FULL; + mPendingCompactionProcesses.add(app); + mCompactionHandler.sendMessage( + mCompactionHandler.obtainMessage( + COMPACT_PROCESS_MSG, app.curAdj, app.setProcState)); + + } + final class MemCompactionHandler extends Handler { + AppCompactor mAc; + + private MemCompactionHandler(AppCompactor ac) { + super(ac.mCompactionThread.getLooper()); + mAc = ac; + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case COMPACT_PROCESS_MSG: { + long start = SystemClock.uptimeMillis(); + ProcessRecord proc; + int pid; + String action; + final String name; + int pendingAction, lastCompactAction; + long lastCompactTime; + synchronized(mAc.mAm) { + proc = mAc.mPendingCompactionProcesses.remove(0); + + // don't compact if the process has returned to perceptible + if (proc.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) { + return; + } + + pid = proc.pid; + name = proc.processName; + pendingAction = proc.reqCompactAction; + lastCompactAction = proc.lastCompactAction; + lastCompactTime = proc.lastCompactTime; + } + if (pid == 0) { + // not a real process, either one being launched or one being killed + return; + } + + // basic throttling + if (pendingAction == COMPACT_PROCESS_SOME) { + // if we're compacting some, then compact if >10s after last full + // or >5s after last some + if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < 5000)) || + (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < 10000))) { + return; + } + } else { + // if we're compacting full, then compact if >10s after last full + // or >.5s after last some + if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < 500)) || + (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < 10000))) { + return; + } + } + + try { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Compact " + + ((pendingAction == COMPACT_PROCESS_SOME) ? "some" : "full") + + ": " + name); + long[] rssBefore = Process.getRss(pid); + FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim"); + if (pendingAction == COMPACT_PROCESS_SOME) { + action = "file"; + } else { + action = "all"; + } + fos.write(action.getBytes()); + 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], 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(mAc.mAm) { + proc.lastCompactTime = end; + proc.lastCompactAction = pendingAction; + } + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } catch (Exception e) { + // nothing to do, presumably the process died + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } + } + } + } + } + + +} diff --git a/services/core/java/com/android/server/deviceidle/BluetoothConstraint.java b/services/core/java/com/android/server/deviceidle/BluetoothConstraint.java new file mode 100644 index 000000000000..cc319bf23ddd --- /dev/null +++ b/services/core/java/com/android/server/deviceidle/BluetoothConstraint.java @@ -0,0 +1,135 @@ +/* + * 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.deviceidle; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; +import android.bluetooth.BluetoothProfile; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.os.Message; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.DeviceIdleController; + +/** + * Track whether there are any active Bluetooth devices connected. + */ +public class BluetoothConstraint implements IDeviceIdleConstraint { + private static final String TAG = BluetoothConstraint.class.getSimpleName(); + private static final long INACTIVITY_TIMEOUT_MS = 20 * 60 * 1000L; + + private final Context mContext; + private final Handler mHandler; + private final DeviceIdleController.LocalService mLocalService; + private final BluetoothManager mBluetoothManager; + + private volatile boolean mConnected = true; + private volatile boolean mMonitoring = false; + + public BluetoothConstraint( + Context context, Handler handler, DeviceIdleController.LocalService localService) { + mContext = context; + mHandler = handler; + mLocalService = localService; + mBluetoothManager = mContext.getSystemService(BluetoothManager.class); + } + + @Override + public synchronized void startMonitoring() { + // Start by assuming we have a connected bluetooth device. + mConnected = true; + mMonitoring = true; + + // Register a receiver to get updates on bluetooth devices disconnecting or the + // adapter state changing. + IntentFilter filter = new IntentFilter(); + filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); + filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); + filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); + mContext.registerReceiver(mReceiver, filter); + + // Some devices will try to stay connected indefinitely. Set a timeout to ignore them. + mHandler.sendMessageDelayed( + Message.obtain(mHandler, mTimeoutCallback), INACTIVITY_TIMEOUT_MS); + + // Now we have the receiver registered, make a direct check for connected devices. + updateAndReportActiveLocked(); + } + + @Override + public synchronized void stopMonitoring() { + mContext.unregisterReceiver(mReceiver); + mHandler.removeCallbacks(mTimeoutCallback); + mMonitoring = false; + } + + private synchronized void cancelMonitoringDueToTimeout() { + if (mMonitoring) { + mMonitoring = false; + mLocalService.onConstraintStateChanged(this, /* active= */ false); + } + } + + /** + * Check the latest data from BluetoothManager and let DeviceIdleController know whether we + * have connected devices (for example TV remotes / gamepads) and thus want to stay awake. + */ + @GuardedBy("this") + private void updateAndReportActiveLocked() { + final boolean connected = isBluetoothConnected(mBluetoothManager); + if (connected != mConnected) { + mConnected = connected; + // If we lost all of our connections, we are on track to going into idle state. + mLocalService.onConstraintStateChanged(this, /* active= */ mConnected); + } + } + + /** + * True if the bluetooth adapter exists, is enabled, and has at least one GATT device connected. + */ + @VisibleForTesting + static boolean isBluetoothConnected(BluetoothManager bluetoothManager) { + BluetoothAdapter adapter = bluetoothManager.getAdapter(); + if (adapter != null && adapter.isEnabled()) { + return bluetoothManager.getConnectedDevices(BluetoothProfile.GATT).size() > 0; + } + return false; + } + + /** + * Registered in {@link #startMonitoring()}, unregistered in {@link #stopMonitoring()}. + */ + @VisibleForTesting + final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(intent.getAction())) { + mLocalService.exitIdle("bluetooth"); + } else { + updateAndReportActiveLocked(); + } + } + }; + + private final Runnable mTimeoutCallback = () -> cancelMonitoringDueToTimeout(); +} diff --git a/services/core/java/com/android/server/deviceidle/ConstraintController.java b/services/core/java/com/android/server/deviceidle/ConstraintController.java new file mode 100644 index 000000000000..6d52f7188d99 --- /dev/null +++ b/services/core/java/com/android/server/deviceidle/ConstraintController.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.deviceidle; + +/** + * Device idle constraints for a specific form factor or use-case. + */ +public interface ConstraintController { + /** + * Begin any general continuing work and register all constraints. + */ + void start(); + + /** + * Unregister all constraints and stop any general work. + */ + void stop(); +} diff --git a/services/core/java/com/android/server/deviceidle/DeviceIdleConstraintTracker.java b/services/core/java/com/android/server/deviceidle/DeviceIdleConstraintTracker.java new file mode 100644 index 000000000000..4d5760ef9c86 --- /dev/null +++ b/services/core/java/com/android/server/deviceidle/DeviceIdleConstraintTracker.java @@ -0,0 +1,57 @@ +/* + * 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.deviceidle; + +/** + * Current state of an {@link IDeviceIdleConstraint}. + * + * If the current doze state is between leastActive and mostActive, then startMonitoring() will + * be the most recent call. Otherwise, stopMonitoring() is the most recent call. + */ +public class DeviceIdleConstraintTracker { + + /** + * Appears in "dumpsys deviceidle". + */ + public final String name; + + /** + * Whenever a constraint is active, it will keep the device at or above + * minState (provided the rule is currently in effect). + * + */ + public final int minState; + + /** + * Whether this constraint currently prevents going below {@link #minState}. + * + * When the state is set to exactly minState, active is automatically + * overwritten with {@code true}. + */ + public boolean active = false; + + /** + * Internal tracking for whether the {@link IDeviceIdleConstraint} on the other + * side has been told it needs to send updates. + */ + public boolean monitoring = false; + + public DeviceIdleConstraintTracker(final String name, int minState) { + this.name = name; + this.minState = minState; + } +} diff --git a/services/core/java/com/android/server/deviceidle/IDeviceIdleConstraint.java b/services/core/java/com/android/server/deviceidle/IDeviceIdleConstraint.java new file mode 100644 index 000000000000..f1f957307716 --- /dev/null +++ b/services/core/java/com/android/server/deviceidle/IDeviceIdleConstraint.java @@ -0,0 +1,67 @@ +/* + * 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.deviceidle; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Implemented by OEM and/or Form Factor. System ones are built into the + * image regardless of build flavour but may still be switched off at run time. + * Individual feature flags at build time control which are used. We may + * also explore a local override for quick testing. + */ +public interface IDeviceIdleConstraint { + + /** + * A state for this constraint to block descent from. + * + * <p>These states are a subset of the states in DeviceIdleController that make sense for + * constraints to be able to block on. For example, {@link #SENSING_OR_ABOVE} clearly has + * defined "above" and "below" states. However, a hypothetical {@code QUICK_DOZE_OR_ABOVE} + * state would not have clear semantics as to what transitions should be blocked and which + * should be allowed. + */ + @IntDef(flag = false, value = { + ACTIVE, + SENSING_OR_ABOVE, + }) + @Retention(RetentionPolicy.SOURCE) + @interface MinimumState {} + + int ACTIVE = 0; + int SENSING_OR_ABOVE = 1; + + /** + * Begin tracking events for this constraint. + * + * <p>The device idle controller has reached a point where it is waiting for the all-clear + * from this tracker (possibly among others) in order to continue with progression into + * idle state. It will not proceed until one of the following happens: + * <ul> + * <li>The constraint reports inactive with {@code .setActive(false)}.</li> + * <li>The constraint is unregistered with {@code .unregisterDeviceIdleConstraint(this)}.</li> + * <li>A transition timeout in DeviceIdleController fires. + * </ul> + */ + void startMonitoring(); + + /** Stop checking for new events and do not call into LocalService with updates any more. */ + void stopMonitoring(); +} diff --git a/services/core/java/com/android/server/deviceidle/TvConstraintController.java b/services/core/java/com/android/server/deviceidle/TvConstraintController.java new file mode 100644 index 000000000000..2d472de6f5e1 --- /dev/null +++ b/services/core/java/com/android/server/deviceidle/TvConstraintController.java @@ -0,0 +1,66 @@ +/* + * 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.deviceidle; + +import android.annotation.Nullable; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Handler; + +import com.android.server.DeviceIdleController; +import com.android.server.LocalServices; + +/** + * Device idle constraints for television devices. + * + * <p>Televisions are devices with {@code FEATURE_LEANBACK_ONLY}. Other devices might support + * some kind of leanback mode but they should not follow the same rules for idle state. + */ +public class TvConstraintController implements ConstraintController { + private final Context mContext; + private final Handler mHandler; + private final DeviceIdleController.LocalService mDeviceIdleService; + + @Nullable + private final BluetoothConstraint mBluetoothConstraint; + + public TvConstraintController(Context context, Handler handler) { + mContext = context; + mHandler = handler; + mDeviceIdleService = LocalServices.getService(DeviceIdleController.LocalService.class); + + final PackageManager pm = context.getPackageManager(); + mBluetoothConstraint = pm.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH) + ? new BluetoothConstraint(mContext, mHandler, mDeviceIdleService) + : null; + } + + @Override + public void start() { + if (mBluetoothConstraint != null) { + mDeviceIdleService.registerDeviceIdleConstraint( + mBluetoothConstraint, "bluetooth", IDeviceIdleConstraint.SENSING_OR_ABOVE); + } + } + + @Override + public void stop() { + if (mBluetoothConstraint != null) { + mDeviceIdleService.unregisterDeviceIdleConstraint(mBluetoothConstraint); + } + } +} diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java index 8e3f35111268..918dc07835be 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java @@ -38,11 +38,11 @@ import android.util.Slog; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; import android.view.textservice.SpellCheckerInfo; -import android.view.textservice.TextServicesManager; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.inputmethod.StartInputFlags; +import com.android.server.textservices.TextServicesManagerInternal; import java.util.ArrayList; import java.util.Arrays; @@ -641,7 +641,7 @@ final class InputMethodUtils { } // Only the current spell checker should be treated as an enabled one. final SpellCheckerInfo currentSpellChecker = - TextServicesManager.getInstance().getCurrentSpellChecker(); + TextServicesManagerInternal.get().getCurrentSpellCheckerForUser(userId); for (final String packageName : systemImesDisabledUntilUsed) { if (DEBUG) { Slog.d(TAG, "check " + packageName); diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 0f060fe09318..9c6cb20b88b6 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -170,7 +170,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private static final int GPS_CAPABILITY_SINGLE_SHOT = 0x0000008; private static final int GPS_CAPABILITY_ON_DEMAND_TIME = 0x0000010; private static final int GPS_CAPABILITY_GEOFENCING = 0x0000020; - private static final int GPS_CAPABILITY_MEASUREMENTS = 0x0000040; + public static final int GPS_CAPABILITY_MEASUREMENTS = 0x0000040; private static final int GPS_CAPABILITY_NAV_MESSAGES = 0x0000080; // The AGPS SUPL mode @@ -1607,20 +1607,20 @@ public class GnssLocationProvider extends AbstractLocationProvider implements @NativeEntryPoint private void setEngineCapabilities(final int capabilities) { // send to handler thread for fast native return, and in-order handling - mHandler.post(() -> { - mEngineCapabilities = capabilities; + mHandler.post( + () -> { + mEngineCapabilities = capabilities; - if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) { - mNtpTimeHelper.enablePeriodicTimeInjection(); - requestUtcTime(); - } + if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) { + mNtpTimeHelper.enablePeriodicTimeInjection(); + requestUtcTime(); + } - mGnssMeasurementsProvider.onCapabilitiesUpdated(hasCapability( - GPS_CAPABILITY_MEASUREMENTS)); - mGnssNavigationMessageProvider.onCapabilitiesUpdated(hasCapability( - GPS_CAPABILITY_NAV_MESSAGES)); - restartRequests(); - }); + mGnssMeasurementsProvider.onCapabilitiesUpdated(capabilities); + mGnssNavigationMessageProvider.onCapabilitiesUpdated( + hasCapability(GPS_CAPABILITY_NAV_MESSAGES)); + restartRequests(); + }); } private void restartRequests() { diff --git a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java index 3e2ba87a033e..77dee8241bea 100644 --- a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java +++ b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java @@ -17,6 +17,7 @@ package com.android.server.location; import android.content.Context; +import android.location.GnssMeasurementCorrections; import android.location.GnssMeasurementsEvent; import android.location.IGnssMeasurementsListener; import android.os.Handler; @@ -27,14 +28,13 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; /** - * An base implementation for GPS measurements provider. - * It abstracts out the responsibility of handling listeners, while still allowing technology - * specific implementations to be built. + * An base implementation for GPS measurements provider. It abstracts out the responsibility of + * handling listeners, while still allowing technology specific implementations to be built. * * @hide */ -public abstract class GnssMeasurementsProvider extends - RemoteListenerHelper<IGnssMeasurementsListener> { +public abstract class GnssMeasurementsProvider + extends RemoteListenerHelper<IGnssMeasurementsListener> { private static final String TAG = "GnssMeasurementsProvider"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -42,19 +42,19 @@ public abstract class GnssMeasurementsProvider extends private boolean mIsCollectionStarted; private boolean mEnableFullTracking; + private int mGnssEngineCapabilities; protected GnssMeasurementsProvider(Context context, Handler handler) { this(context, handler, new GnssMeasurementProviderNative()); } @VisibleForTesting - GnssMeasurementsProvider(Context context, Handler handler, - GnssMeasurementProviderNative aNative) { + GnssMeasurementsProvider( + Context context, Handler handler, GnssMeasurementProviderNative aNative) { super(context, handler, TAG); mNative = aNative; } - // TODO(b/37460011): Use this with death recovery logic. void resumeIfStarted() { if (DEBUG) { Log.d(TAG, "resumeIfStarted"); @@ -87,6 +87,25 @@ public abstract class GnssMeasurementsProvider extends } } + /** + * Injects GNSS measurement corrections into the GNSS chipset. + * + * @param measurementCorrections a {@link GnssMeasurementCorrections} object with the GNSS + * measurement corrections to be injected into the GNSS chipset. + */ + public void injectGnssMeasurementCorrections( + GnssMeasurementCorrections measurementCorrections) { + mHandler.post( + new Runnable() { + @Override + public void run() { + if (!mNative.injectGnssMeasurementCorrections(measurementCorrections)) { + Log.e(TAG, "Failure in injecting GNSS corrections."); + } + } + }); + } + @Override protected void unregisterFromService() { boolean stopped = mNative.stopMeasurementCollection(); @@ -96,20 +115,31 @@ public abstract class GnssMeasurementsProvider extends } public void onMeasurementsAvailable(final GnssMeasurementsEvent event) { - foreach((IGnssMeasurementsListener listener, int uid, String packageName) -> { - if (!hasPermission(uid, packageName)) { - logPermissionDisabledEventNotReported(TAG, packageName, "GNSS measurements"); - return; - } - listener.onGnssMeasurementsReceived(event); - }); + foreach( + (IGnssMeasurementsListener listener, int uid, String packageName) -> { + if (!hasPermission(uid, packageName)) { + logPermissionDisabledEventNotReported( + TAG, packageName, "GNSS measurements"); + return; + } + listener.onGnssMeasurementsReceived(event); + }); } - public void onCapabilitiesUpdated(boolean isGnssMeasurementsSupported) { + /** Updates the framework about the capabilities of the GNSS chipset */ + public void onCapabilitiesUpdated(int capabilities) { + mGnssEngineCapabilities = capabilities; + boolean isGnssMeasurementsSupported = + (capabilities & GnssLocationProvider.GPS_CAPABILITY_MEASUREMENTS) != 0; setSupported(isGnssMeasurementsSupported); updateResult(); } + /** Obtains the GNSS engine capabilities. */ + public int getGnssCapabilities() { + return mGnssEngineCapabilities; + } + public void onGpsEnabledChanged() { tryUpdateRegistrationWithService(); updateResult(); @@ -170,6 +200,11 @@ public abstract class GnssMeasurementsProvider extends public boolean stopMeasurementCollection() { return native_stop_measurement_collection(); } + + public boolean injectGnssMeasurementCorrections( + GnssMeasurementCorrections measurementCorrections) { + return native_inject_gnss_measurement_corrections(measurementCorrections); + } } private static native boolean native_is_measurement_supported(); @@ -177,4 +212,7 @@ public abstract class GnssMeasurementsProvider extends private static native boolean native_start_measurement_collection(boolean enableFullTracking); private static native boolean native_stop_measurement_collection(); + + private static native boolean native_inject_gnss_measurement_corrections( + GnssMeasurementCorrections measurementCorrections); } diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java index 37d43fc1da69..e69b2ec28ebc 100644 --- a/services/core/java/com/android/server/location/RemoteListenerHelper.java +++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java @@ -43,7 +43,7 @@ abstract class RemoteListenerHelper<TListener extends IInterface> { protected static final int RESULT_UNKNOWN = 5; protected static final int RESULT_NOT_ALLOWED = 6; - private final Handler mHandler; + protected final Handler mHandler; private final String mTag; private final Map<IBinder, LinkedListener> mListenerMap = new HashMap<>(); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 2359c2a02863..d961bad1cf59 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -4939,7 +4939,7 @@ public class NotificationManagerService extends SystemService { Slog.e(TAG, "Not posting notification without small icon: " + notification); if (old != null && !old.isCanceled) { mListeners.notifyRemovedLocked(r, - NotificationListenerService.REASON_ERROR, null); + NotificationListenerService.REASON_ERROR, r.getStats()); mHandler.post(new Runnable() { @Override public void run() { diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 0d0db9457672..35ffe8d5fa95 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -1065,6 +1065,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (isStaged()) { // STOPSHIP: implement staged sessions mStagedSessionReady = true; + mPm.sendSessionUpdatedBroadcast(generateInfo(), userId); dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null); return; } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 6453db5db1b6..af1263321d5f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -325,7 +325,6 @@ import dalvik.system.VMRuntime; import libcore.io.IoUtils; import libcore.util.EmptyArray; -import libcore.util.HexEncoding; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -2225,9 +2224,7 @@ public class PackageManagerService extends IPackageManager.Stub for (int i = 0; i < builtInLibCount; i++) { String name = libConfig.keyAt(i); SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i); - addSharedLibraryLPw(entry.filename, null, null, name, - SharedLibraryInfo.VERSION_UNDEFINED, SharedLibraryInfo.TYPE_BUILTIN, - PLATFORM_PACKAGE_NAME, 0); + addBuiltInSharedLibraryLocked(entry.filename, name); } // Now that we have added all the libraries, iterate again to add dependency @@ -8744,8 +8741,15 @@ public class PackageManagerService extends IPackageManager.Stub if (scanResult.success) { synchronized (mPackages) { try { + final Map<String, ReconciledPackage> reconcileResult = + reconcilePackagesLocked(new ReconcileRequest( + Collections.singletonMap(scanResult.pkgSetting.name, + scanResult), + mSharedLibraries, + mPackages)); prepareScanResultLocked(scanResult); - commitScanResultLocked(scanResult); + commitReconciledScanResultLocked( + reconcileResult.get(scanResult.pkgSetting.name)); } catch (PackageManagerException e) { unprepareScanResultLocked(scanResult); throw e; @@ -9352,8 +9356,26 @@ public class PackageManagerService extends IPackageManager.Stub } } - private @Nullable SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) { - LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name); + @Nullable + private SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) { + return getSharedLibraryInfo(name, version, mSharedLibraries, null); + } + + @Nullable + private static SharedLibraryInfo getSharedLibraryInfo(String name, long version, + Map<String, LongSparseArray<SharedLibraryInfo>> existingLibraries, + @Nullable Map<String, LongSparseArray<SharedLibraryInfo>> newLibraries) { + if (newLibraries != null) { + final LongSparseArray<SharedLibraryInfo> versionedLib = newLibraries.get(name); + SharedLibraryInfo info = null; + if (versionedLib != null) { + info = versionedLib.get(version); + } + if (info != null) { + return info; + } + } + final LongSparseArray<SharedLibraryInfo> versionedLib = existingLibraries.get(name); if (versionedLib == null) { return null; } @@ -9681,35 +9703,50 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mPackages") private void updateSharedLibrariesLPr(PackageParser.Package pkg, PackageParser.Package changingLib) throws PackageManagerException { + final ArrayList<SharedLibraryInfo> sharedLibraryInfos = + collectSharedLibraryInfos(pkg, Collections.unmodifiableMap(mPackages), + mSharedLibraries, null); + executeSharedLibrariesUpdateLPr(pkg, changingLib, sharedLibraryInfos); + } + + private static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(PackageParser.Package pkg, + Map<String, PackageParser.Package> availablePackages, + @NonNull final Map<String, LongSparseArray<SharedLibraryInfo>> existingLibraries, + @Nullable final Map<String, LongSparseArray<SharedLibraryInfo>> newLibraries) + throws PackageManagerException { if (pkg == null) { - return; + return null; } - - // If the package provides libraries, clear their old dependencies. - // This method will set them up again. - applyDefiningSharedLibraryUpdateLocked(pkg, null, (definingLibrary, dependency) -> { - definingLibrary.clearDependencies(); - }); // The collection used here must maintain the order of addition (so // that libraries are searched in the correct order) and must have no // duplicates. ArrayList<SharedLibraryInfo> usesLibraryInfos = null; if (pkg.usesLibraries != null) { - usesLibraryInfos = addSharedLibrariesLPw(pkg.usesLibraries, - null, null, pkg.packageName, true, - pkg.applicationInfo.targetSdkVersion, null); + usesLibraryInfos = collectSharedLibraryInfos(pkg.usesLibraries, null, null, + pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, null, + availablePackages, existingLibraries, newLibraries); } if (pkg.usesStaticLibraries != null) { - usesLibraryInfos = addSharedLibrariesLPw(pkg.usesStaticLibraries, + usesLibraryInfos = collectSharedLibraryInfos(pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions, pkg.usesStaticLibrariesCertDigests, - pkg.packageName, true, - pkg.applicationInfo.targetSdkVersion, usesLibraryInfos); + pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, usesLibraryInfos, + availablePackages, existingLibraries, newLibraries); } if (pkg.usesOptionalLibraries != null) { - usesLibraryInfos = addSharedLibrariesLPw(pkg.usesOptionalLibraries, - null, null, pkg.packageName, false, - pkg.applicationInfo.targetSdkVersion, usesLibraryInfos); + usesLibraryInfos = collectSharedLibraryInfos(pkg.usesOptionalLibraries, + null, null, pkg.packageName, false, pkg.applicationInfo.targetSdkVersion, + usesLibraryInfos, availablePackages, existingLibraries, newLibraries); } + return usesLibraryInfos; + } + + private void executeSharedLibrariesUpdateLPr(PackageParser.Package pkg, + PackageParser.Package changingLib, ArrayList<SharedLibraryInfo> usesLibraryInfos) { + // If the package provides libraries, clear their old dependencies. + // This method will set them up again. + applyDefiningSharedLibraryUpdateLocked(pkg, null, (definingLibrary, dependency) -> { + definingLibrary.clearDependencies(); + }); if (usesLibraryInfos != null) { pkg.usesLibraryInfos = usesLibraryInfos; // Use LinkedHashSet to preserve the order of files added to @@ -9726,18 +9763,22 @@ public class PackageManagerService extends IPackageManager.Stub } @GuardedBy("mPackages") - private ArrayList<SharedLibraryInfo> addSharedLibrariesLPw( + private static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos( @NonNull List<String> requestedLibraries, @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests, @NonNull String packageName, boolean required, int targetSdk, - @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries) + @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries, + @NonNull final Map<String, PackageParser.Package> availablePackages, + @NonNull final Map<String, LongSparseArray<SharedLibraryInfo>> existingLibraries, + @Nullable final Map<String, LongSparseArray<SharedLibraryInfo>> newLibraries) throws PackageManagerException { final int libCount = requestedLibraries.size(); for (int i = 0; i < libCount; i++) { final String libName = requestedLibraries.get(i); final long libVersion = requiredVersions != null ? requiredVersions[i] : SharedLibraryInfo.VERSION_UNDEFINED; - final SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(libName, libVersion); + final SharedLibraryInfo libraryInfo = getSharedLibraryInfo(libName, libVersion, + existingLibraries, newLibraries); if (libraryInfo == null) { if (required) { throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY, @@ -9757,7 +9798,8 @@ public class PackageManagerService extends IPackageManager.Stub + libraryInfo.getLongVersion() + "; failing!"); } - PackageParser.Package libPkg = mPackages.get(libraryInfo.getPackageName()); + PackageParser.Package libPkg = + availablePackages.get(libraryInfo.getPackageName()); if (libPkg == null) { throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY, "Package " + packageName + " requires unavailable static shared" @@ -9930,15 +9972,24 @@ public class PackageManagerService extends IPackageManager.Stub @Nullable public final PackageSetting pkgSetting; /** ABI code paths that have changed in the package scan */ @Nullable public final List<String> changedAbiCodePath; + + public final SharedLibraryInfo staticSharedLibraryInfo; + + public final List<SharedLibraryInfo> dynamicSharedLibraryInfos; + public ScanResult( ScanRequest request, boolean success, @Nullable PackageSetting pkgSetting, - @Nullable List<String> changedAbiCodePath, boolean existingSettingCopied) { + @Nullable List<String> changedAbiCodePath, boolean existingSettingCopied, + SharedLibraryInfo staticSharedLibraryInfo, + List<SharedLibraryInfo> dynamicSharedLibraryInfos) { this.request = request; this.success = success; this.pkgSetting = pkgSetting; this.changedAbiCodePath = changedAbiCodePath; this.existingSettingCopied = existingSettingCopied; + this.staticSharedLibraryInfo = staticSharedLibraryInfo; + this.dynamicSharedLibraryInfos = dynamicSharedLibraryInfos; } } @@ -10136,29 +10187,6 @@ public class PackageManagerService extends IPackageManager.Stub } - private void commitSuccessfulScanResults(@NonNull List<ScanResult> results) - throws PackageManagerException { - synchronized(mPackages) { - for (ScanResult result : results) { - // failures should have been caught earlier, but in case it wasn't, - // let's double check - if (!result.success) { - throw new PackageManagerException( - "Scan failed for " + result.request.pkg.packageName); - } - } - for (ScanResult result : results) { - try { - prepareScanResultLocked(result); - commitScanResultLocked(result); - } catch (PackageManagerException e) { - unprepareScanResultLocked(result); - throw e; - } - } - } - } - /** Prepares the system to commit a {@link ScanResult} in a way that will not fail. */ private void prepareScanResultLocked(@NonNull ScanResult result) throws PackageManagerException { @@ -10191,7 +10219,9 @@ public class PackageManagerService extends IPackageManager.Stub * possible and the system is not left in an inconsistent state. */ @GuardedBy({"mPackages", "mInstallLock"}) - private void commitScanResultLocked(@NonNull ScanResult result) throws PackageManagerException { + private void commitReconciledScanResultLocked(@NonNull ReconciledPackage reconciledPkg) + throws PackageManagerException { + final ScanResult result = reconciledPkg.scanResult; final ScanRequest request = result.request; final PackageParser.Package pkg = request.pkg; final PackageParser.Package oldPkg = request.oldPkg; @@ -10235,19 +10265,8 @@ public class PackageManagerService extends IPackageManager.Stub mTransferedPackages.add(pkg.packageName); } - // THROWS: when requested libraries that can't be found. it only changes - // the state of the passed in pkg object, so, move to the top of the method - // and allow it to abort - if ((scanFlags & SCAN_BOOTING) == 0 - && (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) { - // Check all shared libraries and map to their actual file path. - // We only do this here for apps not on a system dir, because those - // are the only ones that can fail an install due to this. We - // will take care of the system apps by updating all of their - // library paths after the scan is done. Also during the initial - // scan don't update any libs as we do this wholesale after all - // apps are scanned to avoid dependency based scanning. - updateSharedLibrariesLPr(pkg, null); + if (reconciledPkg.collectedSharedLibraryInfos != null) { + executeSharedLibrariesUpdateLPr(pkg, null, reconciledPkg.collectedSharedLibraryInfos); } // All versions of a static shared library are referenced with the same @@ -10396,8 +10415,8 @@ public class PackageManagerService extends IPackageManager.Stub } else { final int userId = user == null ? 0 : user.getIdentifier(); // Modify state for the given package setting - commitPackageSettings(pkg, oldPkg, pkgSetting, user, scanFlags, - (parseFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/); + commitPackageSettings(pkg, oldPkg, pkgSetting, scanFlags, + (parseFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/, reconciledPkg); if (pkgSetting.getInstantApp(userId)) { mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId); } @@ -10568,11 +10587,10 @@ public class PackageManagerService extends IPackageManager.Stub UserManagerService.getInstance(), usesStaticLibraries, pkg.usesStaticLibrariesVersions); } else { - if (!createNewPackage) { - // make a deep copy to avoid modifying any existing system state. - pkgSetting = new PackageSetting(pkgSetting); - pkgSetting.pkg = pkg; - } + // make a deep copy to avoid modifying any existing system state. + pkgSetting = new PackageSetting(pkgSetting); + pkgSetting.pkg = pkg; + // REMOVE SharedUserSetting from method; update in a separate call. // // TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi, @@ -10791,8 +10809,21 @@ public class PackageManagerService extends IPackageManager.Stub pkgSetting.volumeUuid = volumeUuid; } + SharedLibraryInfo staticSharedLibraryInfo = null; + if (!TextUtils.isEmpty(pkg.staticSharedLibName)) { + staticSharedLibraryInfo = SharedLibraryInfo.createForStatic(pkg); + } + List<SharedLibraryInfo> dynamicSharedLibraryInfos = null; + if (!ArrayUtils.isEmpty(pkg.libraryNames)) { + dynamicSharedLibraryInfos = new ArrayList<>(pkg.libraryNames.size()); + for (String name : pkg.libraryNames) { + dynamicSharedLibraryInfos.add(SharedLibraryInfo.createForDynamic(pkg, name)); + } + } + return new ScanResult(request, true, pkgSetting, changedAbiCodePath, - !createNewPackage /* existingSettingCopied */); + !createNewPackage /* existingSettingCopied */, staticSharedLibraryInfo, + dynamicSharedLibraryInfos); } /** @@ -11302,24 +11333,48 @@ public class PackageManagerService extends IPackageManager.Stub } } - private boolean addSharedLibraryLPw(String path, String apk, List<String> codePaths, - String name, long version, int type, String declaringPackageName, - long declaringVersionCode) { + @GuardedBy("mPackages") + private boolean addBuiltInSharedLibraryLocked(String path, String name) { + if (nonStaticSharedLibExistsLocked(name)) { + return false; + } + + SharedLibraryInfo libraryInfo = new SharedLibraryInfo(path, null, null, name, + (long) SharedLibraryInfo.VERSION_UNDEFINED, SharedLibraryInfo.TYPE_BUILTIN, + new VersionedPackage(PLATFORM_PACKAGE_NAME, (long) 0), + null, null); + + commitSharedLibraryInfoLocked(libraryInfo); + return true; + } + + @GuardedBy("mPackages") + private boolean nonStaticSharedLibExistsLocked(String name) { + return sharedLibExists(name, SharedLibraryInfo.VERSION_UNDEFINED, mSharedLibraries); + } + + private static boolean sharedLibExists(final String name, final long version, + Map<String, LongSparseArray<SharedLibraryInfo>> librarySource) { + LongSparseArray<SharedLibraryInfo> versionedLib = librarySource.get(name); + if (versionedLib != null && versionedLib.indexOfKey(version) >= 0) { + return true; + } + return false; + } + + @GuardedBy("mPackages") + private void commitSharedLibraryInfoLocked(SharedLibraryInfo libraryInfo) { + final String name = libraryInfo.getName(); LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name); if (versionedLib == null) { versionedLib = new LongSparseArray<>(); mSharedLibraries.put(name, versionedLib); - if (type == SharedLibraryInfo.TYPE_STATIC) { - mStaticLibsByDeclaringPackage.put(declaringPackageName, versionedLib); - } - } else if (versionedLib.indexOfKey(version) >= 0) { - return false; } - SharedLibraryInfo libraryInfo = new SharedLibraryInfo(path, apk, codePaths, name, - version, type, new VersionedPackage(declaringPackageName, declaringVersionCode), - null, null); - versionedLib.put(version, libraryInfo); - return true; + final String declaringPackageName = libraryInfo.getDeclaringPackage().getPackageName(); + if (libraryInfo.getType() == SharedLibraryInfo.TYPE_STATIC) { + mStaticLibsByDeclaringPackage.put(declaringPackageName, versionedLib); + } + versionedLib.put(libraryInfo.getLongVersion(), libraryInfo); } private boolean removeSharedLibraryLPw(String name, long version) { @@ -11348,8 +11403,8 @@ public class PackageManagerService extends IPackageManager.Stub * be available for query, resolution, etc... */ private void commitPackageSettings(PackageParser.Package pkg, - @Nullable PackageParser.Package oldPkg, PackageSetting pkgSetting, UserHandle user, - final @ScanFlags int scanFlags, boolean chatty) { + @Nullable PackageParser.Package oldPkg, PackageSetting pkgSetting, + final @ScanFlags int scanFlags, boolean chatty, ReconciledPackage reconciledPkg) { final String pkgName = pkg.packageName; if (mCustomResolverComponentName != null && mCustomResolverComponentName.getPackageName().equals(pkg.packageName)) { @@ -11396,91 +11451,24 @@ public class PackageManagerService extends IPackageManager.Stub ArrayList<PackageParser.Package> clientLibPkgs = null; // writer synchronized (mPackages) { - boolean hasStaticSharedLibs = false; - - // Any app can add new static shared libraries - if (pkg.staticSharedLibName != null) { - // Static shared libs don't allow renaming as they have synthetic package - // names to allow install of multiple versions, so use name from manifest. - if (addSharedLibraryLPw(null, pkg.packageName, pkg.getAllCodePaths(), - pkg.staticSharedLibName, - pkg.staticSharedLibVersion, SharedLibraryInfo.TYPE_STATIC, - pkg.manifestPackageName, pkg.getLongVersionCode())) { - hasStaticSharedLibs = true; + if (!ArrayUtils.isEmpty(reconciledPkg.allowedSharedLibraryInfos)) { + for (SharedLibraryInfo info : reconciledPkg.allowedSharedLibraryInfos) { + commitSharedLibraryInfoLocked(info); + } + try { // Shared libraries for the package need to be updated. - try { - updateSharedLibrariesLPr(pkg, null); - } catch (PackageManagerException e) { - Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e); - } - } else { - Slog.w(TAG, "Package " + pkg.packageName + " library " - + pkg.staticSharedLibName + " already exists; skipping"); - } - // Static shared libs cannot be updated once installed since they - // use synthetic package name which includes the version code, so - // not need to update other packages's shared lib dependencies. - } - - if (!hasStaticSharedLibs - && (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { - // Only system apps can add new dynamic shared libraries. - if (pkg.libraryNames != null) { - for (int i = 0; i < pkg.libraryNames.size(); i++) { - String name = pkg.libraryNames.get(i); - boolean allowed = false; - if (pkg.isUpdatedSystemApp()) { - // New library entries can only be added through the - // system image. This is important to get rid of a lot - // of nasty edge cases: for example if we allowed a non- - // system update of the app to add a library, then uninstalling - // the update would make the library go away, and assumptions - // we made such as through app install filtering would now - // have allowed apps on the device which aren't compatible - // with it. Better to just have the restriction here, be - // conservative, and create many fewer cases that can negatively - // impact the user experience. - final PackageSetting sysPs = mSettings - .getDisabledSystemPkgLPr(pkg.packageName); - if (sysPs.pkg != null && sysPs.pkg.libraryNames != null) { - for (int j = 0; j < sysPs.pkg.libraryNames.size(); j++) { - if (name.equals(sysPs.pkg.libraryNames.get(j))) { - allowed = true; - break; - } - } - } - } else { - allowed = true; - } - if (allowed) { - if (!addSharedLibraryLPw(null, pkg.packageName, pkg.getAllCodePaths(), - name, SharedLibraryInfo.VERSION_UNDEFINED, - SharedLibraryInfo.TYPE_DYNAMIC, - pkg.packageName, pkg.getLongVersionCode())) { - Slog.w(TAG, "Package " + pkg.packageName + " library " - + name + " already exists; skipping"); - } - // Shared libraries for the package need to be updated. - try { - updateSharedLibrariesLPr(pkg, null); - } catch (PackageManagerException e) { - Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e); - } - } else { - Slog.w(TAG, "Package " + pkg.packageName + " declares lib " - + name + " that is not declared on system image; skipping"); - } - } - - if ((scanFlags & SCAN_BOOTING) == 0) { - // If we are not booting, we need to update any applications - // that are clients of our shared library. If we are booting, - // this will all be done once the scan is complete. - clientLibPkgs = updateAllSharedLibrariesLPw(pkg); - } + updateSharedLibrariesLPr(pkg, null); + } catch (PackageManagerException e) { + Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e); } } + + if (reconciledPkg.hasDynamicSharedLibraries() && (scanFlags & SCAN_BOOTING) == 0) { + // If we are not booting, we need to update any applications + // that are clients of our shared library. If we are booting, + // this will all be done once the scan is complete. + clientLibPkgs = updateAllSharedLibrariesLPw(pkg); + } } if ((scanFlags & SCAN_BOOTING) != 0) { @@ -15235,8 +15223,8 @@ public class PackageManagerService extends IPackageManager.Stub private static class ReconcileRequest { public final Map<String, ScanResult> scannedPackages; - // TODO: Remove install-specific details from reconcile request; make them generic types - // that can be used for scanDir for example. + public final Map<String, PackageParser.Package> allPackages; + public final Map<String, LongSparseArray<SharedLibraryInfo>> sharedLibrarySource; public final Map<String, InstallArgs> installArgs; public final Map<String, PackageInstalledInfo> installResults; public final Map<String, PrepareResult> preparedPackages; @@ -15244,11 +15232,22 @@ public class PackageManagerService extends IPackageManager.Stub private ReconcileRequest(Map<String, ScanResult> scannedPackages, Map<String, InstallArgs> installArgs, Map<String, PackageInstalledInfo> installResults, - Map<String, PrepareResult> preparedPackages) { + Map<String, PrepareResult> preparedPackages, + Map<String, LongSparseArray<SharedLibraryInfo>> sharedLibrarySource, + Map<String, PackageParser.Package> allPackages) { this.scannedPackages = scannedPackages; this.installArgs = installArgs; this.installResults = installResults; this.preparedPackages = preparedPackages; + this.sharedLibrarySource = sharedLibrarySource; + this.allPackages = allPackages; + } + + private ReconcileRequest(Map<String, ScanResult> scannedPackages, + Map<String, LongSparseArray<SharedLibraryInfo>> sharedLibrarySource, + Map<String, PackageParser.Package> allPackages) { + this(scannedPackages, Collections.emptyMap(), Collections.emptyMap(), + Collections.emptyMap(), sharedLibrarySource, allPackages); } } private static class ReconcileFailure extends PackageManagerException { @@ -15267,29 +15266,31 @@ public class PackageManagerService extends IPackageManager.Stub private static class ReconciledPackage { public final PackageSetting pkgSetting; public final ScanResult scanResult; - public final UserHandle installForUser; - public final String volumeUuid; // TODO: Remove install-specific details from the reconcile result public final PackageInstalledInfo installResult; - public final PrepareResult prepareResult; - @PackageManager.InstallFlags - public final int installFlags; - public final InstallArgs installArgs; + @Nullable public final PrepareResult prepareResult; + @Nullable public final InstallArgs installArgs; public final DeletePackageAction deletePackageAction; + public final List<SharedLibraryInfo> allowedSharedLibraryInfos; + public ArrayList<SharedLibraryInfo> collectedSharedLibraryInfos; private ReconciledPackage(InstallArgs installArgs, PackageSetting pkgSetting, - UserHandle installForUser, PackageInstalledInfo installResult, int installFlags, - String volumeUuid, PrepareResult prepareResult, ScanResult scanResult, - DeletePackageAction deletePackageAction) { + PackageInstalledInfo installResult, + PrepareResult prepareResult, ScanResult scanResult, + DeletePackageAction deletePackageAction, + List<SharedLibraryInfo> allowedSharedLibraryInfos) { this.installArgs = installArgs; this.pkgSetting = pkgSetting; - this.installForUser = installForUser; this.installResult = installResult; - this.installFlags = installFlags; - this.volumeUuid = volumeUuid; this.prepareResult = prepareResult; this.scanResult = scanResult; this.deletePackageAction = deletePackageAction; + this.allowedSharedLibraryInfos = allowedSharedLibraryInfos; + } + + public boolean hasDynamicSharedLibraries() { + return !ArrayUtils.isEmpty(allowedSharedLibraryInfos) + && allowedSharedLibraryInfos.get(0).getType() != SharedLibraryInfo.TYPE_STATIC; } } @@ -15297,19 +15298,50 @@ public class PackageManagerService extends IPackageManager.Stub private static Map<String, ReconciledPackage> reconcilePackagesLocked( final ReconcileRequest request) throws ReconcileFailure { - Map<String, ReconciledPackage> result = new ArrayMap<>(request.scannedPackages.size()); - for (String installPackageName : request.installArgs.keySet()) { - final ScanResult scanResult = request.scannedPackages.get(installPackageName); + final Map<String, ScanResult> scannedPackages = request.scannedPackages; + + final Map<String, ReconciledPackage> result = new ArrayMap<>(scannedPackages.size()); + + // make a copy of the existing set of packages so we can combine them with incoming packages + final ArrayMap<String, PackageParser.Package> combinedPackages = + new ArrayMap<>(request.allPackages.size() + scannedPackages.size()); + combinedPackages.putAll(request.allPackages); + + final Map<String, LongSparseArray<SharedLibraryInfo>> incomingSharedLibraries = + new ArrayMap<>(); + + for (String installPackageName : scannedPackages.keySet()) { + final ScanResult scanResult = scannedPackages.get(installPackageName); + + // add / replace existing with incoming packages + combinedPackages.put(scanResult.pkgSetting.name, scanResult.request.pkg); + + // in the first pass, we'll build up the set of incoming shared libraries + final List<SharedLibraryInfo> allowedSharedLibInfos = + getAllowedSharedLibInfos(scanResult, request.sharedLibrarySource); + final SharedLibraryInfo staticLib = scanResult.staticSharedLibraryInfo; + if (allowedSharedLibInfos != null) { + for (SharedLibraryInfo info : allowedSharedLibInfos) { + if (!addSharedLibraryToPackageVersionMap(incomingSharedLibraries, info)) { + throw new ReconcileFailure("Static Shared Library " + staticLib.getName() + + " is being installed twice in this set!"); + } + } + } + + // the following may be null if we're just reconciling on boot (and not during install) final InstallArgs installArgs = request.installArgs.get(installPackageName); final PackageInstalledInfo res = request.installResults.get(installPackageName); final PrepareResult prepareResult = request.preparedPackages.get(installPackageName); - if (scanResult == null || installArgs == null || res == null) { - throw new ReconcileFailure( - "inputs not balanced; missing argument for " + installPackageName); + final boolean isInstall = installArgs != null; + if (isInstall && (res == null || prepareResult == null)) { + throw new ReconcileFailure("Reconcile arguments are not balanced for " + + installPackageName + "!"); } + final DeletePackageAction deletePackageAction; // we only want to try to delete for non system apps - if (prepareResult.replace && !prepareResult.system) { + if (isInstall && prepareResult.replace && !prepareResult.system) { final boolean killApp = (scanResult.request.scanFlags & SCAN_DONT_KILL_APP) == 0; final int deleteFlags = PackageManager.DELETE_KEEP_DATA | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP); @@ -15324,15 +15356,127 @@ public class PackageManagerService extends IPackageManager.Stub } else { deletePackageAction = null; } + result.put(installPackageName, - new ReconciledPackage(installArgs, scanResult.pkgSetting, installArgs.getUser(), - res, installArgs.installFlags, installArgs.volumeUuid, - request.preparedPackages.get(installPackageName), scanResult, - deletePackageAction)); + new ReconciledPackage(installArgs, scanResult.pkgSetting, + res, request.preparedPackages.get(installPackageName), scanResult, + deletePackageAction, allowedSharedLibInfos)); } + + for (String installPackageName : scannedPackages.keySet()) { + // Check all shared libraries and map to their actual file path. + // We only do this here for apps not on a system dir, because those + // are the only ones that can fail an install due to this. We + // will take care of the system apps by updating all of their + // library paths after the scan is done. Also during the initial + // scan don't update any libs as we do this wholesale after all + // apps are scanned to avoid dependency based scanning. + final ScanResult scanResult = scannedPackages.get(installPackageName); + if ((scanResult.request.scanFlags & SCAN_BOOTING) != 0 + || (scanResult.request.parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) { + continue; + } + try { + result.get(installPackageName).collectedSharedLibraryInfos = + collectSharedLibraryInfos(scanResult.request.pkg, combinedPackages, + request.sharedLibrarySource, incomingSharedLibraries); + + } catch (PackageManagerException e) { + throw new ReconcileFailure(e.error, e.getMessage()); + } + } + return result; } + private static List<SharedLibraryInfo> getAllowedSharedLibInfos( + ScanResult scanResult, + Map<String, LongSparseArray<SharedLibraryInfo>> existingSharedLibraries) { + final PackageParser.Package pkg = scanResult.pkgSetting.pkg; + if (scanResult.staticSharedLibraryInfo == null + && scanResult.dynamicSharedLibraryInfos == null) { + return null; + } + + // Any app can add new static shared libraries + if (scanResult.staticSharedLibraryInfo != null) { + return Collections.singletonList(scanResult.staticSharedLibraryInfo); + } + final boolean hasDynamicLibraries = + (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 + && scanResult.dynamicSharedLibraryInfos != null; + if (!hasDynamicLibraries) { + return null; + } + final ArrayList<SharedLibraryInfo> infos = + new ArrayList<>(scanResult.dynamicSharedLibraryInfos.size()); + final boolean updatedSystemApp = pkg.isUpdatedSystemApp(); + for (SharedLibraryInfo info : scanResult.dynamicSharedLibraryInfos) { + String name = info.getName(); + boolean allowed = false; + if (updatedSystemApp) { + // New library entries can only be added through the + // system image. This is important to get rid of a lot + // of nasty edge cases: for example if we allowed a non- + // system update of the app to add a library, then uninstalling + // the update would make the library go away, and assumptions + // we made such as through app install filtering would now + // have allowed apps on the device which aren't compatible + // with it. Better to just have the restriction here, be + // conservative, and create many fewer cases that can negatively + // impact the user experience. + final PackageSetting sysPs = scanResult.request.disabledPkgSetting; + if (sysPs.pkg != null && sysPs.pkg.libraryNames != null) { + for (int j = 0; j < sysPs.pkg.libraryNames.size(); j++) { + if (name.equals(sysPs.pkg.libraryNames.get(j))) { + allowed = true; + break; + } + } + } + } else { + allowed = true; + } + if (allowed) { + if (sharedLibExists( + name, SharedLibraryInfo.VERSION_UNDEFINED, existingSharedLibraries)) { + Slog.w(TAG, "Package " + pkg.packageName + " library " + + name + " already exists; skipping"); + continue; + } + infos.add(info); + } else { + Slog.w(TAG, "Package " + pkg.packageName + " declares lib " + + name + " that is not declared on system image; skipping"); + continue; + } + } + return infos; + } + + /** + * Returns false if the adding shared library already exists in the map and so could not be + * added. + */ + private static boolean addSharedLibraryToPackageVersionMap( + Map<String, LongSparseArray<SharedLibraryInfo>> target, + SharedLibraryInfo library) { + final String name = library.getName(); + if (target.containsKey(name)) { + if (library.getType() != SharedLibraryInfo.TYPE_STATIC) { + // We've already added this non-version-specific library to the map. + return false; + } else if (target.get(name).indexOfKey(library.getLongVersion()) >= 0) { + // We've already added this version of a version-specific library to the map. + return false; + } + } else { + target.put(name, new LongSparseArray<>()); + } + target.get(name).put(library.getLongVersion(), library); + return true; + } + @GuardedBy("mPackages") private boolean commitPackagesLocked(final CommitRequest request) { // TODO: remove any expected failures from this method; this should only be able to fail due @@ -15474,7 +15618,7 @@ public class PackageManagerService extends IPackageManager.Stub try { prepareScanResultLocked(scanResult); - commitScanResultLocked(scanResult); + commitReconciledScanResultLocked(reconciledPkg); } catch (PackageManagerException e) { unprepareScanResultLocked(scanResult); res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR); @@ -15482,7 +15626,7 @@ public class PackageManagerService extends IPackageManager.Stub return false; } updateSettingsLI(pkg, reconciledPkg.installArgs.installerPackageName, request.mAllUsers, - res, reconciledPkg.installForUser, reconciledPkg.installArgs.installReason); + res, reconciledPkg.installArgs.user, reconciledPkg.installArgs.installReason); final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null) { @@ -15579,7 +15723,9 @@ public class PackageManagerService extends IPackageManager.Stub } ReconcileRequest reconcileRequest = new ReconcileRequest(scans, installArgs, installResults, - prepareResults); + prepareResults, + mSharedLibraries, + Collections.unmodifiableMap(mPackages)); CommitRequest commitRequest = null; synchronized (mPackages) { Map<String, ReconciledPackage> reconciledPackages; @@ -15631,8 +15777,8 @@ public class PackageManagerService extends IPackageManager.Stub */ private void executePostCommitSteps(CommitRequest commitRequest) { for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) { - final boolean instantApp = - ((reconciledPkg.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0); + final boolean instantApp = ((reconciledPkg.scanResult.request.scanFlags + & PackageManagerService.SCAN_AS_INSTANT_APP) != 0); final PackageParser.Package pkg = reconciledPkg.pkgSetting.pkg; final String packageName = pkg.packageName; prepareAppDataAfterInstallLIF(pkg); @@ -15650,7 +15796,7 @@ public class PackageManagerService extends IPackageManager.Stub // can be used for optimizations. mArtManagerService.prepareAppProfiles( pkg, - resolveUserIds(reconciledPkg.installForUser.getIdentifier()), + resolveUserIds(reconciledPkg.installArgs.user.getIdentifier()), /* updateReferenceProfileContent= */ true); // Check whether we need to dexopt the app. @@ -15727,8 +15873,32 @@ public class PackageManagerService extends IPackageManager.Stub try { final List<ScanResult> restoreResults = scanPackageTracedLI(oldPackage, reconciledPackage.prepareResult.parseFlags, SCAN_UPDATE_SIGNATURE, 0, - reconciledPackage.installForUser); - commitSuccessfulScanResults(restoreResults); + reconciledPackage.installArgs.user); + for (ScanResult result : restoreResults) { + // failures should have been caught earlier, but in case it wasn't, + // let's double check + if (!result.success) { + throw new PackageManagerException( + "Scan failed for " + result.request.pkg.packageName); + } + } + for (ScanResult result : restoreResults) { + try { + prepareScanResultLocked(result); + final Map<String, ReconciledPackage> reconcileResult = + reconcilePackagesLocked(new ReconcileRequest( + Collections.singletonMap(result.pkgSetting.name, + result), + mSharedLibraries, + mPackages + )); + commitReconciledScanResultLocked( + reconcileResult.get(result.pkgSetting.name)); + } catch (PackageManagerException e) { + unprepareScanResultLocked(result); + throw e; + } + } restoredPkg = restoreResults.get(0).pkgSetting.pkg; } catch (PackageManagerException e) { Slog.e(TAG, "Failed to restore original package: " + e.getMessage()); @@ -16284,15 +16454,6 @@ public class PackageManagerService extends IPackageManager.Stub throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI"); } - - // Shared libraries for the package need to be updated. - synchronized (mPackages) { - try { - updateSharedLibrariesLPr(pkg, null); - } catch (PackageManagerException e) { - Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage()); - } - } } if (!args.doRename(res.returnCode, pkg)) { @@ -17856,21 +18017,14 @@ public class PackageManagerService extends IPackageManager.Stub public final PackageRemovedInfo outInfo; public final int flags; public final UserHandle user; - /** - * True if this package is an unupdated system app that may be deleted by the system. - * When true, disabledPs will be null. - */ - public final boolean mayDeleteUnupdatedSystemApp; private DeletePackageAction(PackageSetting deletingPs, PackageSetting disabledPs, - PackageRemovedInfo outInfo, int flags, UserHandle user, - boolean mayDeleteUnupdatedSystemApp) { + PackageRemovedInfo outInfo, int flags, UserHandle user) { this.deletingPs = deletingPs; this.disabledPs = disabledPs; this.outInfo = outInfo; this.flags = flags; this.user = user; - this.mayDeleteUnupdatedSystemApp = mayDeleteUnupdatedSystemApp; } } @@ -17886,24 +18040,22 @@ public class PackageManagerService extends IPackageManager.Stub if (ps == null) { return null; } - boolean mayDeleteUnupdatedSystemApp = false; if (isSystemApp(ps)) { if (ps.parentPackageName != null) { Slog.w(TAG, "Attempt to delete child system package " + ps.pkg.packageName); return null; } - if (((flags & PackageManager.DELETE_SYSTEM_APP) != 0) && user != null - && user.getIdentifier() != UserHandle.USER_ALL) { - mayDeleteUnupdatedSystemApp = true; - } else if (disabledPs == null) { - // Confirmed if the system package has been updated - // An updated system app can be deleted. This will also have to restore - // the system pkg from system partition - // reader + final boolean deleteSystem = (flags & PackageManager.DELETE_SYSTEM_APP) != 0; + final boolean deleteAllUsers = + user == null || user.getIdentifier() == UserHandle.USER_ALL; + if ((!deleteSystem || deleteAllUsers) && disabledPs == null) { Slog.w(TAG, "Attempt to delete unknown system package " + ps.pkg.packageName); return null; } + // Confirmed if the system package has been updated + // An updated system app can be deleted. This will also have to restore + // the system pkg from system partition reader } final int parentReferenceCount = (ps.childPackageNames != null) ? ps.childPackageNames.size() : 0; @@ -17918,8 +18070,7 @@ public class PackageManagerService extends IPackageManager.Stub } } } - return new DeletePackageAction(ps, disabledPs, outInfo, flags, user, - mayDeleteUnupdatedSystemApp); + return new DeletePackageAction(ps, disabledPs, outInfo, flags, user); } /* @@ -17993,8 +18144,8 @@ public class PackageManagerService extends IPackageManager.Stub if (ps.getPermissionsState().hasPermission(Manifest.permission.SUSPEND_APPS, userId)) { unsuspendForSuspendingPackage(packageName, userId); } - - if (!systemApp || action.mayDeleteUnupdatedSystemApp) { + if ((!systemApp || (flags & PackageManager.DELETE_SYSTEM_APP) != 0) + && userId != UserHandle.USER_ALL) { // The caller is asking that the package only be deleted for a single // user. To do this, we just mark its uninstalled state and delete // its data. If this is a system app, we only allow this to happen if @@ -18017,13 +18168,7 @@ public class PackageManagerService extends IPackageManager.Stub // We need to set it back to 'installed' so the uninstall // broadcasts will be sent correctly. if (DEBUG_REMOVE) Slog.d(TAG, "Not installed by other users, full delete"); - if (userId != UserHandle.USER_ALL) { - ps.setInstalled(true, userId); - } else { - for (int origUserId : outInfo.origUsers) { - ps.setInstalled(true, origUserId); - } - } + ps.setInstalled(true, userId); mSettings.writeKernelMappingLPr(ps); } } else { @@ -19484,6 +19629,17 @@ public class PackageManagerService extends IPackageManager.Stub return getHomeActivitiesAsUser(allHomeCandidates, UserHandle.getCallingUserId()); } + /** + * Send a {@code PackageInstaller.ACTION_SESSION_UPDATED} broadcast intent, containing + * the {@code sessionInfo} in the extra field {@code PackageInstaller.EXTRA_SESSION}. + */ + public void sendSessionUpdatedBroadcast(PackageInstaller.SessionInfo sessionInfo, + int userId) { + Intent sessionUpdatedIntent = new Intent(PackageInstaller.ACTION_SESSION_UPDATED) + .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo); + mContext.sendBroadcastAsUser(sessionUpdatedIntent, UserHandle.of(userId)); + } + public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId) { UserManagerService ums = UserManagerService.getInstance(); if (ums != null) { @@ -20124,10 +20280,10 @@ public class PackageManagerService extends IPackageManager.Stub ContentObserver co = new ContentObserver(mHandler) { @Override public void onChange(boolean selfChange) { - boolean ephemeralFeatureDisabled = + final boolean ephemeralFeatureDisabled = Global.getInt(resolver, Global.ENABLE_EPHEMERAL_FEATURE, 1) == 0; for (int userId : UserManagerService.getInstance().getUserIds()) { - boolean instantAppsDisabledForUser = + final boolean instantAppsDisabledForUser = ephemeralFeatureDisabled || Secure.getIntForUser(resolver, Secure.INSTANT_APPS_ENABLED, 1, userId) == 0; mWebInstantAppsDisabled.put(userId, instantAppsDisabledForUser); 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 164af38be5a6..789664d82332 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -1193,9 +1193,9 @@ public final class DefaultPermissionGrantPolicy { if (pm.checkPermission(fgPerm, pkg.packageName) == PackageManager.PERMISSION_GRANTED) { // Upgrade the app-op state of the fg permission to allow bg access - mContext.getSystemService(AppOpsManager.class).setMode( + mContext.getSystemService(AppOpsManager.class).setUidMode( AppOpsManager.permissionToOp(fgPerm), uid, - pkg.packageName, AppOpsManager.MODE_ALLOWED); + AppOpsManager.MODE_ALLOWED); break; } @@ -1205,8 +1205,8 @@ public final class DefaultPermissionGrantPolicy { String bgPerm = getBackgroundPermission(permission); if (bgPerm == null) { if (op != null) { - mContext.getSystemService(AppOpsManager.class).setMode(op, uid, - pkg.packageName, AppOpsManager.MODE_ALLOWED); + mContext.getSystemService(AppOpsManager.class).setUidMode(op, uid, + AppOpsManager.MODE_ALLOWED); } } else { int mode; @@ -1217,8 +1217,7 @@ public final class DefaultPermissionGrantPolicy { mode = AppOpsManager.MODE_FOREGROUND; } - mContext.getSystemService(AppOpsManager.class).setMode(op, uid, - pkg.packageName, mode); + mContext.getSystemService(AppOpsManager.class).setUidMode(op, uid, mode); } if (DEBUG) { diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index b58c811645f7..93964cb09ae6 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -27,7 +27,6 @@ import static android.app.AppOpsManager.MODE_IGNORED; import static android.app.AppOpsManager.OP_NONE; import static android.app.AppOpsManager.permissionToOp; import static android.app.AppOpsManager.permissionToOpCode; -import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; @@ -1073,9 +1072,8 @@ public class PermissionManagerService { AppOpsManagerInternal appOpsInternal = LocalServices.getService( AppOpsManagerInternal.class); - appOpsInternal.setMode(permissionToOpCode(permission), - getUid(userId, getAppId(pkg.applicationInfo.uid)), pkg.packageName, mode, - (pkg.applicationInfo.privateFlags & PRIVATE_FLAG_PRIVILEGED) != 0); + appOpsInternal.setUidMode(permissionToOpCode(permission), + getUid(userId, getAppId(pkg.applicationInfo.uid)), mode); } /** @@ -1345,8 +1343,12 @@ public class PermissionManagerService { sourcePermNum++) { String sourcePerm = sourcePerms.valueAt(sourcePermNum); - if (appOpsManager.unsafeCheckOpNoThrow(permissionToOp(sourcePerm), - getUid(userId, getAppId(pkg.applicationInfo.uid)), pkgName) + if (ps.hasRuntimePermission(sourcePerm, userId) + && ps.getRuntimePermissionState(sourcePerm, userId) + .isGranted() + && appOpsManager.unsafeCheckOpNoThrow( + permissionToOp(sourcePerm), getUid(userId, + getAppId(pkg.applicationInfo.uid)), pkgName) == MODE_ALLOWED) { setAppOpMode(sourcePerm, pkg, userId, MODE_FOREGROUND); } diff --git a/services/core/java/com/android/server/textservices/TextServicesManagerInternal.java b/services/core/java/com/android/server/textservices/TextServicesManagerInternal.java new file mode 100644 index 000000000000..56bcdd9ec146 --- /dev/null +++ b/services/core/java/com/android/server/textservices/TextServicesManagerInternal.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.textservices; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.view.textservice.SpellCheckerInfo; + +import com.android.server.LocalServices; + +/** + * Local interface of {@link TextServicesManagerService} inside system server process. + */ +public abstract class TextServicesManagerInternal { + /** + * Returns the list of installed input methods for the specified user. + * + * <p>CAVEAT: This method is not fully implemented yet. This may return an empty list if + * {@code userId} for a background user is specified. Check the implementation before starting + * this method.</p> + * + * @param userId The user ID to be queried. + * @return {@link SpellCheckerInfo} that is currently selected {@code userId}. + */ + @Nullable + public abstract SpellCheckerInfo getCurrentSpellCheckerForUser(@UserIdInt int userId); + + /** + * Fake implementation of {@link TextServicesManagerInternal}. All the methods do nothing. + */ + private static final TextServicesManagerInternal NOP = + new TextServicesManagerInternal() { + @Override + public SpellCheckerInfo getCurrentSpellCheckerForUser(@UserIdInt int userId) { + return null; + } + }; + + /** + * @return Global instance if exists. Otherwise, a dummy no-op instance. + */ + @NonNull + public static TextServicesManagerInternal get() { + final TextServicesManagerInternal instance = + LocalServices.getService(TextServicesManagerInternal.class); + return instance != null ? instance : NOP; + } +} diff --git a/services/core/java/com/android/server/textservices/TextServicesManagerService.java b/services/core/java/com/android/server/textservices/TextServicesManagerService.java index 23c29f8f8997..65d5b10590fa 100644 --- a/services/core/java/com/android/server/textservices/TextServicesManagerService.java +++ b/services/core/java/com/android/server/textservices/TextServicesManagerService.java @@ -18,20 +18,6 @@ package com.android.server.textservices; import static android.view.textservice.TextServicesManager.DISABLE_PER_PROFILE_SPELL_CHECKER; -import com.android.internal.annotations.GuardedBy; -import com.android.internal.content.PackageMonitor; -import com.android.internal.inputmethod.SubtypeLocaleUtils; -import com.android.internal.textservice.ISpellCheckerService; -import com.android.internal.textservice.ISpellCheckerServiceCallback; -import com.android.internal.textservice.ISpellCheckerSession; -import com.android.internal.textservice.ISpellCheckerSessionListener; -import com.android.internal.textservice.ITextServicesManager; -import com.android.internal.textservice.ITextServicesSessionListener; -import com.android.internal.util.DumpUtils; -import com.android.server.SystemService; - -import org.xmlpull.v1.XmlPullParserException; - import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -60,12 +46,27 @@ import android.util.SparseArray; import android.view.textservice.SpellCheckerInfo; import android.view.textservice.SpellCheckerSubtype; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.content.PackageMonitor; +import com.android.internal.inputmethod.SubtypeLocaleUtils; +import com.android.internal.textservice.ISpellCheckerService; +import com.android.internal.textservice.ISpellCheckerServiceCallback; +import com.android.internal.textservice.ISpellCheckerSession; +import com.android.internal.textservice.ISpellCheckerSessionListener; +import com.android.internal.textservice.ITextServicesManager; +import com.android.internal.textservice.ITextServicesSessionListener; +import com.android.internal.util.DumpUtils; +import com.android.server.LocalServices; +import com.android.server.SystemService; + +import org.xmlpull.v1.XmlPullParserException; + import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; import java.lang.ref.WeakReference; -import java.util.Arrays; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -278,6 +279,14 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { @Override public void onStart() { + LocalServices.addService(TextServicesManagerInternal.class, + new TextServicesManagerInternal() { + @Override + public SpellCheckerInfo getCurrentSpellCheckerForUser( + @UserIdInt int userId) { + return mService.getCurrentSpellCheckerForUser(userId); + } + }); publishBinderService(Context.TEXT_SERVICES_MANAGER_SERVICE, mService); } @@ -493,6 +502,15 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { return spellCheckerList.get(0); } + @Nullable + private SpellCheckerInfo getCurrentSpellCheckerForUser(@UserIdInt int userId) { + synchronized (mLock) { + final int spellCheckerOwnerUserId = mSpellCheckerOwnerUserIdMap.get(userId); + final TextServicesData data = mUserData.get(spellCheckerOwnerUserId); + return data != null ? data.getCurrentSpellChecker() : null; + } + } + // TODO: Save SpellCheckerService by supported languages. Currently only one spell // checker is saved. @Override diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 4d8440a899a3..2cd0168aff42 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2486,36 +2486,20 @@ final class ActivityRecord extends ConfigurationContainer { } void setRequestedOrientation(int requestedOrientation) { - final int displayId = getDisplayId(); - final Configuration displayConfig = - mRootActivityContainer.getDisplayOverrideConfiguration(displayId); - - final Configuration config = setOrientation(requestedOrientation, - displayId, displayConfig, mayFreezeScreenLocked(app)); - if (config != null) { - frozenBeforeDestroy = true; - if (!mAtmService.updateDisplayOverrideConfigurationLocked(config, this, - false /* deferResume */, displayId)) { - mRootActivityContainer.resumeFocusedStacksTopActivities(); - } - } + setOrientation(requestedOrientation, mayFreezeScreenLocked(app)); mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged( task.taskId, requestedOrientation); } - Configuration setOrientation(int requestedOrientation, int displayId, - Configuration displayConfig, boolean freezeScreenIfNeeded) { + private void setOrientation(int requestedOrientation, boolean freezeScreenIfNeeded) { if (mAppWindowToken == null) { Slog.w(TAG_WM, "Attempted to set orientation of non-existing app token: " + appToken); - return null; + return; } - mAppWindowToken.setOrientation(requestedOrientation); - final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null; - return mAtmService.mWindowManager.updateOrientationFromAppTokens(displayConfig, binder, - displayId); + mAppWindowToken.setOrientation(requestedOrientation, binder, this); } int getOrientation() { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 1943efca8ad0..a5ceee268fa2 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -397,10 +397,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private final Matrix mTmpMatrix = new Matrix(); private final Region mTmpRegion = new Region(); - /** Used for handing back size of display */ private final Rect mTmpBounds = new Rect(); + private final Configuration mTmpConfiguration = new Configuration(); + /** Remove this display when animation on it has completed. */ private boolean mDeferredRemoval; @@ -1156,6 +1157,36 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mWmService.mH.obtainMessage(SEND_NEW_CONFIGURATION, this).sendToTarget(); } + @Override + boolean onDescendantOrientationChanged(IBinder freezeDisplayToken, + ConfigurationContainer requestingContainer) { + final Configuration config = updateOrientationFromAppTokens( + getRequestedOverrideConfiguration(), freezeDisplayToken, false); + // If display rotation class tells us that it doesn't consider app requested orientation, + // this display won't rotate just because of an app changes its requested orientation. Thus + // it indicates that this display chooses not to handle this request. + final boolean handled = getDisplayRotation().respectAppRequestedOrientation(); + if (config == null) { + return handled; + } + + if (handled && requestingContainer instanceof ActivityRecord) { + final ActivityRecord activityRecord = (ActivityRecord) requestingContainer; + final boolean kept = mWmService.mAtmService.updateDisplayOverrideConfigurationLocked( + config, activityRecord, false /* deferResume */, getDisplayId()); + activityRecord.frozenBeforeDestroy = true; + if (!kept) { + mWmService.mAtmService.mRootActivityContainer.resumeFocusedStacksTopActivities(); + } + } else { + // We have a new configuration to push so we need to update ATMS for now. + // TODO: Clean up display configuration push between ATMS and WMS after unification. + mWmService.mAtmService.updateDisplayOverrideConfigurationLocked( + config, null /* starting */, false /* deferResume */, getDisplayId()); + } + return handled; + } + /** * Determine the new desired orientation of this display. * @@ -1169,7 +1200,56 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return updateOrientationFromAppTokens(false /* forceUpdate */); } - boolean updateOrientationFromAppTokens(boolean forceUpdate) { + /** + * Update orientation of the target display, returning a non-null new Configuration if it has + * changed from the current orientation. If a non-null configuration is returned, someone must + * call {@link WindowManagerService#setNewDisplayOverrideConfiguration(Configuration, + * DisplayContent)} to tell the window manager it can unfreeze the screen. This will typically + * be done by calling {@link WindowManagerService#sendNewConfiguration(int)}. + */ + Configuration updateOrientationFromAppTokens(Configuration currentConfig, + IBinder freezeDisplayToken, boolean forceUpdate) { + if (!mDisplayReady) { + return null; + } + + Configuration config = null; + if (updateOrientationFromAppTokens(forceUpdate)) { + // If we changed the orientation but mOrientationChangeComplete is already true, + // we used seamless rotation, and we don't need to freeze the screen. + if (freezeDisplayToken != null && !mWmService.mRoot.mOrientationChangeComplete) { + final AppWindowToken atoken = getAppWindowToken(freezeDisplayToken); + if (atoken != null) { + atoken.startFreezingScreen(); + } + } + config = new Configuration(); + computeScreenConfiguration(config); + } else if (currentConfig != null) { + // No obvious action we need to take, but if our current state mismatches the + // activity manager's, update it, disregarding font scale, which should remain set + // to the value of the previous configuration. + // Here we're calling Configuration#unset() instead of setToDefaults() because we + // need to keep override configs clear of non-empty values (e.g. fontSize). + mTmpConfiguration.unset(); + mTmpConfiguration.updateFrom(currentConfig); + computeScreenConfiguration(mTmpConfiguration); + if (currentConfig.diff(mTmpConfiguration) != 0) { + mWaitingForConfig = true; + setLayoutNeeded(); + int[] anim = new int[2]; + getDisplayPolicy().selectRotationAnimationLw(anim); + + mWmService.startFreezingDisplayLocked(anim[0], anim[1], this); + config = new Configuration(mTmpConfiguration); + } + } + + return config; + } + + + private boolean updateOrientationFromAppTokens(boolean forceUpdate) { final int req = getOrientation(); if (req != mLastOrientation || forceUpdate) { mLastOrientation = req; diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 7aabc15d9860..bcc7be42333b 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -329,6 +329,15 @@ public class DisplayRotation { return mFixedToUserRotation; } + /** + * Returns {@code true} if this display rotation takes app requested orientation into + * consideration; {@code false} otherwise. For the time being the only case where this is {@code + * false} is when {@link #isFixedToUserRotation()} is {@code true}. + */ + boolean respectAppRequestedOrientation() { + return !mFixedToUserRotation; + } + public int getLandscapeRotation() { return mLandscapeRotation; } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index c1e9a7353fd5..fc1c65cf5807 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -49,7 +49,6 @@ import android.view.SurfaceControl; import com.android.server.policy.WindowManagerPolicy; import java.io.PrintWriter; -import java.util.Arrays; import java.util.Set; import java.util.function.Consumer; @@ -70,9 +69,9 @@ final class InputMonitor { private final UpdateInputForAllWindowsConsumer mUpdateInputForAllWindowsConsumer = new UpdateInputForAllWindowsConsumer(); - private int mDisplayId; + private final int mDisplayId; - SurfaceControl.Transaction mInputTransaction = new SurfaceControl.Transaction(); + private final SurfaceControl.Transaction mInputTransaction; /** * The set of input consumer added to the window manager by name, which consumes input events @@ -109,6 +108,7 @@ final class InputMonitor { public InputMonitor(WindowManagerService service, int displayId) { mService = service; mDisplayId = displayId; + mInputTransaction = mService.mRoot.getDisplayContent(mDisplayId).getPendingTransaction(); } private void addInputConsumer(String name, InputConsumerImpl consumer) { @@ -397,7 +397,7 @@ final class InputMonitor { wallpaperInputConsumer.show(mInputTransaction, 0); } - mInputTransaction.apply(); + dc.scheduleAnimation(); Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java index 8ec97c5117f1..6f92e647d301 100644 --- a/services/core/java/com/android/server/wm/RootActivityContainer.java +++ b/services/core/java/com/android/server/wm/RootActivityContainer.java @@ -597,11 +597,15 @@ class RootActivityContainer extends ConfigurationContainer // Force-update the orientation from the WindowManager, since we need the true configuration // to send to the client now. - final Configuration config = mWindowManager.updateOrientationFromAppTokens( - getDisplayOverrideConfiguration(displayId), - starting != null && starting.mayFreezeScreenLocked(starting.app) - ? starting.appToken : null, - displayId, true /* forceUpdate */); + final DisplayContent displayContent = mRootWindowContainer.getDisplayContent(displayId); + Configuration config = null; + if (displayContent != null) { + config = displayContent.updateOrientationFromAppTokens( + getDisplayOverrideConfiguration(displayId), + starting != null && starting.mayFreezeScreenLocked(starting.app) + ? starting.appToken : null, + true /* forceUpdate */); + } if (starting != null && markFrozenIfConfigChanged && config != null) { starting.frozenBeforeDestroy = true; } @@ -1519,6 +1523,13 @@ class RootActivityContainer extends ConfigurationContainer for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { final ActivityDisplay display = mActivityDisplays.get(displayNdx); for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + // Stacks and activities could be removed while putting activities to sleep if + // the app process was gone. This prevents us getting exception by accessing an + // invalid stack index. + if (stackNdx >= display.getChildCount()) { + continue; + } + final ActivityStack stack = display.getChildAt(stackNdx); if (allowDelay) { allSleep &= stack.goToSleepIfPossible(shuttingDown); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index b10fd31ba7ad..d334bd298ada 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -44,6 +44,7 @@ import android.app.ActivityManager.TaskDescription; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; +import android.os.IBinder; import android.util.EventLog; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -328,6 +329,23 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta return boundsChange; } + @Override + public boolean onDescendantOrientationChanged(IBinder freezeDisplayToken, + ConfigurationContainer requestingContainer) { + if (super.onDescendantOrientationChanged(freezeDisplayToken, requestingContainer)) { + return true; + } + + // No one in higher hierarchy handles this request, let's adjust our bounds to fulfill + // it if possible. + // TODO: Move to TaskRecord after unification is done. + if (mTaskRecord != null) { + mTaskRecord.onConfigurationChanged(mTaskRecord.getParent().getConfiguration()); + return true; + } + return false; + } + void resize(boolean relayout, boolean forced) { if (setBounds(getRequestedOverrideBounds(), forced) != BOUNDS_CHANGE_NONE && relayout) { getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 11d9ebb59db0..ee74bdfaa113 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -996,15 +996,20 @@ public class TaskStack extends WindowContainer<Task> implements * Adjusts the stack bounds if the IME is visible. * * @param imeWin The IME window. + * @param keepLastAmount Use {@code true} to keep the last adjusted amount from + * {@link DockedStackDividerController} for adjusting the stack bounds, + * Use {@code false} to reset adjusted amount as 0. + * @see #updateAdjustForIme(float, float, boolean) */ - void setAdjustedForIme(WindowState imeWin, boolean forceUpdate) { + void setAdjustedForIme(WindowState imeWin, boolean keepLastAmount) { mImeWin = imeWin; mImeGoingAway = false; - if (!mAdjustedForIme || forceUpdate) { + if (!mAdjustedForIme || keepLastAmount) { mAdjustedForIme = true; - mAdjustImeAmount = 0f; - mAdjustDividerAmount = 0f; - updateAdjustForIme(0f, 0f, true /* force */); + DockedStackDividerController controller = getDisplayContent().mDividerControllerLocked; + final float adjustImeAmount = keepLastAmount ? controller.mLastAnimationProgress : 0f; + final float adjustDividerAmount = keepLastAmount ? controller.mLastDividerProgress : 0f; + updateAdjustForIme(adjustImeAmount, adjustDividerAmount, true /* force */); } } diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java index c006a7ba44d7..b2194190f4f4 100644 --- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java +++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java @@ -66,8 +66,11 @@ public class TaskTapPointerEventListener implements PointerEventListener { // method target window will lose the focus. return; } - mDisplayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP, - mDisplayContent, true /* includingParents */); + WindowContainer parent = mDisplayContent.getParent(); + if (parent != null) { + parent.positionChildAt(WindowContainer.POSITION_TOP, mDisplayContent, + true /* includingParents */); + } } }; } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 651089d6ce10..32c5a3b8688e 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -31,10 +31,12 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.CallSuper; import android.annotation.IntDef; +import android.annotation.Nullable; import android.app.WindowConfiguration; import android.content.res.Configuration; import android.graphics.Point; import android.graphics.Rect; +import android.os.IBinder; import android.util.Pools; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -698,8 +700,58 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } } + /** + * Called when this container or one of its descendants changed its requested orientation, and + * wants this container to handle it or pass it to its parent. + * + * @param freezeDisplayToken freeze this app window token if display needs to freeze + * @param requestingContainer the container which orientation request has changed + * @return {@code true} if handled; {@code false} otherwise. + */ + boolean onDescendantOrientationChanged(@Nullable IBinder freezeDisplayToken, + @Nullable ConfigurationContainer requestingContainer) { + final WindowContainer parent = getParent(); + if (parent == null) { + return false; + } + return parent.onDescendantOrientationChanged(freezeDisplayToken, + requestingContainer); + } + + /** + * Calls {@link #setOrientation(int, IBinder, ActivityRecord)} with {@code null} to the last 2 + * parameters. + * + * @param orientation the specified orientation. + */ void setOrientation(int orientation) { + setOrientation(orientation, null /* freezeDisplayToken */, + null /* ActivityRecord */); + } + + /** + * Sets the specified orientation of this container. It percolates this change upward along the + * hierarchy to let each level of the hierarchy a chance to respond to it. + * + * @param orientation the specified orientation. Needs to be one of {@link + * android.content.pm.ActivityInfo.ScreenOrientation}. + * @param freezeDisplayToken uses this token to freeze display if orientation change is not + * done. Display will not be frozen if this is {@code null}, which + * should only happen in tests. + * @param requestingContainer the container which orientation request has changed. Mostly used + * to ensure it gets correct configuration. + */ + void setOrientation(int orientation, @Nullable IBinder freezeDisplayToken, + @Nullable ConfigurationContainer requestingContainer) { + final boolean changed = mOrientation != orientation; mOrientation = orientation; + if (!changed) { + return; + } + final WindowContainer parent = getParent(); + if (parent != null) { + onDescendantOrientationChanged(freezeDisplayToken, requestingContainer); + } } int getOrientation() { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index e3ced8350f0d..b6a4a51c41f2 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2383,85 +2383,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - @Override - public Configuration updateOrientationFromAppTokens(Configuration currentConfig, - IBinder freezeThisOneIfNeeded, int displayId) { - return updateOrientationFromAppTokens(currentConfig, freezeThisOneIfNeeded, displayId, - false /* forceUpdate */); - } - - public Configuration updateOrientationFromAppTokens(Configuration currentConfig, - IBinder freezeThisOneIfNeeded, int displayId, boolean forceUpdate) { - if (!checkCallingPermission(MANAGE_APP_TOKENS, "updateOrientationFromAppTokens()")) { - throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); - } - - final Configuration config; - final long ident = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - config = updateOrientationFromAppTokensLocked(currentConfig, freezeThisOneIfNeeded, - displayId, forceUpdate); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - - return config; - } - - /** - * Update orientation of the target display, returning a non-null new Configuration if it has - * changed from the current orientation. If a non-null configuration is returned, someone must - * call {@link #setNewDisplayOverrideConfiguration(Configuration, int)} to tell the window - * manager it can unfreeze the screen. This will typically be done by calling - * {@link #sendNewConfiguration(int)}. - * - * @see android.view.IWindowManager#updateOrientationFromAppTokens(Configuration, IBinder, int) - */ - private Configuration updateOrientationFromAppTokensLocked(Configuration currentConfig, - IBinder freezeThisOneIfNeeded, int displayId, boolean forceUpdate) { - if (!mDisplayReady) { - return null; - } - Configuration config = null; - - final DisplayContent dc = mRoot.getDisplayContent(displayId); - if (dc != null && dc.updateOrientationFromAppTokens(forceUpdate)) { - // If we changed the orientation but mOrientationChangeComplete is already true, - // we used seamless rotation, and we don't need to freeze the screen. - if (freezeThisOneIfNeeded != null && !mRoot.mOrientationChangeComplete) { - final AppWindowToken atoken = mRoot.getAppWindowToken(freezeThisOneIfNeeded); - if (atoken != null) { - atoken.startFreezingScreen(); - } - } - config = computeNewConfigurationLocked(displayId); - - } else if (currentConfig != null) { - // No obvious action we need to take, but if our current state mismatches the activity - // manager's, update it, disregarding font scale, which should remain set to the value - // of the previous configuration. - // Here we're calling Configuration#unset() instead of setToDefaults() because we need - // to keep override configs clear of non-empty values (e.g. fontSize). - mTempConfiguration.unset(); - mTempConfiguration.updateFrom(currentConfig); - final DisplayContent displayContent = mRoot.getDisplayContent(displayId); - displayContent.computeScreenConfiguration(mTempConfiguration); - if (currentConfig.diff(mTempConfiguration) != 0) { - displayContent.mWaitingForConfig = true; - displayContent.setLayoutNeeded(); - int anim[] = new int[2]; - displayContent.getDisplayPolicy().selectRotationAnimationLw(anim); - - startFreezingDisplayLocked(anim[0], anim[1], displayContent); - config = new Configuration(mTempConfiguration); - } - } - - return config; - } - void setNewDisplayOverrideConfiguration(Configuration overrideConfig, @NonNull DisplayContent dc) { if (dc.mWaitingForConfig) { diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index 58fd30e225c1..729aed171ab1 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -25,6 +25,7 @@ #include <android/hardware/gnss/1.0/IGnssMeasurement.h> #include <android/hardware/gnss/1.1/IGnssMeasurement.h> #include <android/hardware/gnss/2.0/IGnssMeasurement.h> +#include <android/hardware/gnss/measurement_corrections/1.0/IMeasurementCorrections.h> #include <nativehelper/JNIHelp.h> #include "jni.h" #include "hardware_legacy/power.h" @@ -68,6 +69,25 @@ static jmethodID method_reportMeasurementData; static jmethodID method_reportNavigationMessages; static jmethodID method_reportLocationBatch; static jmethodID method_reportGnssServiceDied; +static jmethodID method_correctionsGetLatitudeDegrees; +static jmethodID method_correctionsGetLongitudeDegrees; +static jmethodID method_correctionsGetAltitudeMeters; +static jmethodID method_correctionsGetToaGpsNanosecondsOfWeek; +static jmethodID method_correctionsGetSingleSatCorrectionList; +static jmethodID method_listSize; +static jmethodID method_correctionListGet; +static jmethodID method_correctionSatFlags; +static jmethodID method_correctionSatConstType; +static jmethodID method_correctionSatId; +static jmethodID method_correctionSatCarrierFreq; +static jmethodID method_correctionSatIsLos; +static jmethodID method_correctionSatEpl; +static jmethodID method_correctionSatEplUnc; +static jmethodID method_correctionSatRefPlane; +static jmethodID method_correctionPlaneLatDeg; +static jmethodID method_correctionPlaneLngDeg; +static jmethodID method_correctionPlaneAltDeg; +static jmethodID method_correctionPlaneAzimDeg; /* * Save a pointer to JavaVm to attach/detach threads executing @@ -105,7 +125,10 @@ using android::hardware::gnss::V1_0::IGnssNiCallback; using android::hardware::gnss::V1_0::IGnssXtra; using android::hardware::gnss::V1_0::IGnssXtraCallback; -using android::hardware::gnss::V1_1::IGnssCallback; +using android::hardware::gnss::V2_0::IGnssCallback; +using android::hardware::gnss::measurement_corrections::V1_0::MeasurementCorrections; +using android::hardware::gnss::measurement_corrections::V1_0::SingleSatCorrection; +using android::hardware::gnss::measurement_corrections::V1_0::ReflectingPlane; using android::hidl::base::V1_0::IBase; @@ -123,6 +146,9 @@ using IGnssMeasurementCallback_V2_0 = android::hardware::gnss::V2_0::IGnssMeasur using IAGnssRil_V1_0 = android::hardware::gnss::V1_0::IAGnssRil; using IAGnssRil_V2_0 = android::hardware::gnss::V2_0::IAGnssRil; +using IMeasurementCorrections = + android::hardware::gnss::measurement_corrections::V1_0::IMeasurementCorrections; + struct GnssDeathRecipient : virtual public hidl_death_recipient { // hidl_death_recipient interface @@ -155,6 +181,11 @@ sp<IGnssMeasurement_V1_0> gnssMeasurementIface = nullptr; sp<IGnssMeasurement_V1_1> gnssMeasurementIface_V1_1 = nullptr; sp<IGnssMeasurement_V2_0> gnssMeasurementIface_V2_0 = nullptr; sp<IGnssNavigationMessage> gnssNavigationMessageIface = nullptr; +sp<IMeasurementCorrections> gnssCorrectionsIface = nullptr; +// This boolean is needed to ensure that Gnsss Measurement Corrections related method are only +// initalized when needed which will be few devices initially +bool firstGnssMeasurementCorrectionInjected = false; + #define WAKE_LOCK_NAME "GPS" @@ -415,6 +446,8 @@ struct GnssCallback : public IGnssCallback { // New in 1.1 Return<void> gnssNameCb(const android::hardware::hidl_string& name) override; + Return<void> gnssSetCapabilitiesCb_2_0(uint32_t capabilities) override; + // TODO(b/73306084): Reconsider allocation cost vs threadsafety on these statics static const char* sNmeaString; static size_t sNmeaStringLength; @@ -537,6 +570,10 @@ Return<void> GnssCallback::gnssSetCapabilitesCb(uint32_t capabilities) { return Void(); } +Return<void> GnssCallback::gnssSetCapabilitiesCb_2_0(uint32_t capabilities) { + return GnssCallback::gnssSetCapabilitesCb(capabilities); +} + Return<void> GnssCallback::gnssAcquireWakelockCb() { acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME); return Void(); @@ -1283,6 +1320,12 @@ static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass if (gnssHal_V2_0 != nullptr) { // TODO(b/119638366): getExtensionGnssMeasurement_1_1 from gnssHal_V2_0 auto gnssMeasurement = gnssHal_V2_0->getExtensionGnssMeasurement_2_0(); + auto gnssCorrections = gnssHal_V2_0->getExtensionMeasurementCorrections(); + if (!gnssCorrections.isOk()) { + ALOGD("Unable to get a handle to GnssMeasurementCorrections interface"); + } else { + gnssCorrectionsIface = gnssCorrections; + } if (!gnssMeasurement.isOk()) { ALOGD("Unable to get a handle to GnssMeasurement_V2_0"); } else { @@ -1386,11 +1429,14 @@ static jboolean android_location_GnssLocationProvider_init(JNIEnv* env, jobject sp<IGnssCallback> gnssCbIface = new GnssCallback(); Return<bool> result = false; - if (gnssHal_V1_1 != nullptr) { + if (gnssHal_V2_0 != nullptr) { + result = gnssHal_V2_0->setCallback_2_0(gnssCbIface); + } else if (gnssHal_V1_1 != nullptr) { result = gnssHal_V1_1->setCallback_1_1(gnssCbIface); } else { result = gnssHal->setCallback(gnssCbIface); } + if (!result.isOk() || !result) { ALOGE("SetCallback for Gnss Interface fails\n"); return JNI_FALSE; @@ -1933,6 +1979,150 @@ static jboolean android_location_GnssMeasurementsProvider_stop_measurement_colle return boolToJbool(result.isOk()); } +static jboolean android_location_GnssMeasurementsProvider_inject_gnss_measurement_corrections( + JNIEnv* env, + jobject obj /* clazz*/, + jobject correctionsObj) { + + if (gnssCorrectionsIface == nullptr) { + ALOGW("Trying to inject GNSS corrections on a chipset that does not support them."); + return JNI_FALSE; + } + if (firstGnssMeasurementCorrectionInjected == false) { + jclass measCorrClass = env->GetObjectClass(correctionsObj); + method_correctionsGetLatitudeDegrees = env->GetMethodID( + measCorrClass,"getLatitudeDegrees", "()D"); + + method_correctionsGetLongitudeDegrees = env->GetMethodID( + measCorrClass, "getLongitudeDegrees", "()D"); + + method_correctionsGetAltitudeMeters = env->GetMethodID( + measCorrClass, "getAltitudeMeters", "()D"); + + method_correctionsGetToaGpsNanosecondsOfWeek = env->GetMethodID( + measCorrClass, "getToaGpsNanosecondsOfWeek", "()J"); + + method_correctionsGetSingleSatCorrectionList = env->GetMethodID( + measCorrClass, "getSingleSatCorrectionList", "()Ljava.util.List;"); + } + + jdouble latitudeDegreesCorr = env->CallDoubleMethod( + correctionsObj, method_correctionsGetLatitudeDegrees); + jdouble longitudeDegreesCorr = env->CallDoubleMethod( + correctionsObj, method_correctionsGetLongitudeDegrees); + jdouble altitudeDegreesCorr = env->CallDoubleMethod( + correctionsObj, method_correctionsGetAltitudeMeters); + jlong toaGpsNanosOfWeek = env->CallLongMethod( + correctionsObj, method_correctionsGetToaGpsNanosecondsOfWeek); + jobject singleSatCorrectionList = env->CallObjectMethod(correctionsObj, + method_correctionsGetSingleSatCorrectionList); + + if (firstGnssMeasurementCorrectionInjected == false) { + jclass corrListClass = env->GetObjectClass(singleSatCorrectionList); + method_listSize = env->GetMethodID(corrListClass, "size", "()I"); + method_correctionListGet = env->GetMethodID( + corrListClass, "get", "(I)Landroid/location/GnssSingleSatCorrection;"); + } + + auto len = (singleSatCorrectionList == nullptr) + ? 0 + : env->CallIntMethod(singleSatCorrectionList, method_listSize); + hidl_vec<SingleSatCorrection> list(len); + + for (uint16_t i = 0; i < len; ++i) { + jobject singleSatCorrectionObj = env->CallObjectMethod( + singleSatCorrectionList, method_correctionListGet, i); + + if (firstGnssMeasurementCorrectionInjected == false) { + jclass singleSatCorrClass = env->GetObjectClass(singleSatCorrectionObj); + method_correctionSatFlags = env->GetMethodID( + singleSatCorrClass, "getSingleSatCorrectionFlags", "()I"); + method_correctionSatConstType = env->GetMethodID( + singleSatCorrClass, "getConstellationType", "()I"); + method_correctionSatId= env->GetMethodID( + singleSatCorrClass, "getSatId", "()I"); + method_correctionSatCarrierFreq = env->GetMethodID( + singleSatCorrClass, "getCarrierFrequencyHz", "()F"); + method_correctionSatIsLos = env->GetMethodID( + singleSatCorrClass,"getSatIsLos", "()Z"); + method_correctionSatEpl = env->GetMethodID( + singleSatCorrClass, "getExcessPathLengthMeters", "()F"); + method_correctionSatEplUnc = env->GetMethodID( + singleSatCorrClass, "getExcessPathLengthUncertaintyMeters", "()F"); + method_correctionSatRefPlane = env->GetMethodID( + singleSatCorrClass, "getReflectingPlane", + "()Landroid/location/GnssReflectingPlane;"); + } + + jint correctionFlags = + env->CallIntMethod(singleSatCorrectionObj, method_correctionSatFlags); + jint constType = env->CallIntMethod(singleSatCorrectionObj, + method_correctionSatConstType); + jint satId = + env->CallIntMethod(singleSatCorrectionObj, method_correctionSatId); + jfloat carrierFreqHz = env->CallFloatMethod( + singleSatCorrectionObj, method_correctionSatCarrierFreq); + jboolean satIsLos = env->CallBooleanMethod(singleSatCorrectionObj, + method_correctionSatIsLos); + jfloat eplMeters = + env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEpl); + jfloat eplUncMeters = env->CallFloatMethod(singleSatCorrectionObj, + method_correctionSatEplUnc); + jobject reflectingPlaneObj = env->CallObjectMethod( + singleSatCorrectionObj, method_correctionSatRefPlane); + + if (firstGnssMeasurementCorrectionInjected == false) { + jclass refPlaneClass = env->GetObjectClass(reflectingPlaneObj); + method_correctionPlaneLatDeg = env->GetMethodID( + refPlaneClass, "getLatitudeDegrees", "()D"); + method_correctionPlaneLngDeg = env->GetMethodID( + refPlaneClass, "getLongitudeDegrees", "()D"); + method_correctionPlaneAltDeg = env->GetMethodID( + refPlaneClass, "getAltitudeMeters", "()D"); + method_correctionPlaneAzimDeg = env->GetMethodID( + refPlaneClass, "getAzimuthDegrees", "()D"); + } + + jdouble latitudeDegreesRefPlane = env->CallDoubleMethod( + reflectingPlaneObj, method_correctionPlaneLatDeg); + jdouble longitudeDegreesRefPlane = env->CallDoubleMethod( + reflectingPlaneObj, method_correctionPlaneLngDeg); + jdouble altitudeDegreesRefPlane = env->CallDoubleMethod( + reflectingPlaneObj, method_correctionPlaneAltDeg); + jdouble azimuthDegreeRefPlane = env->CallDoubleMethod( + reflectingPlaneObj, method_correctionPlaneAzimDeg); + ReflectingPlane reflectingPlane = { + .latitudeDegrees = latitudeDegreesRefPlane, + .longitudeDegrees = longitudeDegreesRefPlane, + .altitudeMeters = altitudeDegreesRefPlane, + .azimuthDegrees = azimuthDegreeRefPlane, + }; + + SingleSatCorrection singleSatCorrection = { + .singleSatCorrectionFlags = static_cast<uint16_t>(correctionFlags), + .constellation = static_cast<GnssConstellationType>(constType), + .svid = static_cast<uint16_t>(satId), + .carrierFrequencyHz = carrierFreqHz, + .satIsLos = static_cast<bool>(satIsLos), + .excessPathLengthMeters = eplMeters, + .excessPathLengthUncertaintyMeters = eplUncMeters, + .reflectingPlane = reflectingPlane, + }; + list[i] = singleSatCorrection; + } + MeasurementCorrections measurementCorrections = { + .latitudeDegrees = latitudeDegreesCorr, + .longitudeDegrees = longitudeDegreesCorr, + .altitudeMeters = altitudeDegreesCorr, + .toaGpsNanosecondsOfWeek = static_cast<uint64_t>(toaGpsNanosOfWeek), + .satCorrections = list, + }; + + gnssCorrectionsIface->setCorrections(measurementCorrections); + firstGnssMeasurementCorrectionInjected = true; + return JNI_TRUE; +} + static jboolean android_location_GnssNavigationMessageProvider_is_navigation_message_supported( JNIEnv* env, jclass clazz) { @@ -2310,19 +2500,20 @@ static const JNINativeMethod sGeofenceMethods[] = { }; static const JNINativeMethod sMeasurementMethods[] = { - /* name, signature, funcPtr */ - {"native_is_measurement_supported", - "()Z", - reinterpret_cast<void *>( - android_location_GnssMeasurementsProvider_is_measurement_supported)}, - {"native_start_measurement_collection", - "(Z)Z", - reinterpret_cast<void *>( - android_location_GnssMeasurementsProvider_start_measurement_collection)}, - {"native_stop_measurement_collection", - "()Z", - reinterpret_cast<void *>( - android_location_GnssMeasurementsProvider_stop_measurement_collection)}, + /* name, signature, funcPtr */ + {"native_is_measurement_supported", "()Z", + reinterpret_cast<void*>( + android_location_GnssMeasurementsProvider_is_measurement_supported)}, + {"native_start_measurement_collection", "(Z)Z", + reinterpret_cast<void*>( + android_location_GnssMeasurementsProvider_start_measurement_collection)}, + {"native_stop_measurement_collection", "()Z", + reinterpret_cast<void*>( + android_location_GnssMeasurementsProvider_stop_measurement_collection)}, + {"native_inject_gnss_measurement_corrections", + "(Landroid/location/GnssMeasurementCorrections;)Z", + reinterpret_cast<void*>( + android_location_GnssMeasurementsProvider_inject_gnss_measurement_corrections)}, }; static const JNINativeMethod sNavigationMessageMethods[] = { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index bd3dfe96a67e..409e7f4d6bdb 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -2105,6 +2105,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return Settings.Global.getInt(mContext.getContentResolver(), name, def); } + @Nullable String settingsGlobalGetString(String name) { return Settings.Global.getString(mContext.getContentResolver(), name); } @@ -13901,7 +13902,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkNotNull(who, "ComponentName is null"); enforceDeviceOwner(who); - switch (mInjector.settingsGlobalGetString(PRIVATE_DNS_MODE)) { + String currentMode = mInjector.settingsGlobalGetString(PRIVATE_DNS_MODE); + if (currentMode == null) { + currentMode = ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE_FALLBACK; + } + switch (currentMode) { case ConnectivityManager.PRIVATE_DNS_MODE_OFF: return PRIVATE_DNS_MODE_OFF; case ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC: diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java index b253e0ae0a40..b8723c520cf8 100644 --- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java +++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java @@ -175,6 +175,32 @@ public class BackupManagerServiceTest { assertThat(serviceUsers.get(mUserOneId)).isEqualTo(mUserOneService); } + /** Test that the service unregisters users when stopped. */ + @Test + public void testStopServiceForUser_forRegisteredUser_unregistersCorrectUser() throws Exception { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService); + + backupManagerService.stopServiceForUser(mUserOneId); + + SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers(); + assertThat(serviceUsers.size()).isEqualTo(1); + assertThat(serviceUsers.get(mUserOneId)).isNull(); + assertThat(serviceUsers.get(mUserTwoId)).isEqualTo(mUserTwoService); + } + + /** Test that the service unregisters users when stopped. */ + @Test + public void testStopServiceForUser_forUnknownUser_doesNothing() throws Exception { + BackupManagerService backupManagerService = createService(); + + backupManagerService.stopServiceForUser(mUserOneId); + + SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers(); + assertThat(serviceUsers.size()).isEqualTo(0); + } + /** * 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. diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java index cff0521bba50..1a16e568ca53 100644 --- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java @@ -46,9 +46,11 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; 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.anyString; +import static org.mockito.ArgumentMatchers.eq; import android.app.ActivityManagerInternal; import android.app.AlarmManager; @@ -56,6 +58,7 @@ import android.app.IActivityManager; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.hardware.Sensor; import android.hardware.SensorManager; import android.location.LocationManager; import android.location.LocationProvider; @@ -70,6 +73,7 @@ import android.os.SystemClock; import androidx.test.runner.AndroidJUnit4; +import com.android.server.deviceidle.ConstraintController; import com.android.server.net.NetworkPolicyManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; @@ -77,6 +81,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Answers; import org.mockito.Mock; import org.mockito.MockitoSession; import org.mockito.quality.Strictness; @@ -89,6 +94,7 @@ public class DeviceIdleControllerTest { private DeviceIdleController mDeviceIdleController; private AnyMotionDetectorForTest mAnyMotionDetector; private AppStateTrackerForTest mAppStateTracker; + private DeviceIdleController.Constants mConstants; private InjectorForTest mInjector; private MockitoSession mMockingSession; @@ -97,7 +103,7 @@ public class DeviceIdleControllerTest { @Mock private ConnectivityService mConnectivityService; @Mock - private DeviceIdleController.Constants mConstants; + private ContentResolver mContentResolver; @Mock private IActivityManager mIActivityManager; @Mock @@ -108,10 +114,13 @@ public class DeviceIdleControllerTest { private PowerManager.WakeLock mWakeLock; @Mock private PowerManagerInternal mPowerManagerInternal; + @Mock + private SensorManager mSensorManager; class InjectorForTest extends DeviceIdleController.Injector { ConnectivityService connectivityService; LocationManager locationManager; + ConstraintController constraintController; InjectorForTest(Context ctx) { super(ctx); @@ -139,37 +148,51 @@ public class DeviceIdleControllerTest { } @Override - DeviceIdleController.Constants getConstants(DeviceIdleController controller, - Handler handler, - ContentResolver resolver) { - return mConstants; - } - - @Override LocationManager getLocationManager() { return locationManager; } @Override DeviceIdleController.MyHandler getHandler(DeviceIdleController controller) { - return mock(DeviceIdleController.MyHandler.class); + return mock(DeviceIdleController.MyHandler.class, Answers.RETURNS_DEEP_STUBS); } @Override PowerManager getPowerManager() { return mPowerManager; } + + @Override + SensorManager getSensorManager() { + return mSensorManager; + } + + @Override + ConstraintController getConstraintController( + Handler handler, DeviceIdleController.LocalService localService) { + return constraintController; + } + + @Override + boolean useMotionSensor() { + return true; + } } private class AnyMotionDetectorForTest extends AnyMotionDetector { boolean isMonitoring = false; AnyMotionDetectorForTest() { - super(mPowerManager, mock(Handler.class), mock(SensorManager.class), + super(mPowerManager, mock(Handler.class), mSensorManager, mock(DeviceIdleCallback.class), 0.5f); } @Override + public boolean hasSensor() { + return true; + } + + @Override public void checkForAnyMotion() { isMonitoring = true; } @@ -201,7 +224,7 @@ public class DeviceIdleControllerTest { mMockingSession = mockitoSession() .initMocks(this) .strictness(Strictness.LENIENT) - .mockStatic(LocalServices.class) + .spyStatic(LocalServices.class) .startMocking(); spyOn(getContext()); doReturn(null).when(getContext()).registerReceiver(any(), any()); @@ -218,9 +241,13 @@ public class DeviceIdleControllerTest { when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock); doNothing().when(mWakeLock).acquire(); doNothing().when(mAlarmManager).set(anyInt(), anyLong(), anyString(), any(), any()); + doReturn(mock(Sensor.class)).when(mSensorManager) + .getDefaultSensor(eq(Sensor.TYPE_SIGNIFICANT_MOTION), eq(true)); + doReturn(true).when(mSensorManager).registerListener(any(), any(), anyInt()); mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper()); mAnyMotionDetector = new AnyMotionDetectorForTest(); mInjector = new InjectorForTest(getContext()); + doNothing().when(mContentResolver).registerContentObserver(any(), anyBoolean(), any()); mDeviceIdleController = new DeviceIdleController(getContext(), mInjector); spyOn(mDeviceIdleController); doNothing().when(mDeviceIdleController).publishBinderService(any(), any()); @@ -228,6 +255,10 @@ public class DeviceIdleControllerTest { mDeviceIdleController.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); mDeviceIdleController.setDeepEnabledForTest(true); mDeviceIdleController.setLightEnabledForTest(true); + + // Get the same Constants object that mDeviceIdleController got. + mConstants = mInjector.getConstants(mDeviceIdleController, + mInjector.getHandler(mDeviceIdleController), mContentResolver); } @After @@ -235,9 +266,10 @@ public class DeviceIdleControllerTest { if (mMockingSession != null) { mMockingSession.finishMocking(); } - // DeviceIdleController adds this to LocalServices in the constructor, so we have to remove - // it after each test, otherwise, subsequent tests will fail. + // DeviceIdleController adds these to LocalServices in the constructor, so we have to remove + // them after each test, otherwise, subsequent tests will fail. LocalServices.removeServiceForTest(AppStateTracker.class); + LocalServices.removeServiceForTest(DeviceIdleController.LocalService.class); } @Test @@ -1456,8 +1488,8 @@ public class DeviceIdleControllerTest { private void setAlarmSoon(boolean isSoon) { if (isSoon) { - doReturn(SystemClock.elapsedRealtime() + mConstants.MIN_TIME_TO_ALARM / 2).when( - mAlarmManager).getNextWakeFromIdleTime(); + doReturn(SystemClock.elapsedRealtime() + mConstants.MIN_TIME_TO_ALARM / 2) + .when(mAlarmManager).getNextWakeFromIdleTime(); } else { doReturn(Long.MAX_VALUE).when(mAlarmManager).getNextWakeFromIdleTime(); } @@ -1481,27 +1513,36 @@ public class DeviceIdleControllerTest { assertFalse(mDeviceIdleController.isScreenOn()); break; case STATE_IDLE_PENDING: - assertTrue(mDeviceIdleController.mMotionListener.isActive()); + assertEquals( + mDeviceIdleController.hasMotionSensor(), + mDeviceIdleController.mMotionListener.isActive()); assertFalse(mAnyMotionDetector.isMonitoring); assertFalse(mDeviceIdleController.isCharging()); assertFalse(mDeviceIdleController.isScreenOn()); break; case STATE_SENSING: - assertTrue(mDeviceIdleController.mMotionListener.isActive()); - assertTrue(mAnyMotionDetector.isMonitoring); + assertEquals( + mDeviceIdleController.hasMotionSensor(), + mDeviceIdleController.mMotionListener.isActive()); + assertEquals( + mDeviceIdleController.hasMotionSensor(), + mAnyMotionDetector.isMonitoring); assertFalse(mDeviceIdleController.isCharging()); assertFalse(mDeviceIdleController.isScreenOn()); break; case STATE_LOCATING: - assertTrue(mDeviceIdleController.mMotionListener.isActive()); - assertTrue(mAnyMotionDetector.isMonitoring); + assertEquals( + mDeviceIdleController.hasMotionSensor(), + mDeviceIdleController.mMotionListener.isActive()); assertFalse(mDeviceIdleController.isCharging()); assertFalse(mDeviceIdleController.isScreenOn()); break; case STATE_IDLE: - assertTrue(mDeviceIdleController.mMotionListener.isActive() + if (mDeviceIdleController.hasMotionSensor()) { + assertTrue(mDeviceIdleController.mMotionListener.isActive() // If quick doze is enabled, the motion listener should NOT be active. || mDeviceIdleController.isQuickDozeEnabled()); + } assertFalse(mAnyMotionDetector.isMonitoring); assertFalse(mDeviceIdleController.isCharging()); assertFalse(mDeviceIdleController.isScreenOn()); @@ -1509,9 +1550,11 @@ public class DeviceIdleControllerTest { verifyLightStateConditions(LIGHT_STATE_OVERRIDE); break; case STATE_IDLE_MAINTENANCE: - assertTrue(mDeviceIdleController.mMotionListener.isActive() + if (mDeviceIdleController.hasMotionSensor()) { + assertTrue(mDeviceIdleController.mMotionListener.isActive() // If quick doze is enabled, the motion listener should NOT be active. || mDeviceIdleController.isQuickDozeEnabled()); + } assertFalse(mAnyMotionDetector.isMonitoring); assertFalse(mDeviceIdleController.isCharging()); assertFalse(mDeviceIdleController.isScreenOn()); diff --git a/services/tests/mockingservicestests/src/com/android/server/deviceidle/BluetoothConstraintTest.java b/services/tests/mockingservicestests/src/com/android/server/deviceidle/BluetoothConstraintTest.java new file mode 100644 index 000000000000..f74ac1f5317f --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/deviceidle/BluetoothConstraintTest.java @@ -0,0 +1,147 @@ +/* + * 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.deviceidle; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder; +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.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.eq; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; +import android.bluetooth.BluetoothProfile; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.DeviceIdleController; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; + +import java.util.Arrays; + +/** + * Tests for {@link com.android.server.deviceidle.BluetoothConstraint}. + */ +@RunWith(AndroidJUnit4.class) +public class BluetoothConstraintTest { + + private MockitoSession mMockingSession; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private Context mContext; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private Handler mHandler; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private BluetoothManager mBluetoothManager; + + @Mock + private DeviceIdleController.LocalService mDeviceIdleService; + + private BluetoothConstraint mConstraint; + + @Before + public void setUp() { + mMockingSession = mockitoSession() + .initMocks(this) + .strictness(Strictness.LENIENT) + .startMocking(); + doReturn(mBluetoothManager) + .when(mContext).getSystemService(BluetoothManager.class); + mConstraint = new BluetoothConstraint(mContext, mHandler, mDeviceIdleService); + } + + @After + public void tearDown() { + if (mMockingSession != null) { + mMockingSession.finishMocking(); + } + } + + @Test + public void testIsConnected_noBluetoothAdapter() { + doReturn(null).when(mBluetoothManager).getAdapter(); + assertFalse(BluetoothConstraint.isBluetoothConnected(mBluetoothManager)); + } + + @Test + public void testIsConnected_noConnectedDevice() { + enableBluetooth(true); + assertFalse(BluetoothConstraint.isBluetoothConnected(mBluetoothManager)); + } + + @Test + public void testIsConnected_twoConnectedDevices() { + enableBluetooth(true, mock(BluetoothDevice.class), mock(BluetoothDevice.class)); + assertTrue(BluetoothConstraint.isBluetoothConnected(mBluetoothManager)); + } + + @Test + public void testStartMonitoring_updatesActiveAtCorrectTimes() { + // First setup -> no callbacks should fire. + BluetoothConstraint constraint = mConstraint; + verify(mDeviceIdleService, never()).onConstraintStateChanged(any(), anyBoolean()); + verify(mContext, never()).registerReceiver(eq(constraint.mReceiver), any()); + + InOrder order = inOrder(mDeviceIdleService); + + // No devices -> active=false should be triggered. + enableBluetooth(true); + constraint.startMonitoring(); + order.verify(mDeviceIdleService, times(1)).onConstraintStateChanged(any(), eq(false)); + + // One device -> active=true should be triggered. + enableBluetooth(true, mock(BluetoothDevice.class)); + constraint.mReceiver.onReceive( + mContext, new Intent(BluetoothDevice.ACTION_ACL_CONNECTED)); + constraint.startMonitoring(); + order.verify(mDeviceIdleService, times(1)).exitIdle(eq("bluetooth")); + + // Stop monitoring -> broadcast receiver should be unregistered. + constraint.stopMonitoring(); + verify(mContext, times(1)).unregisterReceiver(eq(constraint.mReceiver)); + order.verifyNoMoreInteractions(); + + } + + private void enableBluetooth(boolean enabled, BluetoothDevice... devices) { + when(mBluetoothManager.getAdapter().isEnabled()).thenReturn(enabled); + when(mBluetoothManager.getConnectedDevices(eq(BluetoothProfile.GATT))) + .thenReturn(Arrays.asList(devices)); + } +} diff --git a/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java b/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java index 52f434db3be3..edd89f9e61d1 100644 --- a/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java +++ b/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java @@ -52,8 +52,8 @@ public class AppOpsNotedWatcherTest { // 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); + appOpsManager.startWatchingNoted(new int[]{AppOpsManager.OP_FINE_LOCATION, + AppOpsManager.OP_RECORD_AUDIO}, listener); fail("Watching noted ops shoudl require " + Manifest.permission.WATCH_APPOPS); } catch (SecurityException expected) { /*ignored*/ @@ -67,23 +67,23 @@ public class AppOpsNotedWatcherTest { // Start watching noted ops final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class); - appOpsManager.startWatchingNoted(new String[]{AppOpsManager.OPSTR_FINE_LOCATION, - AppOpsManager.OPSTR_CAMERA}, listener); + appOpsManager.startWatchingNoted(new int[]{AppOpsManager.OP_FINE_LOCATION, + AppOpsManager.OP_CAMERA}, listener); // Note some ops - appOpsManager.noteOp(AppOpsManager.OPSTR_FINE_LOCATION, Process.myUid(), + appOpsManager.noteOp(AppOpsManager.OP_FINE_LOCATION, Process.myUid(), getContext().getPackageName()); - appOpsManager.noteOp(AppOpsManager.OPSTR_CAMERA, Process.myUid(), + appOpsManager.noteOp(AppOpsManager.OP_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), + .times(1)).onOpNoted(eq(AppOpsManager.OP_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), + .times(1)).onOpNoted(eq(AppOpsManager.OP_CAMERA), eq(Process.myUid()), eq(getContext().getPackageName()), eq(AppOpsManager.MODE_ALLOWED)); 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 0851cf3bc4c0..e02a30def3dc 100644 --- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java +++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java @@ -23,6 +23,7 @@ import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; @@ -132,9 +133,19 @@ public class TrampolineTest { } @Test + public void unlockUser_whenMultiUserSettingDisabled_callsBackupManagerServiceForSystemUser() { + Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0); + mTrampoline.initializeService(); + + mTrampoline.unlockUser(UserHandle.USER_SYSTEM); + + verify(mBackupManagerServiceMock).startServiceForUser(UserHandle.USER_SYSTEM); + } + + @Test public void unlockUser_whenMultiUserSettingDisabled_isIgnoredForNonSystemUser() { Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.unlockUser(10); @@ -144,7 +155,7 @@ public class TrampolineTest { @Test public void unlockUser_whenMultiUserSettingEnabled_callsBackupManagerServiceForNonSystemUser() { Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.unlockUser(10); @@ -152,27 +163,48 @@ public class TrampolineTest { } @Test - public void initializeService_forUserSystem_successfullyInitialized() { - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + public void stopUser_whenMultiUserSettingDisabled_callsBackupManagerServiceForSystemUser() { + Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0); + mTrampoline.initializeService(); - assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM)); + mTrampoline.stopUser(UserHandle.USER_SYSTEM); + + verify(mBackupManagerServiceMock).stopServiceForUser(UserHandle.USER_SYSTEM); } - // The BackupManagerService can only be initialized by USER_SYSTEM, so we check that if any - // other user trying to initialize it leaves it non-active. @Test - public void initializeService_forNonUserSystem_nonInitialized() { - mTrampoline.initializeService(NON_USER_SYSTEM); + public void stopUser_whenMultiUserSettingDisabled_isIgnoredForNonSystemUser() { + Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0); + mTrampoline.initializeService(); - assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM)); + mTrampoline.stopUser(10); + + verify(mBackupManagerServiceMock, never()).stopServiceForUser(10); + } + + @Test + public void stopUser_whenMultiUserSettingEnabled_callsBackupManagerServiceForNonSystemUser() { + Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1); + mTrampoline.initializeService(); + + mTrampoline.stopUser(10); + + verify(mBackupManagerServiceMock).stopServiceForUser(10); + } + + @Test + public void initializeService_successfullyInitializesBackupService() { + mTrampoline.initializeService(); + + assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM)); } @Test public void initializeService_globallyDisabled_nonInitialized() { TrampolineTestable.sBackupDisabled = true; - TrampolineTestable trampoline = new TrampolineTestable(mContextMock); - trampoline.initializeService(UserHandle.USER_SYSTEM); + + trampoline.initializeService(); assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM)); } @@ -181,14 +213,21 @@ public class TrampolineTest { @Test public void initializeService_suppressFileExists_nonInitialized() { when(mSuppressFileMock.exists()).thenReturn(true); - TrampolineTestable trampoline = new TrampolineTestable(mContextMock); - trampoline.initializeService(UserHandle.USER_SYSTEM); + + trampoline.initializeService(); assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM)); } @Test + public void initializeService_doesNotStartServiceForUsers() { + mTrampoline.initializeService(); + + verify(mBackupManagerServiceMock, never()).startServiceForUser(anyInt()); + } + + @Test public void isBackupServiceActive_calledBeforeInitialize_returnsFalse() { assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM)); } @@ -263,7 +302,7 @@ public class TrampolineTest { @Test public void setBackupServiceActive_makeNonActive_serviceDeletedAndSuppressFileCreated() throws IOException { - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM)); mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false); @@ -277,7 +316,7 @@ public class TrampolineTest { setBackupServiceActive_makeNonActive_serviceDeletedAndSuppressFileCreated_ioExceptionHandled() throws IOException { when(mSuppressFileMock.createNewFile()).thenThrow(new IOException()); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM)); mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false); @@ -303,7 +342,7 @@ public class TrampolineTest { @Test public void dataChangedForUser_forwarded() throws RemoteException { - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.dataChangedForUser(mUserId, PACKAGE_NAME); @@ -313,7 +352,7 @@ public class TrampolineTest { @Test public void dataChanged_forwarded() throws RemoteException { TrampolineTestable.sCallingUserId = mUserId; - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.dataChanged(PACKAGE_NAME); @@ -328,7 +367,7 @@ public class TrampolineTest { @Test public void clearBackupDataForUser_forwarded() throws RemoteException { - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.clearBackupDataForUser(mUserId, TRANSPORT_NAME, PACKAGE_NAME); @@ -338,7 +377,7 @@ public class TrampolineTest { @Test public void clearBackupData_forwarded() throws RemoteException { TrampolineTestable.sCallingUserId = mUserId; - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.clearBackupData(TRANSPORT_NAME, PACKAGE_NAME); @@ -353,7 +392,7 @@ public class TrampolineTest { @Test public void agentConnectedForUser_forwarded() throws RemoteException { - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.agentConnectedForUser(mUserId, PACKAGE_NAME, mAgentMock); @@ -363,7 +402,7 @@ public class TrampolineTest { @Test public void agentConnected_forwarded() throws RemoteException { TrampolineTestable.sCallingUserId = mUserId; - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.agentConnected(PACKAGE_NAME, mAgentMock); @@ -378,7 +417,7 @@ public class TrampolineTest { @Test public void agentDisconnectedForUser_forwarded() throws RemoteException { - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.agentDisconnectedForUser(mUserId, PACKAGE_NAME); @@ -388,7 +427,7 @@ public class TrampolineTest { @Test public void agentDisconnected_forwarded() throws RemoteException { TrampolineTestable.sCallingUserId = mUserId; - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.agentDisconnected(PACKAGE_NAME); @@ -403,7 +442,7 @@ public class TrampolineTest { @Test public void restoreAtInstallForUser_forwarded() throws RemoteException { - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.restoreAtInstallForUser(mUserId, PACKAGE_NAME, 123); @@ -413,7 +452,7 @@ public class TrampolineTest { @Test public void restoreAtInstall_forwarded() throws RemoteException { TrampolineTestable.sCallingUserId = mUserId; - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.restoreAtInstall(PACKAGE_NAME, 123); @@ -428,7 +467,7 @@ public class TrampolineTest { @Test public void setBackupEnabledForUser_forwarded() throws RemoteException { - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.setBackupEnabledForUser(mUserId, true); @@ -438,7 +477,7 @@ public class TrampolineTest { @Test public void setBackupEnabled_forwardedToCallingUserId() throws RemoteException { TrampolineTestable.sCallingUserId = mUserId; - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.setBackupEnabled(true); @@ -453,7 +492,7 @@ public class TrampolineTest { @Test public void setAutoRestoreForUser_forwarded() throws RemoteException { - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.setAutoRestoreForUser(mUserId, true); @@ -463,7 +502,7 @@ public class TrampolineTest { @Test public void setAutoRestore_forwarded() throws RemoteException { TrampolineTestable.sCallingUserId = mUserId; - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.setAutoRestore(true); @@ -478,7 +517,7 @@ public class TrampolineTest { @Test public void setBackupProvisioned_forwarded() throws RemoteException { - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.setBackupProvisioned(true); verifyNoMoreInteractions(mBackupManagerServiceMock); } @@ -491,7 +530,7 @@ public class TrampolineTest { @Test public void isBackupEnabledForUser_forwarded() throws RemoteException { - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.isBackupEnabledForUser(mUserId); @@ -501,7 +540,7 @@ public class TrampolineTest { @Test public void isBackupEnabled_forwardedToCallingUserId() throws RemoteException { TrampolineTestable.sCallingUserId = mUserId; - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.isBackupEnabled(); @@ -516,7 +555,7 @@ public class TrampolineTest { @Test public void setBackupPassword_forwarded() throws RemoteException { - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.setBackupPassword(CURRENT_PASSWORD, NEW_PASSWORD); verify(mBackupManagerServiceMock).setBackupPassword(CURRENT_PASSWORD, NEW_PASSWORD); } @@ -529,7 +568,7 @@ public class TrampolineTest { @Test public void hasBackupPassword_forwarded() throws RemoteException { - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.hasBackupPassword(); verify(mBackupManagerServiceMock).hasBackupPassword(); } @@ -542,7 +581,7 @@ public class TrampolineTest { @Test public void backupNowForUser_forwarded() throws RemoteException { - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.backupNowForUser(mUserId); @@ -552,7 +591,7 @@ public class TrampolineTest { @Test public void backupNow_forwardedToCallingUserId() throws RemoteException { TrampolineTestable.sCallingUserId = mUserId; - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.backupNow(); @@ -569,7 +608,7 @@ public class TrampolineTest { @Test public void adbBackup_forwarded() throws RemoteException { - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.adbBackup(mUserId, mParcelFileDescriptorMock, true, true, true, true, true, true, true, true, PACKAGE_NAMES); @@ -585,7 +624,7 @@ public class TrampolineTest { @Test public void fullTransportBackupForUser_forwarded() throws RemoteException { - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.fullTransportBackupForUser(mUserId, PACKAGE_NAMES); @@ -600,7 +639,7 @@ public class TrampolineTest { @Test public void adbRestore_forwarded() throws RemoteException { - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.adbRestore(mUserId, mParcelFileDescriptorMock); verify(mBackupManagerServiceMock).adbRestore(mUserId, mParcelFileDescriptorMock); } @@ -615,7 +654,7 @@ public class TrampolineTest { @Test public void acknowledgeFullBackupOrRestoreForUser_forwarded() throws RemoteException { - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.acknowledgeFullBackupOrRestoreForUser( mUserId, @@ -638,7 +677,7 @@ public class TrampolineTest { @Test public void acknowledgeFullBackupOrRestore_forwarded() throws RemoteException { TrampolineTestable.sCallingUserId = mUserId; - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.acknowledgeFullBackupOrRestore(123, true, CURRENT_PASSWORD, ENCRYPTION_PASSWORD, mFullBackupRestoreObserverMock); @@ -662,7 +701,7 @@ public class TrampolineTest { @Test public void getCurrentTransportForUser_forwarded() throws RemoteException { when(mBackupManagerServiceMock.getCurrentTransport(mUserId)).thenReturn(TRANSPORT_NAME); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); assertEquals(TRANSPORT_NAME, mTrampoline.getCurrentTransportForUser(mUserId)); verify(mBackupManagerServiceMock).getCurrentTransport(mUserId); @@ -672,7 +711,7 @@ public class TrampolineTest { public void getCurrentTransport_forwarded() throws RemoteException { TrampolineTestable.sCallingUserId = mUserId; when(mBackupManagerServiceMock.getCurrentTransport(mUserId)).thenReturn(TRANSPORT_NAME); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); assertEquals(TRANSPORT_NAME, mTrampoline.getCurrentTransport()); verify(mBackupManagerServiceMock).getCurrentTransport(mUserId); @@ -687,7 +726,7 @@ public class TrampolineTest { @Test public void listAllTransportsForUser_forwarded() throws RemoteException { when(mBackupManagerServiceMock.listAllTransports(mUserId)).thenReturn(TRANSPORTS); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); assertEquals(TRANSPORTS, mTrampoline.listAllTransportsForUser(mUserId)); verify(mBackupManagerServiceMock).listAllTransports(mUserId); @@ -698,7 +737,7 @@ public class TrampolineTest { public void listAllTransports_forwarded() throws RemoteException { TrampolineTestable.sCallingUserId = mUserId; when(mBackupManagerServiceMock.listAllTransports(mUserId)).thenReturn(TRANSPORTS); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); assertEquals(TRANSPORTS, mTrampoline.listAllTransports()); verify(mBackupManagerServiceMock).listAllTransports(mUserId); @@ -715,7 +754,7 @@ public class TrampolineTest { public void listAllTransportComponentsForUser_forwarded() throws RemoteException { when(mBackupManagerServiceMock.listAllTransportComponents(mUserId)).thenReturn( TRANSPORT_COMPONENTS); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); assertEquals(TRANSPORT_COMPONENTS, mTrampoline.listAllTransportComponentsForUser(mUserId)); verify(mBackupManagerServiceMock).listAllTransportComponents(mUserId); @@ -730,8 +769,8 @@ public class TrampolineTest { @Test public void getTransportWhitelist_forwarded() throws RemoteException { when(mBackupManagerServiceMock.getTransportWhitelist()).thenReturn(TRANSPORTS); + mTrampoline.initializeService(); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); assertEquals(TRANSPORTS, mTrampoline.getTransportWhitelist()); verify(mBackupManagerServiceMock).getTransportWhitelist(); } @@ -754,7 +793,7 @@ public class TrampolineTest { @Test public void updateTransportAttributesForUser_forwarded() throws RemoteException { when(mBackupManagerServiceMock.getTransportWhitelist()).thenReturn(TRANSPORTS); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.updateTransportAttributesForUser( mUserId, @@ -784,7 +823,7 @@ public class TrampolineTest { @Test public void selectBackupTransportForUser_forwarded() throws RemoteException { - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.selectBackupTransportForUser(mUserId, TRANSPORT_NAME); @@ -794,7 +833,7 @@ public class TrampolineTest { @Test public void selectBackupTransport_forwarded() throws RemoteException { TrampolineTestable.sCallingUserId = mUserId; - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.selectBackupTransport(TRANSPORT_NAME); @@ -870,7 +909,7 @@ public class TrampolineTest { @Test public void selectBackupTransportAsyncForUser_forwarded() throws RemoteException { - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, null); @@ -889,7 +928,7 @@ public class TrampolineTest { Intent configurationIntentStub = new Intent(); when(mBackupManagerServiceMock.getConfigurationIntent(mUserId, TRANSPORT_NAME)).thenReturn( configurationIntentStub); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); assertEquals( configurationIntentStub, @@ -903,7 +942,7 @@ public class TrampolineTest { Intent configurationIntentStub = new Intent(); when(mBackupManagerServiceMock.getConfigurationIntent(mUserId, TRANSPORT_NAME)).thenReturn( configurationIntentStub); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); assertEquals(configurationIntentStub, mTrampoline.getConfigurationIntent(TRANSPORT_NAME)); verify(mBackupManagerServiceMock).getConfigurationIntent(mUserId, TRANSPORT_NAME); @@ -919,7 +958,7 @@ public class TrampolineTest { public void getDestinationStringForUser_forwarded() throws RemoteException { when(mBackupManagerServiceMock.getDestinationString(mUserId, TRANSPORT_NAME)).thenReturn( DESTINATION_STRING); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); assertEquals( DESTINATION_STRING, @@ -933,7 +972,7 @@ public class TrampolineTest { when(mBackupManagerServiceMock.getDestinationString(mUserId, TRANSPORT_NAME)).thenReturn( DESTINATION_STRING); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); assertEquals(DESTINATION_STRING, mTrampoline.getDestinationString(TRANSPORT_NAME)); verify(mBackupManagerServiceMock).getDestinationString(mUserId, TRANSPORT_NAME); } @@ -949,7 +988,7 @@ public class TrampolineTest { Intent dataManagementIntent = new Intent(); when(mBackupManagerServiceMock.getDataManagementIntent(mUserId, TRANSPORT_NAME)).thenReturn( dataManagementIntent); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); assertEquals( dataManagementIntent, @@ -963,7 +1002,7 @@ public class TrampolineTest { Intent dataManagementIntent = new Intent(); when(mBackupManagerServiceMock.getDataManagementIntent(mUserId, TRANSPORT_NAME)).thenReturn( dataManagementIntent); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); assertEquals(dataManagementIntent, mTrampoline.getDataManagementIntent(TRANSPORT_NAME)); verify(mBackupManagerServiceMock).getDataManagementIntent(mUserId, TRANSPORT_NAME); @@ -979,7 +1018,7 @@ public class TrampolineTest { public void getDataManagementLabelForUser_forwarded() throws RemoteException { when(mBackupManagerServiceMock.getDataManagementLabel(mUserId, TRANSPORT_NAME)).thenReturn( DATA_MANAGEMENT_LABEL); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); assertEquals( DATA_MANAGEMENT_LABEL, @@ -992,7 +1031,7 @@ public class TrampolineTest { TrampolineTestable.sCallingUserId = mUserId; when(mBackupManagerServiceMock.getDataManagementLabel(mUserId, TRANSPORT_NAME)).thenReturn( DATA_MANAGEMENT_LABEL); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); assertEquals(DATA_MANAGEMENT_LABEL, mTrampoline.getDataManagementLabel(TRANSPORT_NAME)); verify(mBackupManagerServiceMock).getDataManagementLabel(mUserId, TRANSPORT_NAME); @@ -1006,7 +1045,7 @@ public class TrampolineTest { @Test public void beginRestoreSessionForUser_forwarded() throws RemoteException { - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.beginRestoreSessionForUser(mUserId, PACKAGE_NAME, TRANSPORT_NAME); @@ -1023,7 +1062,7 @@ public class TrampolineTest { @Test public void opComplete_forwarded() throws RemoteException { TrampolineTestable.sCallingUserId = mUserId; - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.opComplete(1, 2); @@ -1041,7 +1080,7 @@ public class TrampolineTest { public void getAvailableRestoreTokenForUser_forwarded() throws RemoteException { when(mBackupManagerServiceMock.getAvailableRestoreToken(mUserId, PACKAGE_NAME)) .thenReturn(123L); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); assertEquals(123, mTrampoline.getAvailableRestoreTokenForUser(mUserId, PACKAGE_NAME)); verify(mBackupManagerServiceMock).getAvailableRestoreToken(mUserId, PACKAGE_NAME); @@ -1058,7 +1097,7 @@ public class TrampolineTest { public void isAppEligibleForBackupForUser_forwarded() throws RemoteException { when(mBackupManagerServiceMock.isAppEligibleForBackup(mUserId, PACKAGE_NAME)) .thenReturn(true); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); assertTrue(mTrampoline.isAppEligibleForBackupForUser(mUserId, PACKAGE_NAME)); verify(mBackupManagerServiceMock).isAppEligibleForBackup(mUserId, PACKAGE_NAME); @@ -1075,7 +1114,7 @@ public class TrampolineTest { public void requestBackupForUser_forwarded() throws RemoteException { when(mBackupManagerServiceMock.requestBackup(mUserId, PACKAGE_NAMES, mBackupObserverMock, mBackupManagerMonitorMock, 123)).thenReturn(456); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); assertEquals(456, mTrampoline.requestBackupForUser(mUserId, PACKAGE_NAMES, mBackupObserverMock, mBackupManagerMonitorMock, 123)); @@ -1088,7 +1127,7 @@ public class TrampolineTest { TrampolineTestable.sCallingUserId = mUserId; when(mBackupManagerServiceMock.requestBackup(NON_USER_SYSTEM, PACKAGE_NAMES, mBackupObserverMock, mBackupManagerMonitorMock, 123)).thenReturn(456); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); assertEquals(456, mTrampoline.requestBackup(PACKAGE_NAMES, mBackupObserverMock, mBackupManagerMonitorMock, 123)); @@ -1104,7 +1143,7 @@ public class TrampolineTest { @Test public void cancelBackupsForUser_forwarded() throws RemoteException { - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.cancelBackupsForUser(mUserId); @@ -1114,7 +1153,7 @@ public class TrampolineTest { @Test public void cancelBackups_forwardedToCallingUserId() throws RemoteException { TrampolineTestable.sCallingUserId = mUserId; - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.cancelBackups(); @@ -1132,7 +1171,7 @@ public class TrampolineTest { FullBackupJob fullBackupJob = new FullBackupJob(); when(mBackupManagerServiceMock.beginFullBackup(fullBackupJob)).thenReturn(true); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); assertTrue(mTrampoline.beginFullBackup(fullBackupJob)); verify(mBackupManagerServiceMock).beginFullBackup(fullBackupJob); } @@ -1145,7 +1184,7 @@ public class TrampolineTest { @Test public void endFullBackup_forwarded() throws RemoteException { - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.endFullBackup(); verify(mBackupManagerServiceMock).endFullBackup(); } @@ -1155,8 +1194,7 @@ public class TrampolineTest { when(mContextMock.checkCallingOrSelfPermission( android.Manifest.permission.DUMP)).thenReturn( PackageManager.PERMISSION_DENIED); - - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.dump(mFileDescriptorStub, mPrintWriterMock, new String[0]); @@ -1179,8 +1217,7 @@ public class TrampolineTest { when(mContextMock.checkCallingOrSelfPermission( android.Manifest.permission.DUMP)).thenReturn( PackageManager.PERMISSION_GRANTED); - - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.initializeService(); mTrampoline.dump(mFileDescriptorStub, mPrintWriterMock, null); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 371e8f4db9de..83c1c7670338 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -3849,4 +3849,26 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.reportSeen(r); verify(mAppUsageStats, times(1)).reportEvent(anyString(), anyInt(), anyInt()); } + + @Test + public void testNotificationStats_notificationError() { + NotificationRecord r = generateNotificationRecord(mTestNotificationChannel); + mService.addNotification(r); + + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, "tag", mUid, 0, + new Notification.Builder(mContext, mTestNotificationChannel.getId()).build(), + new UserHandle(mUid), null, 0); + NotificationRecord update = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + mService.addEnqueuedNotification(update); + assertNull(update.sbn.getNotification().getSmallIcon()); + + NotificationManagerService.PostNotificationRunnable runnable = + mService.new PostNotificationRunnable(update.getKey()); + runnable.run(); + waitForIdle(); + + ArgumentCaptor<NotificationStats> captor = ArgumentCaptor.forClass(NotificationStats.class); + verify(mListeners).notifyRemovedLocked(any(), anyInt(), captor.capture()); + assertNotNull(captor.getValue()); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java index 92b4dbb5076b..bc62de12373d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -155,15 +155,17 @@ public class AppWindowTokenTests extends WindowTestsBase { // Set initial orientation and update. mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); - mWm.updateOrientationFromAppTokens(mDisplayContent.getRequestedOverrideConfiguration(), - null, mDisplayContent.getDisplayId()); + mDisplayContent.updateOrientationFromAppTokens( + mDisplayContent.getRequestedOverrideConfiguration(), + null /* freezeThisOneIfNeeded */, false /* forceUpdate */); assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getLastOrientation()); appWindow.mResizeReported = false; // Update the orientation to perform 180 degree rotation and check that resize was reported. mToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE); - mWm.updateOrientationFromAppTokens(mDisplayContent.getRequestedOverrideConfiguration(), - null, mDisplayContent.getDisplayId()); + mDisplayContent.updateOrientationFromAppTokens( + mDisplayContent.getRequestedOverrideConfiguration(), + null /* freezeThisOneIfNeeded */, false /* forceUpdate */); mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */); assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, mDisplayContent.getLastOrientation()); assertTrue(appWindow.mResizeReported); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 8430616731d2..3826fac22b05 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -31,7 +31,13 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.same; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; @@ -60,9 +66,11 @@ import android.view.Surface; import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; +import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.server.wm.utils.WmDisplayCutout; import org.junit.Test; +import org.mockito.ArgumentCaptor; import java.util.ArrayList; import java.util.Arrays; @@ -475,6 +483,13 @@ public class DisplayContentTests extends WindowTestsBase { @SuppressLint("InlinedApi") public void testOrientationDefinedByKeyguard() { final DisplayContent dc = createNewDisplay(); + + // When display content is created its configuration is not yet initialized, which could + // cause unnecessary configuration propagation, so initialize it here. + final Configuration config = new Configuration(); + dc.computeScreenConfiguration(config); + dc.onRequestedOverrideConfigurationChanged(config); + // Create a window that requests landscape orientation. It will define device orientation // by default. final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w"); @@ -567,6 +582,52 @@ public class DisplayContentTests extends WindowTestsBase { assertNull("default display Ime target: ", mDisplayContent.mInputMethodTarget); } + @Test + public void testOnDescendantOrientationRequestChanged() { + final DisplayContent dc = createNewDisplay(); + mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class); + final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE + ? SCREEN_ORIENTATION_PORTRAIT + : SCREEN_ORIENTATION_LANDSCAPE; + + final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w"); + window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS); + window.mAppToken.setOrientation(newOrientation); + + ActivityRecord activityRecord = mock(ActivityRecord.class); + + assertTrue("Display should rotate to handle orientation request by default.", + dc.onDescendantOrientationChanged(window.mToken.token, activityRecord)); + + final ArgumentCaptor<Configuration> captor = ArgumentCaptor.forClass(Configuration.class); + verify(mWm.mAtmService).updateDisplayOverrideConfigurationLocked(captor.capture(), + same(activityRecord), anyBoolean(), eq(dc.getDisplayId())); + final Configuration newDisplayConfig = captor.getValue(); + assertEquals(Configuration.ORIENTATION_PORTRAIT, newDisplayConfig.orientation); + } + + @Test + public void testOnDescendantOrientationRequestChanged_FrozenToUserRotation() { + final DisplayContent dc = createNewDisplay(); + dc.getDisplayRotation().setFixedToUserRotation(true); + mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class); + final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE + ? SCREEN_ORIENTATION_PORTRAIT + : SCREEN_ORIENTATION_LANDSCAPE; + + final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w"); + window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS); + window.mAppToken.setOrientation(newOrientation); + + ActivityRecord activityRecord = mock(ActivityRecord.class); + + assertFalse("Display shouldn't rotate to handle orientation request if fixed to" + + " user rotation.", + dc.onDescendantOrientationChanged(window.mToken.token, activityRecord)); + verify(mWm.mAtmService, never()).updateDisplayOverrideConfigurationLocked(any(), + eq(activityRecord), anyBoolean(), eq(dc.getDisplayId())); + } + private boolean isOptionsPanelAtRight(int displayId) { return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT; } 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 bf4b52eb72aa..6b31e6fdbd28 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java @@ -33,6 +33,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.content.ContentResolver; @@ -234,7 +235,7 @@ public class DisplayRotationTests { } @Test - public void testReturnsSidesays_UserRotationLocked_IncompatibleAppRequest() + public void testReturnsSideways_UserRotationLocked_IncompatibleAppRequest() throws Exception { mBuilder.build(); configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); @@ -604,6 +605,26 @@ public class DisplayRotationTests { SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0)); } + // ======================== + // Non-rotation API Tests + // ======================== + @Test + public void testRespectsAppRequestedOrientationByDefault() throws Exception { + mBuilder.build(); + + assertTrue("Display rotation should respect app requested orientation by" + + " default.", mTarget.respectAppRequestedOrientation()); + } + + @Test + public void testNotRespectAppRequestedOrientation_FixedToUserRotation() throws Exception { + mBuilder.build(); + mTarget.setFixedToUserRotation(true); + + assertFalse("Display rotation shouldn't respect app requested orientation if" + + " fixed to user rotation.", mTarget.respectAppRequestedOrientation()); + } + /** * Call {@link DisplayRotation#configure(int, int, int, int)} to configure {@link #mTarget} * according to given parameters. diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java index 83e7ee711831..dfdbf323365f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java @@ -49,12 +49,9 @@ import androidx.test.filters.SmallTest; import com.android.server.wm.LocalAnimationAdapter.AnimationSpec; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; import java.util.concurrent.CountDownLatch; @@ -72,7 +69,6 @@ public class SurfaceAnimationRunnerTest extends WindowTestsBase { @Mock Transaction mMockTransaction; @Mock AnimationSpec mMockAnimationSpec; @Mock PowerManagerInternal mMockPowerManager; - @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); private SurfaceAnimationRunner mSurfaceAnimationRunner; private CountDownLatch mFinishCallbackLatch; diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index 60f957f8f569..e1561436c022 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -27,6 +27,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat; import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; @@ -38,8 +39,10 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; +import android.os.IBinder; import android.platform.test.annotations.Presubmit; import android.view.SurfaceControl; import android.view.SurfaceSession; @@ -559,8 +562,7 @@ public class WindowContainerTests extends WindowTestsBase { builder.setLayer(2).setIsVisible(true); final TestWindowContainer visibleUnspecifiedRootChildChildFillsParent = visibleUnspecifiedRootChild.addChildWindow(builder); - visibleUnspecifiedRootChildChildFillsParent.setOrientation( - SCREEN_ORIENTATION_PORTRAIT); + visibleUnspecifiedRootChildChildFillsParent.setOrientation(SCREEN_ORIENTATION_PORTRAIT); assertEquals(SCREEN_ORIENTATION_PORTRAIT, visibleUnspecifiedRootChildChildFillsParent.getOrientation()); assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnspecifiedRootChild.getOrientation()); @@ -724,6 +726,19 @@ public class WindowContainerTests extends WindowTestsBase { verify(grandChild, times(1)).onParentResize(); } + @Test + public void testOnDescendantOrientationRequestChangedPropagation() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = spy(builder.build()); + + final IBinder binder = mock(IBinder.class); + final ActivityRecord activityRecord = mock(ActivityRecord.class); + final TestWindowContainer child = root.addChildWindow(); + + child.setOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED, binder, activityRecord); + verify(root).onDescendantOrientationChanged(binder, activityRecord); + } + /* Used so we can gain access to some protected members of the {@link WindowContainer} class */ private static class TestWindowContainer extends WindowContainer<TestWindowContainer> { private final int mLayer; diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java index d6b40aecf52e..38efc743fe34 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java @@ -622,6 +622,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { onRecognitionFailureLocked(); break; case SoundTrigger.RECOGNITION_STATUS_SUCCESS: + case SoundTrigger.RECOGNITION_STATUS_GET_STATE_RESPONSE: if (isKeyphraseRecognitionEvent(event)) { onKeyphraseRecognitionSuccessLocked((KeyphraseRecognitionEvent) event); } else { @@ -638,7 +639,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { private void onGenericRecognitionSuccessLocked(GenericRecognitionEvent event) { MetricsLogger.count(mContext, "sth_generic_recognition_event", 1); - if (event.status != SoundTrigger.RECOGNITION_STATUS_SUCCESS) { + if (event.status != SoundTrigger.RECOGNITION_STATUS_SUCCESS + && event.status != SoundTrigger.RECOGNITION_STATUS_GET_STATE_RESPONSE) { return; } ModelData model = getModelDataForLocked(event.soundModelHandle); @@ -655,7 +657,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return; } - model.setStopped(); + if (event.status != SoundTrigger.RECOGNITION_STATUS_GET_STATE_RESPONSE) { + model.setStopped(); + } try { callback.onGenericSoundTriggerDetected((GenericRecognitionEvent) event); } catch (DeadObjectException e) { @@ -797,7 +801,10 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { Slog.w(TAG, "Received onRecognition event without callback for keyphrase model."); return; } - modelData.setStopped(); + + if (event.status != SoundTrigger.RECOGNITION_STATUS_GET_STATE_RESPONSE) { + modelData.setStopped(); + } try { modelData.getCallback().onKeyphraseDetected((KeyphraseRecognitionEvent) event); diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h index 06059c8c6e56..757d863461f0 100644 --- a/startop/view_compiler/dex_builder.h +++ b/startop/view_compiler/dex_builder.h @@ -73,7 +73,7 @@ class TypeDescriptor { bool operator<(const TypeDescriptor& rhs) const { return descriptor_ < rhs.descriptor_; } private: - TypeDescriptor(std::string descriptor) : descriptor_{descriptor} {} + explicit TypeDescriptor(std::string descriptor) : descriptor_{descriptor} {} const std::string descriptor_; }; @@ -83,7 +83,7 @@ class TypeDescriptor { class Prototype { public: template <typename... TypeDescriptors> - Prototype(TypeDescriptor return_type, TypeDescriptors... param_types) + explicit Prototype(TypeDescriptor return_type, TypeDescriptors... param_types) : return_type_{return_type}, param_types_{param_types...} {} // Encode this prototype into the dex file. diff --git a/startop/view_compiler/main.cc b/startop/view_compiler/main.cc index 9351dc34ea54..55bfdc78ec1b 100644 --- a/startop/view_compiler/main.cc +++ b/startop/view_compiler/main.cc @@ -42,7 +42,7 @@ DEFINE_string(package, "", "The package name for the generated class (required)" class ViewCompilerXmlVisitor : public XMLVisitor { public: - ViewCompilerXmlVisitor(JavaLangViewBuilder* builder) : builder_(builder) {} + explicit ViewCompilerXmlVisitor(JavaLangViewBuilder* builder) : builder_(builder) {} bool VisitEnter(const XMLDocument& /*doc*/) override { builder_->Start(); diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java index 1cbe5a26caed..c115a4bd2a10 100644 --- a/telephony/java/android/provider/Telephony.java +++ b/telephony/java/android/provider/Telephony.java @@ -2732,10 +2732,26 @@ public final class Telephony { /** * The {@code content://} style URL for this table. + * For MSIM, this will return APNs for the default subscription + * {@link SubscriptionManager#getDefaultSubscriptionId()}. To specify subId for MSIM, + * use {@link Uri#withAppendedPath(Uri, String)} to append with subscription id. */ public static final Uri CONTENT_URI = Uri.parse("content://telephony/carriers"); /** + * The {@code content://} style URL for this table. Used for APN query based on current + * subscription. Instead of specifying carrier matching information in the selection, + * this API will return all matching APNs from current subscription carrier and queries + * will be applied on top of that. If there is no match for MVNO (Mobile Virtual Network + * Operator) APNs, return APNs from its MNO (based on mccmnc) instead. For MSIM, this will + * return APNs for the default subscription + * {@link SubscriptionManager#getDefaultSubscriptionId()}. To specify subId for MSIM, + * use {@link Uri#withAppendedPath(Uri, String)} to append with subscription id. + */ + public static final Uri SIM_APN_URI = Uri.parse( + "content://telephony/carriers/sim_apn_list"); + + /** * The {@code content://} style URL to be called from DevicePolicyManagerService, * can manage DPC-owned APNs. * @hide @@ -2745,7 +2761,9 @@ public final class Telephony { /** * The {@code content://} style URL to be called from Telephony to query APNs. * When DPC-owned APNs are enforced, only DPC-owned APNs are returned, otherwise only - * non-DPC-owned APNs are returned. + * non-DPC-owned APNs are returned. For MSIM, this will return APNs for the default + * subscription {@link SubscriptionManager#getDefaultSubscriptionId()}. To specify subId + * for MSIM, use {@link Uri#withAppendedPath(Uri, String)} to append with subscription id. * @hide */ public static final Uri FILTERED_URI = Uri.parse("content://telephony/carriers/filtered"); @@ -2759,13 +2777,6 @@ public final class Telephony { "content://telephony/carriers/enforce_managed"); /** - * The {@code content://} style URL to be called from Telephony to query current APNs. - * @hide - */ - public static final Uri SIM_APN_LIST = Uri.parse( - "content://telephony/carriers/sim_apn_list"); - - /** * The column name for ENFORCE_MANAGED_URI, indicates whether DPC-owned APNs are enforced. * @hide */ @@ -2839,18 +2850,30 @@ public final class Telephony { /** * Mobile Country Code (MCC). * <P>Type: TEXT</P> + * @deprecated Use {@link #SIM_APN_URI} to query APN instead, this API will return + * matching APNs based on current subscription carrier, thus no need to specify MCC and + * other carrier matching information. In the future, Android will not support MCC for + * APN query. */ public static final String MCC = "mcc"; /** * Mobile Network Code (MNC). * <P>Type: TEXT</P> + * @deprecated Use {@link #SIM_APN_URI} to query APN instead, this API will return + * matching APNs based on current subscription carrier, thus no need to specify MNC and + * other carrier matching information. In the future, Android will not support MNC for + * APN query. */ public static final String MNC = "mnc"; /** * Numeric operator ID (as String). Usually {@code MCC + MNC}. * <P>Type: TEXT</P> + * @deprecated Use {@link #SIM_APN_URI} to query APN instead, this API will return + * matching APNs based on current subscription carrier, thus no need to specify Numeric + * and other carrier matching information. In the future, Android will not support Numeric + * for APN query. */ public static final String NUMERIC = "numeric"; @@ -2931,6 +2954,10 @@ public final class Telephony { * MVNO type: * {@code SPN (Service Provider Name), IMSI, GID (Group Identifier Level 1)}. * <P>Type: TEXT</P> + * @deprecated Use {@link #SIM_APN_URI} to query APN instead, this API will return + * matching APNs based on current subscription carrier, thus no need to specify MVNO_TYPE + * and other carrier matching information. In the future, Android will not support MVNO_TYPE + * for APN query. */ public static final String MVNO_TYPE = "mvno_type"; @@ -2943,6 +2970,10 @@ public final class Telephony { * <li>GID: 4E, 33, ...</li> * </ul> * <P>Type: TEXT</P> + * @deprecated Use {@link #SIM_APN_URI} to query APN instead, this API will return + * matching APNs based on current subscription carrier, thus no need to specify + * MVNO_MATCH_DATA and other carrier matching information. In the future, Android will not + * support MVNO_MATCH_DATA for APN query. */ public static final String MVNO_MATCH_DATA = "mvno_match_data"; @@ -3151,7 +3182,6 @@ public final class Telephony { }) @Retention(RetentionPolicy.SOURCE) public @interface EditStatus {} - } /** diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java index ebf198702bb9..6326cc688914 100644 --- a/telephony/java/android/telephony/euicc/EuiccManager.java +++ b/telephony/java/android/telephony/euicc/EuiccManager.java @@ -61,7 +61,6 @@ public class EuiccManager { public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS = "android.telephony.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS"; - /** * Broadcast Action: The eUICC OTA status is changed. * <p class="note"> @@ -87,6 +86,20 @@ public class EuiccManager { "android.telephony.euicc.action.NOTIFY_CARRIER_SETUP_INCOMPLETE"; /** + * Intent action to select a profile to enable before download a new eSIM profile. + * + * May be called during device provisioning when there are multiple slots having profiles on + * them. This Intent launches a screen for all the current existing profiles and let users to + * choose which one they want to enable. In this case, the slot contains the profile will be + * activated. + * + * @hide + */ + @SystemApi + public static final String ACTION_PROFILE_SELECTION = + "android.telephony.euicc.action.PROFILE_SELECTION"; + + /** * Intent action to provision an embedded subscription. * * <p>May be called during device provisioning to launch a screen to perform embedded SIM @@ -132,6 +145,16 @@ public class EuiccManager { public static final int EMBEDDED_SUBSCRIPTION_RESULT_ERROR = 2; /** + * Key for an extra set on the {@link #ACTION_PROVISION_EMBEDDED_SUBSCRIPTION} intent for which + * kind of activation flow will be evolved. (see {@code EUICC_ACTIVATION_}) + * + * @hide + */ + @SystemApi + public static final String EXTRA_ACTIVATION_TYPE = + "android.telephony.euicc.extra.ACTIVATION_TYPE"; + + /** * Key for an extra set on {@link PendingIntent} result callbacks providing a detailed result * code. * @@ -197,6 +220,52 @@ public class EuiccManager { public static final String META_DATA_CARRIER_ICON = "android.telephony.euicc.carriericon"; /** + * Euicc activation type which will be included in {@link #EXTRA_ACTIVATION_TYPE} and used to + * decide which kind of activation flow should be lauched. + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"EUICC_ACTIVATION_"}, value = { + EUICC_ACTIVATION_TYPE_DEFAULT, + EUICC_ACTIVATION_TYPE_BACKUP, + EUICC_ACTIVATION_TYPE_TRANSFER + + }) + public @interface EuiccActivationType{} + + + /** + * The default euicc activation type which includes checking server side and downloading the + * profile based on carrier's download configuration. + * + * @hide + */ + @SystemApi + public static final int EUICC_ACTIVATION_TYPE_DEFAULT = 1; + + /** + * The euicc activation type used when the default download process failed. LPA will start the + * backup flow and try to download the profile for the carrier. + * + * @hide + */ + @SystemApi + public static final int EUICC_ACTIVATION_TYPE_BACKUP = 2; + + /** + * The activation flow of eSIM seamless transfer will be used. LPA will start normal eSIM + * activation flow and if it's failed, the name of the carrier selected will be recorded. After + * the future device pairing, LPA will contact this carrier to transfer it from the other device + * to this device. + * + * @hide + */ + @SystemApi + public static final int EUICC_ACTIVATION_TYPE_TRANSFER = 3; + + + /** * Euicc OTA update status which can be got by {@link #getOtaStatus} * @hide */ @@ -336,7 +405,7 @@ public class EuiccManager { } try { getIEuiccController().downloadSubscription(subscription, switchAfterDownload, - mContext.getOpPackageName(), callbackIntent); + mContext.getOpPackageName(), null /* resolvedBundle */, callbackIntent); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl index 0a0ad90b5954..870a689f85b1 100644 --- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl +++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl @@ -32,7 +32,7 @@ interface IEuiccController { String getEid(); int getOtaStatus(); oneway void downloadSubscription(in DownloadableSubscription subscription, - boolean switchAfterDownload, String callingPackage, in PendingIntent callbackIntent); + boolean switchAfterDownload, String callingPackage, in Bundle resolvedBundle, in PendingIntent callbackIntent); EuiccInfo getEuiccInfo(); oneway void deleteSubscription(int subscriptionId, String callingPackage, in PendingIntent callbackIntent); diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java index 369a002fa273..4c2a984f8198 100644 --- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java +++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java @@ -16,16 +16,16 @@ package com.android.framework.permission.tests; -import android.content.res.Configuration; +import static android.view.Display.DEFAULT_DISPLAY; + import android.os.Binder; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.test.suitebuilder.annotation.SmallTest; import android.view.IWindowManager; -import junit.framework.TestCase; -import static android.view.Display.DEFAULT_DISPLAY; +import junit.framework.TestCase; /** * TODO: Remove this. This is only a placeholder, need to implement this. @@ -73,17 +73,6 @@ public class WindowManagerPermissionTests extends TestCase { } try { - mWm.updateOrientationFromAppTokens(new Configuration(), - null /* freezeThisOneIfNeeded */, DEFAULT_DISPLAY); - fail("IWindowManager.updateOrientationFromAppTokens did not throw SecurityException as" - + " expected"); - } catch (SecurityException e) { - // expected - } catch (RemoteException e) { - fail("Unexpected remote exception"); - } - - try { mWm.prepareAppTransition(0, false); fail("IWindowManager.prepareAppTransition did not throw SecurityException as" + " expected"); diff --git a/tools/incident_report/printer.h b/tools/incident_report/printer.h index ed93fa19542c..63e276b367ca 100644 --- a/tools/incident_report/printer.h +++ b/tools/incident_report/printer.h @@ -22,7 +22,7 @@ class Out { public: - Out(int fd); + explicit Out(int fd); ~Out(); void printf(const char* format, ...); diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp index 703a67b791be..5725f0cdae6e 100644 --- a/tools/stats_log_api_gen/Android.bp +++ b/tools/stats_log_api_gen/Android.bp @@ -96,6 +96,7 @@ genrule { cc_library_shared { name: "libstatslog", + host_supported: true, generated_sources: ["statslog.cpp"], generated_headers: ["statslog.h"], cflags: [ @@ -105,8 +106,19 @@ cc_library_shared { export_generated_headers: ["statslog.h"], shared_libs: [ "liblog", - "libutils", "libcutils", ], static_libs: ["libstatssocket"], + target: { + android: { + shared_libs: [ + "libutils", + ], + }, + host: { + static_libs: [ + "libutils", + ], + }, + }, } diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp index 5192a0e7bf19..11b408fb8b9e 100644 --- a/tools/stats_log_api_gen/main.cpp +++ b/tools/stats_log_api_gen/main.cpp @@ -106,7 +106,9 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, fprintf(out, "#include <mutex>\n"); fprintf(out, "#include <chrono>\n"); fprintf(out, "#include <thread>\n"); + fprintf(out, "#ifdef __ANDROID__\n"); fprintf(out, "#include <cutils/properties.h>\n"); + fprintf(out, "#endif\n"); fprintf(out, "#include <stats_event_list.h>\n"); fprintf(out, "#include <log/log.h>\n"); fprintf(out, "#include <statslog.h>\n"); @@ -117,7 +119,11 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, fprintf(out, "namespace util {\n"); fprintf(out, "// the single event tag id for all stats logs\n"); fprintf(out, "const static int kStatsEventTag = 1937006964;\n"); + fprintf(out, "#ifdef __ANDROID__\n"); fprintf(out, "const static bool kStatsdEnabled = property_get_bool(\"ro.statsd.enable\", true);\n"); + fprintf(out, "#else\n"); + fprintf(out, "const static bool kStatsdEnabled = false;\n"); + fprintf(out, "#endif\n"); std::set<string> kTruncatingAtomNames = {"mobile_radio_power_state_changed", "audio_state_changed", diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 64f8adb36a93..d023b58437af 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -708,6 +708,12 @@ public class WifiConfiguration implements Parcelable { } /** + * This Wifi configuration is expected for OSU(Online Sign Up) of Passpoint Release 2. + * @hide + */ + public boolean osu; + + /** * @hide * Last time the system was connected to this configuration. */ @@ -1645,6 +1651,7 @@ public class WifiConfiguration implements Parcelable { selfAdded = false; didSelfAdd = false; ephemeral = false; + osu = false; trusted = true; // Networks are considered trusted by default. meteredHint = false; meteredOverride = METERED_OVERRIDE_NONE; @@ -1755,6 +1762,7 @@ public class WifiConfiguration implements Parcelable { if (this.selfAdded) sbuf.append(" selfAdded"); if (this.validatedInternetAccess) sbuf.append(" validatedInternetAccess"); if (this.ephemeral) sbuf.append(" ephemeral"); + if (this.osu) sbuf.append(" osu"); if (this.trusted) sbuf.append(" trusted"); if (this.meteredHint) sbuf.append(" meteredHint"); if (this.useExternalScores) sbuf.append(" useExternalScores"); @@ -2244,6 +2252,7 @@ public class WifiConfiguration implements Parcelable { validatedInternetAccess = source.validatedInternetAccess; isLegacyPasspointConfig = source.isLegacyPasspointConfig; ephemeral = source.ephemeral; + osu = source.osu; trusted = source.trusted; meteredHint = source.meteredHint; meteredOverride = source.meteredOverride; @@ -2340,6 +2349,7 @@ public class WifiConfiguration implements Parcelable { dest.writeInt(recentFailure.getAssociationStatus()); dest.writeParcelable(mRandomizedMacAddress, flags); dest.writeInt(macRandomizationSetting); + dest.writeInt(osu ? 1 : 0); } /** Implement the Parcelable interface {@hide} */ @@ -2410,6 +2420,7 @@ public class WifiConfiguration implements Parcelable { config.recentFailure.setAssociationStatus(in.readInt()); config.mRandomizedMacAddress = in.readParcelable(null); config.macRandomizationSetting = in.readInt(); + config.osu = in.readInt() != 0; return config; } |