Merge "Always show back button when screen pinning, for prototype"
diff --git a/api/current.txt b/api/current.txt
index cfe8708..bf3a1f4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11302,6 +11302,7 @@
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";
@@ -11366,13 +11367,20 @@
method public android.net.Uri getReferrerUri();
method public int getSessionId();
method public long getSize();
+ method public int getStagedSessionErrorCode();
method public boolean isActive();
method public boolean isMultiPackage();
method public boolean isSealed();
+ method public boolean isSessionApplied();
+ method public boolean isSessionFailed();
+ method public boolean isSessionReady();
method public boolean isStaged();
method public void writeToParcel(android.os.Parcel, int);
+ field public static final int ACTIVATION_FAILED = 2; // 0x2
field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionInfo> CREATOR;
field public static final int INVALID_ID = -1; // 0xffffffff
+ field public static final int NO_ERROR = 0; // 0x0
+ field public static final int VERIFICATION_FAILED = 1; // 0x1
}
public static class PackageInstaller.SessionParams implements android.os.Parcelable {
@@ -14127,8 +14135,11 @@
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;
@@ -23372,12 +23383,13 @@
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();
@@ -23402,6 +23414,7 @@
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);
@@ -23413,6 +23426,7 @@
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
@@ -23455,14 +23469,24 @@
public final class AudioRecordingConfiguration implements android.os.Parcelable {
method public int describeContents();
method public android.media.AudioDeviceInfo getAudioDevice();
+ method public int getAudioSource();
method public int getClientAudioSessionId();
method public int getClientAudioSource();
+ method public java.util.List<android.media.audiofx.AudioEffect.Descriptor> getClientEffects();
method public android.media.AudioFormat getClientFormat();
+ method public java.util.List<android.media.audiofx.AudioEffect.Descriptor> getEffects();
method public android.media.AudioFormat getFormat();
+ method public boolean isClientSilenced();
method public void writeToParcel(android.os.Parcel, int);
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();
@@ -25464,11 +25488,12 @@
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();
@@ -25477,6 +25502,7 @@
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();
@@ -25512,6 +25538,7 @@
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
@@ -28217,7 +28244,7 @@
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();
@@ -28228,7 +28255,7 @@
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();
@@ -28262,14 +28289,14 @@
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
@@ -28421,6 +28448,7 @@
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();
@@ -28569,10 +28597,10 @@
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();
@@ -28588,7 +28616,7 @@
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;
@@ -28606,7 +28634,7 @@
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;
@@ -35140,6 +35168,7 @@
public final class StorageVolume implements android.os.Parcelable {
method public deprecated android.content.Intent createAccessIntent(java.lang.String);
+ method public android.content.Intent createOpenDocumentTreeIntent();
method public int describeContents();
method public java.lang.String getDescription(android.content.Context);
method public java.lang.String getState();
@@ -38670,22 +38699,23 @@
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";
@@ -52216,20 +52246,20 @@
}
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);
method public void setContentCaptureEnabled(boolean);
}
- public final class ContentCaptureSession implements java.lang.AutoCloseable {
+ public abstract class ContentCaptureSession implements java.lang.AutoCloseable {
method public void close();
- method public void destroy();
- method public android.view.contentcapture.ContentCaptureSessionId getContentCaptureSessionId();
- method public void notifyViewAppeared(android.view.ViewStructure);
- method public void notifyViewDisappeared(android.view.autofill.AutofillId);
- method public void notifyViewTextChanged(android.view.autofill.AutofillId, java.lang.CharSequence, int);
+ 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);
+ method public final void notifyViewDisappeared(android.view.autofill.AutofillId);
+ method public final void notifyViewTextChanged(android.view.autofill.AutofillId, java.lang.CharSequence, int);
field public static final int FLAG_USER_INPUT = 1; // 0x1
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 395a5fc..d981399 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -2577,6 +2577,81 @@
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 @@
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 @@
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 @@
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 @@
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;
@@ -5050,7 +5144,7 @@
package android.service.contentcapture {
- public final class ContentCaptureEventsRequest implements android.os.Parcelable {
+ public final deprecated class ContentCaptureEventsRequest implements android.os.Parcelable {
method public int describeContents();
method public java.util.List<android.view.contentcapture.ContentCaptureEvent> getEvents();
method public void writeToParcel(android.os.Parcel, int);
@@ -5062,7 +5156,8 @@
method public final java.util.Set<android.content.ComponentName> getContentCaptureDisabledActivities();
method public final java.util.Set<java.lang.String> getContentCaptureDisabledPackages();
method public void onActivitySnapshot(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.SnapshotData);
- method public void onContentCaptureEventsRequest(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.ContentCaptureEventsRequest);
+ method public void onContentCaptureEvent(android.view.contentcapture.ContentCaptureSessionId, android.view.contentcapture.ContentCaptureEvent);
+ method public deprecated void onContentCaptureEventsRequest(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.ContentCaptureEventsRequest);
method public void onCreateContentCaptureSession(android.view.contentcapture.ContentCaptureContext, android.view.contentcapture.ContentCaptureSessionId);
method public void onDestroyContentCaptureSession(android.view.contentcapture.ContentCaptureSessionId);
method public final void setActivityContentCaptureEnabled(android.content.ComponentName, boolean);
@@ -5084,6 +5179,16 @@
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();
@@ -5137,7 +5242,8 @@
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);
@@ -5152,19 +5258,25 @@
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 {
@@ -6150,6 +6262,7 @@
method public boolean getEmergencyCallbackMode();
method public java.lang.String getIsimDomain();
method public int getPreferredNetworkType(int);
+ method public int getPreferredNetworkTypeBitmap();
method public int getRadioPowerState();
method public int getSimApplicationState();
method public int getSimCardState();
@@ -6178,6 +6291,7 @@
method public void setDataActivationState(int);
method public deprecated void setDataEnabled(int, boolean);
method public void setDataRoamingEnabled(boolean);
+ method public boolean setPreferredNetworkTypeBitmap(int);
method public boolean setRadio(boolean);
method public boolean setRadioPower(boolean);
method public void setSimPowerState(int);
@@ -6461,12 +6575,17 @@
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";
}
@@ -7550,6 +7669,7 @@
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/api/test-current.txt b/api/test-current.txt
index 1e15792..71a06f1 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -638,6 +638,11 @@
method public static boolean isEncodingLinearPcm(int);
}
+ public final class AudioRecordingConfiguration implements android.os.Parcelable {
+ ctor public AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, java.lang.String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]);
+ ctor public AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, java.lang.String);
+ }
+
public final class BufferingParams implements android.os.Parcelable {
method public int describeContents();
method public int getInitialMarkMs();
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index d7922bc..fe7099b 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_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_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 {
name: "idmap2d",
+ defaults: [
+ "idmap2_defaults",
+ ],
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=*",
- ],
srcs: [
- ":idmap2_aidl",
"idmap2d/Idmap2Service.cpp",
"idmap2d/Main.cpp",
],
@@ -181,9 +184,30 @@
"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 291eaeb..b075673 100644
--- a/cmds/idmap2/idmap2/Create.cpp
+++ b/cmds/idmap2/idmap2/Create.cpp
@@ -32,9 +32,12 @@
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 @@
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 8d0cee5..cfb5dd5 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -63,10 +63,12 @@
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 @@
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 5d9ea77..4012555 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 @@
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 00c49e3..ef560d1 100644
--- a/cmds/idmap2/idmap2/Scan.cpp
+++ b/cmds/idmap2/idmap2/Scan.cpp
@@ -44,7 +44,7 @@
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 @@
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 @@
}
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 b5fa438..4d4a0e7 100644
--- a/cmds/idmap2/idmap2/Verify.cpp
+++ b/cmds/idmap2/idmap2/Verify.cpp
@@ -27,6 +27,7 @@
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 86b00f1..7b16093 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -39,10 +39,11 @@
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 @@
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 @@
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 d64a87b..4393dcc 100644
--- a/cmds/idmap2/idmap2d/Main.cpp
+++ b/cmds/idmap2/idmap2d/Main.cpp
@@ -37,7 +37,7 @@
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 05c6d31..84cc69a 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 29969a2..b7765bc 100644
--- a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
+++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
@@ -64,15 +64,15 @@
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 4ac4c04..88d40d1 100644
--- a/cmds/idmap2/libidmap2/FileUtils.cpp
+++ b/cmds/idmap2/libidmap2/FileUtils.cpp
@@ -34,12 +34,12 @@
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(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 1ef3267..5822745 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 @@
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 @@
}
// 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 @@
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 @@
// 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 @@
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::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 @@
}
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 @@
// 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 fb3bc5b..b36df24 100644
--- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
@@ -49,17 +49,18 @@
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 7c24445..a6bf5fb6 100644
--- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -59,22 +59,23 @@
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 8b552dc..3b9dbe9 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 @@
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 b04b256..243d23a 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, 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 @@
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, 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 @@
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 @@
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 0c6439a..6584ee3 100644
--- a/cmds/idmap2/tests/FileUtilsTests.cpp
+++ b/cmds/idmap2/tests/FileUtilsTests.cpp
@@ -39,7 +39,7 @@
[](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, 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 5c4e857..255f3c1 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -51,15 +51,17 @@
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 @@
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 0379aa4..dc80e0e 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -50,10 +50,10 @@
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 @@
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 @@
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 @@
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.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[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]->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 @@
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.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[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]->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 @@
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 @@
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.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[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]->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 f2469ea..0f683ff 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 da97792..0c4f493 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 c28ce2e..6285f21 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 7f60d75..c8578d3 100644
--- a/cmds/idmap2/tests/ResourceUtilsTests.cpp
+++ b/cmds/idmap2/tests/ResourceUtilsTests.cpp
@@ -52,13 +52,13 @@
};
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 18dc541..356db7a 100644
--- a/cmds/idmap2/tests/TestHelpers.h
+++ b/cmds/idmap2/tests/TestHelpers.h
@@ -117,7 +117,7 @@
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 97ff03e..40758b42 100644
--- a/cmds/idmap2/tests/XmlTests.cpp
+++ b/cmds/idmap2/tests/XmlTests.cpp
@@ -58,11 +58,11 @@
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 784c181..a6074e7 100644
--- a/cmds/incident_helper/src/TextParserBase.h
+++ b/cmds/incident_helper/src/TextParserBase.h
@@ -30,7 +30,7 @@
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 c02a349..09dc8e6 100644
--- a/cmds/incident_helper/src/ih_util.h
+++ b/cmds/incident_helper/src/ih_util.h
@@ -109,7 +109,7 @@
class Reader
{
public:
- Reader(const int fd);
+ explicit Reader(const int fd);
~Reader();
bool readLine(std::string* line);
@@ -162,7 +162,7 @@
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 6252ad2..c63a183 100644
--- a/cmds/incidentd/src/IncidentService.h
+++ b/cmds/incidentd/src/IncidentService.h
@@ -97,7 +97,7 @@
// ================================================================================
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 a3df490..a0159d9 100644
--- a/cmds/incidentd/src/Privacy.h
+++ b/cmds/incidentd/src/Privacy.h
@@ -83,7 +83,7 @@
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 45fd944..2a3abd7 100644
--- a/cmds/incidentd/src/Reporter.h
+++ b/cmds/incidentd/src/Reporter.h
@@ -89,7 +89,7 @@
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 10d2268..32ec1ba 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -415,7 +415,7 @@
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 3badb1f..bca858e 100644
--- a/cmds/statsd/src/anomaly/AlarmMonitor.h
+++ b/cmds/statsd/src/anomaly/AlarmMonitor.h
@@ -42,7 +42,7 @@
* 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/atoms.proto b/cmds/statsd/src/atoms.proto
index bdcdc536..fd62843 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -172,6 +172,7 @@
WifiEnabledStateChanged wifi_enabled_state_changed = 113;
WifiRunningStateChanged wifi_running_state_changed = 114;
AppCompacted app_compacted = 115;
+ NetworkDnsEventReported network_dns_event_Reported = 116;
}
// Pulled events will start at field 10000.
@@ -3712,3 +3713,51 @@
// The process state at the time of compaction.
optional android.app.ProcessStateEnum process_state = 16 [default = PROCESS_STATE_UNKNOWN];
}
+
+/**
+ * Logs the latency period(in microseconds) and the return code of
+ * the DNS(Domain Name System) lookups.
+ * These 4 methods(GETADDRINFO,GETHOSTBYNAME,GETHOSTBYADDR,RES_NSEND)
+ * to get info(address or hostname) from DNS server(or DNS cache).
+ * Logged from:
+ * /system/netd/server/DnsProxyListener.cpp
+ */
+message NetworkDnsEventReported {
+ // The types of the DNS lookups, as defined in
+ //system/netd/server/binder/android/net/metrics/INetdEventListener.aidl
+ enum EventType {
+ EVENT_UNKNOWN = 0;
+ EVENT_GETADDRINFO = 1;
+ EVENT_GETHOSTBYNAME = 2;
+ EVENT_GETHOSTBYADDR = 3;
+ EVENT_RES_NSEND = 4;
+ }
+ optional EventType event_type = 1;
+
+ // The return value of the DNS resolver for each DNS lookups.
+ //bionic/libc/include/netdb.h
+ //system/netd/resolv/include/netd_resolv/resolv.h
+ enum ReturnCode {
+ EAI_NOERR = 0;
+ EAI_ADDRFAMILY = 1;
+ EAI_AGAIN = 2;
+ EAI_BADFLAGS = 3;
+ EAI_FAIL = 4;
+ EAI_FAMILY = 5;
+ EAI_MEMORY = 6;
+ EAI_NODATA = 7;
+ EAI_NONAME = 8;
+ EAI_SERVICE = 9;
+ EAI_SOCKTYPE = 10;
+ EAI_SYSTEM = 11;
+ EAI_BADHINTS = 12;
+ EAI_PROTOCOL = 13;
+ EAI_OVERFLOW = 14;
+ RESOLV_TIMEOUT = 255;
+ EAI_MAX = 256;
+ }
+ optional ReturnCode return_code = 2;
+
+ // The latency period(in microseconds) it took for this DNS lookup to complete.
+ optional int32 latency_micros = 3;
+}
diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h
index a6f88af..2c88147 100644
--- a/cmds/statsd/src/condition/ConditionWizard.h
+++ b/cmds/statsd/src/condition/ConditionWizard.h
@@ -29,7 +29,7 @@
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 dc79519..4cc9393 100644
--- a/cmds/statsd/src/config/ConfigKey.h
+++ b/cmds/statsd/src/config/ConfigKey.h
@@ -33,7 +33,7 @@
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 9b238eaf5..ba6e6c3 100644
--- a/cmds/statsd/src/external/ResourceHealthManagerPuller.h
+++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.h
@@ -28,7 +28,7 @@
*/
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 0a49732..a16baf0 100644
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.h
+++ b/cmds/statsd/src/external/StatsCompanionServicePuller.h
@@ -25,7 +25,7 @@
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 cafd797..f8ecb87 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -33,7 +33,7 @@
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 5408d17..4e37e9b 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -212,7 +212,7 @@
* 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 73e4d33..b8185d2 100644
--- a/cmds/statsd/src/socket/StatsSocketListener.h
+++ b/cmds/statsd/src/socket/StatsSocketListener.h
@@ -35,7 +35,7 @@
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 @@
};
} // 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 e3fe928..0775afe 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,207 +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;
+ 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());
- public static final long CONFIG_ID = 54321;
-
- private static boolean mIsPushedAtom = false;
-
- 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>");
+ if (args.length < 1) {
+ LOGGER.log(Level.SEVERE, "Usage: ./test_drive <atomId1> <atomId2> ... <atomIdN>");
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]);
- 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();
- }
- }
-
- 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 StatsdConfig createConfig(int atomId) {
- try {
- if (mIsPushedAtom) {
- return createSimpleEventMetricConfig(atomId);
- } else {
- return createSimpleGaugeMetricConfig(atomId);
+ 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);
+ }
}
- } catch (ParseException e) {
- logger.log(
- Level.SEVERE,
- "Failed to parse the config! line: "
- + e.getLine()
- + " col: "
- + e.getColumn()
- + " "
- + e.getMessage());
}
- return null;
}
- private StatsdConfig createSimpleEventMetricConfig(int atomId) throws ParseException {
- StatsdConfig.Builder baseBuilder = getSimpleEventMetricBaseConfig();
- baseBuilder.addAtomMatcher(createAtomMatcher(atomId));
- return baseBuilder.build();
+ 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 createSimpleGaugeMetricConfig(int atomId) throws ParseException {
- StatsdConfig.Builder baseBuilder = getSimpleGaugeMetricBaseConfig();
- baseBuilder.addAtomMatcher(createAtomMatcher(atomId));
- return baseBuilder.build();
+ 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 {
+ EventMetric.Builder eventMetricBuilder = EventMetric.newBuilder();
+ eventMetricBuilder
+ .setId(metricId)
+ .setWhat(atomMatcherId);
+ builder.addEventMetric(eventMetricBuilder.build());
+ builder.addAtomMatcher(createAtomMatcher(atomId, atomMatcherId));
+ }
+ atomMatcherId++;
+ mTrackedMetrics.add(metricId++);
+ }
+ return builder.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 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 StatsdConfig.Builder getSimpleGaugeMetricBaseConfig() throws ParseException {
- StatsdConfig.Builder builder = StatsdConfig.newBuilder();
- TextFormat.merge(GAUGE_BASE_CONFIG_STR, builder);
- return builder;
- }
-
- 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"
- + "\n"
- + "hash_strings_in_metric_report: false";
+ private static boolean isPulledAtom(int atomId) {
+ return atomId >= PULL_ATOM_START;
+ }
- 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"
- + "\n"
- + "hash_strings_in_metric_report: false";
-
+ 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 5cd3d5d..7bbeb16 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -1474,7 +1474,6 @@
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 a0ef224..ad99fdb 100644
--- a/core/java/android/annotation/Px.java
+++ b/core/java/android/annotation/Px.java
@@ -29,6 +29,8 @@
* 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 a278423..78fe002 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2496,7 +2496,7 @@
* @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 @@
* @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 @@
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 @@
* 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
@@ -3078,7 +3074,7 @@
*/
public int unsafeCheckOpRaw(String op, int uid, String packageName) {
try {
- return mService.checkOperation(strOpToOp(op), uid, packageName);
+ return mService.checkOperationRaw(strOpToOp(op), uid, packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index 7fe21b2..b556033 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;
@@ -37,10 +36,11 @@
* @param uid The UID for which to check.
* @param packageName The package for which to check.
* @param superImpl The super implementation.
+ * @param raw Whether to check the raw op i.e. not interpret the mode based on UID state.
* @return The app op check result.
*/
- int checkOperation(int code, int uid, String packageName,
- TriFunction<Integer, Integer, String, Integer> superImpl);
+ int checkOperation(int code, int uid, String packageName, boolean raw,
+ QuadFunction<Integer, Integer, String, Boolean, Integer> superImpl);
/**
* Allows overriding check audio operation behavior.
@@ -85,10 +85,7 @@
*
* @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/Context.java b/core/java/android/content/Context.java
index 001e328..6f12cad 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -691,7 +691,8 @@
*
* @see android.content.res.Resources.Theme#obtainStyledAttributes(int[])
*/
- public final TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) {
+ @NonNull
+ public final TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[] attrs) {
return getTheme().obtainStyledAttributes(attrs);
}
@@ -702,8 +703,9 @@
*
* @see android.content.res.Resources.Theme#obtainStyledAttributes(int, int[])
*/
- public final TypedArray obtainStyledAttributes(
- @StyleRes int resid, @StyleableRes int[] attrs) throws Resources.NotFoundException {
+ @NonNull
+ public final TypedArray obtainStyledAttributes(@StyleRes int resid,
+ @NonNull @StyleableRes int[] attrs) throws Resources.NotFoundException {
return getTheme().obtainStyledAttributes(resid, attrs);
}
@@ -714,8 +716,9 @@
*
* @see android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
*/
+ @NonNull
public final TypedArray obtainStyledAttributes(
- AttributeSet set, @StyleableRes int[] attrs) {
+ @Nullable AttributeSet set, @NonNull @StyleableRes int[] attrs) {
return getTheme().obtainStyledAttributes(set, attrs, 0, 0);
}
@@ -726,8 +729,9 @@
*
* @see android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
*/
- public final TypedArray obtainStyledAttributes(
- AttributeSet set, @StyleableRes int[] attrs, @AttrRes int defStyleAttr,
+ @NonNull
+ public final TypedArray obtainStyledAttributes(@Nullable AttributeSet set,
+ @NonNull @StyleableRes int[] attrs, @AttrRes int defStyleAttr,
@StyleRes int defStyleRes) {
return getTheme().obtainStyledAttributes(
set, attrs, defStyleAttr, defStyleRes);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index f81eb76..f06df3d 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -17,6 +17,7 @@
package android.content.pm;
import android.Manifest;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -54,6 +55,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Iterator;
@@ -142,6 +145,15 @@
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";
@@ -1594,6 +1606,29 @@
public static final int INVALID_ID = -1;
/** {@hide} */
private static final int[] NO_SESSIONS = {};
+
+ /** @hide */
+ @IntDef(value = {NO_ERROR, VERIFICATION_FAILED, ACTIVATION_FAILED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StagedSessionErrorCode{}
+ /**
+ * Constant indicating that no error occurred during the preparation or the activation of
+ * this staged session.
+ */
+ public static final int NO_ERROR = 0;
+
+ /**
+ * Constant indicating that an error occurred during the verification phase (pre-reboot) of
+ * this staged session.
+ */
+ public static final int VERIFICATION_FAILED = 1;
+
+ /**
+ * Constant indicating that an error occurred during the activation phase (post-reboot) of
+ * this staged session.
+ */
+ public static final int ACTIVATION_FAILED = 2;
+
/** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public int sessionId;
@@ -1653,6 +1688,14 @@
public int[] childSessionIds = NO_SESSIONS;
/** {@hide} */
+ public boolean isSessionApplied;
+ /** {@hide} */
+ public boolean isSessionReady;
+ /** {@hide} */
+ public boolean isSessionFailed;
+ private int mStagedSessionErrorCode;
+
+ /** {@hide} */
@UnsupportedAppUsage
public SessionInfo() {
}
@@ -1686,6 +1729,10 @@
if (childSessionIds == null) {
childSessionIds = NO_SESSIONS;
}
+ isSessionApplied = source.readBoolean();
+ isSessionReady = source.readBoolean();
+ isSessionFailed = source.readBoolean();
+ mStagedSessionErrorCode = source.readInt();
}
/**
@@ -1970,6 +2017,44 @@
return childSessionIds;
}
+ /**
+ * Whether the staged session has been applied successfully, meaning that all of its
+ * packages have been activated and no further action is required.
+ * Only meaningful if {@code isStaged} is true.
+ */
+ public boolean isSessionApplied() {
+ return isSessionApplied;
+ }
+
+ /**
+ * Whether the staged session is ready to be applied at next reboot. Only meaningful if
+ * {@code isStaged} is true.
+ */
+ public boolean isSessionReady() {
+ return isSessionReady;
+ }
+
+ /**
+ * Whether something went wrong and the staged session is declared as failed, meaning that
+ * it will be ignored at next reboot. Only meaningful if {@code isStaged} is true.
+ */
+ public boolean isSessionFailed() {
+ return isSessionFailed;
+ }
+
+ /**
+ * If something went wrong with a staged session, clients can check this error code to
+ * understand which kind of failure happened. Only meaningful if {@code isStaged} is true.
+ */
+ public int getStagedSessionErrorCode() {
+ return mStagedSessionErrorCode;
+ }
+
+ /** {@hide} */
+ public void setStagedSessionErrorCode(@StagedSessionErrorCode int errorCode) {
+ mStagedSessionErrorCode = errorCode;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -2001,6 +2086,10 @@
dest.writeBoolean(isStaged);
dest.writeInt(parentSessionId);
dest.writeIntArray(childSessionIds);
+ dest.writeBoolean(isSessionApplied);
+ dest.writeBoolean(isSessionReady);
+ dest.writeBoolean(isSessionFailed);
+ dest.writeInt(mStagedSessionErrorCode);
}
public static final Parcelable.Creator<SessionInfo>
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index ad82626d..35609c9 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -33,11 +33,29 @@
* 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/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 88b1c88..365ceac 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1468,7 +1468,8 @@
* @see #obtainStyledAttributes(int, int[])
* @see #obtainStyledAttributes(AttributeSet, int[], int, int)
*/
- public TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) {
+ @NonNull
+ public TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[] attrs) {
return mThemeImpl.obtainStyledAttributes(this, null, attrs, 0, 0);
}
@@ -1493,7 +1494,9 @@
* @see #obtainStyledAttributes(int[])
* @see #obtainStyledAttributes(AttributeSet, int[], int, int)
*/
- public TypedArray obtainStyledAttributes(@StyleRes int resId, @StyleableRes int[] attrs)
+ @NonNull
+ public TypedArray obtainStyledAttributes(@StyleRes int resId,
+ @NonNull @StyleableRes int[] attrs)
throws NotFoundException {
return mThemeImpl.obtainStyledAttributes(this, null, attrs, 0, resId);
}
@@ -1547,8 +1550,10 @@
* @see #obtainStyledAttributes(int[])
* @see #obtainStyledAttributes(int, int[])
*/
- public TypedArray obtainStyledAttributes(AttributeSet set,
- @StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
+ @NonNull
+ public TypedArray obtainStyledAttributes(@Nullable AttributeSet set,
+ @NonNull @StyleableRes int[] attrs, @AttrRes int defStyleAttr,
+ @StyleRes int defStyleRes) {
return mThemeImpl.obtainStyledAttributes(this, set, attrs, defStyleAttr, defStyleRes);
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 4714587..49c3dc6 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -187,13 +187,19 @@
* 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 @@
* 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 @@
*
* @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 @@
* @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 1b9a66c..80517ce 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -18,6 +18,7 @@
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 @@
/**
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public LinkProperties() {
}
@@ -195,7 +196,7 @@
* @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 @@
* object.
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public void setLinkAddresses(Collection<LinkAddress> addresses) {
mLinkAddresses.clear();
for (LinkAddress address: addresses) {
@@ -392,7 +393,7 @@
* @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 @@
* domains to search when resolving host names on this link.
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public void setDomains(String domains) {
mDomains = domains;
}
@@ -552,7 +553,7 @@
* @param mtu The MTU to use for this link.
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public void setMtu(int mtu) {
mMtu = mtu;
}
@@ -562,9 +563,7 @@
* this will return 0.
*
* @return The mtu value set for this link.
- * @hide
*/
- @UnsupportedAppUsage
public int getMtu() {
return mMtu;
}
@@ -613,7 +612,7 @@
*
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public boolean addRoute(RouteInfo route) {
if (route != null) {
String routeIface = route.getInterface();
@@ -688,7 +687,7 @@
* @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 @@
* 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 c41a56c..1b44c92 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -1017,7 +1017,7 @@
* @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 1a1d2d334..89d9961 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -28,7 +28,20 @@
* 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 @@
* <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 @@
* 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 @@
* 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 @@
* 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 04b6b44..3b01b03 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 @@
* @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/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 8a0d916..f3810bd 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -257,8 +257,7 @@
return sDriverMap.get(OpenGlDriverChoice.DEFAULT);
}
// Make sure we have good settings to use
- if (globalSettingsDriverPkgs.isEmpty() || globalSettingsDriverValues.isEmpty()
- || (globalSettingsDriverPkgs.size() != globalSettingsDriverValues.size())) {
+ if (globalSettingsDriverPkgs.size() != globalSettingsDriverValues.size()) {
Log.w(TAG,
"Global.Settings values are invalid: "
+ "globalSettingsDriverPkgs.size = "
@@ -299,9 +298,120 @@
}
/**
+ * Attempt to setup ANGLE with a temporary rules file.
+ * True: Temporary rules file was loaded.
+ * False: Temporary rules file was *not* loaded.
+ */
+ private boolean setupAngleWithTempRulesFile(Context context,
+ String packageName,
+ String paths,
+ String devOptIn) {
+ // Check for temporary rules if debuggable or root
+ if (!isDebuggable(context) && !(getCanLoadSystemLibraries() == 1)) {
+ Log.v(TAG, "Skipping loading temporary rules file");
+ return false;
+ }
+
+ String angleTempRules = SystemProperties.get(ANGLE_TEMP_RULES);
+
+ if ((angleTempRules == null) || angleTempRules.isEmpty()) {
+ Log.v(TAG, "System property '" + ANGLE_TEMP_RULES + "' is not set or is empty");
+ return false;
+ }
+
+ Log.i(TAG, "Detected system property " + ANGLE_TEMP_RULES + ": " + angleTempRules);
+
+ File tempRulesFile = new File(angleTempRules);
+ if (tempRulesFile.exists()) {
+ Log.i(TAG, angleTempRules + " exists, loading file.");
+ try {
+ FileInputStream stream = new FileInputStream(angleTempRules);
+
+ try {
+ FileDescriptor rulesFd = stream.getFD();
+ long rulesOffset = 0;
+ long rulesLength = stream.getChannel().size();
+ Log.i(TAG, "Loaded temporary ANGLE rules from " + angleTempRules);
+
+ setAngleInfo(paths, packageName, devOptIn, rulesFd, rulesOffset, rulesLength);
+
+ stream.close();
+
+ // We successfully setup ANGLE, so return with good status
+ return true;
+ } catch (IOException e) {
+ Log.w(TAG, "Hit IOException thrown by FileInputStream: " + e);
+ }
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "Temp ANGLE rules file not found: " + e);
+ } catch (SecurityException e) {
+ Log.w(TAG, "Temp ANGLE rules file not accessible: " + e);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Attempt to setup ANGLE with a (temporary) default rules file: b/121153494
+ * True: Rules file was loaded.
+ * False: Rules file was *not* loaded.
+ */
+ private boolean setupAngleRulesDebug(String packageName, String paths, String devOptIn) {
+ // b/121153494
+ // Skip APK rules file checking.
+ if (!DEBUG) {
+ Log.v(TAG, "Skipping loading the rules file.");
+ // Fill in some default values for now, so the loader can get an answer when it asks.
+ // Most importantly, we need to indicate which app we are init'ing and what the
+ // developer options for it are so we can turn on ANGLE if needed.
+ setAngleInfo(paths, packageName, devOptIn, null, 0, 0);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Attempt to setup ANGLE with a rules file loaded from the ANGLE APK.
+ * True: APK rules file was loaded.
+ * False: APK rules file was *not* loaded.
+ */
+ private boolean setupAngleRulesApk(String anglePkgName,
+ ApplicationInfo angleInfo,
+ Context context,
+ String packageName,
+ String paths,
+ String devOptIn) {
+ // Pass the rules file to loader for ANGLE decisions
+ try {
+ AssetManager angleAssets =
+ context.getPackageManager().getResourcesForApplication(angleInfo).getAssets();
+
+ try {
+ AssetFileDescriptor assetsFd = angleAssets.openFd(ANGLE_RULES_FILE);
+
+ setAngleInfo(paths, packageName, devOptIn, assetsFd.getFileDescriptor(),
+ assetsFd.getStartOffset(), assetsFd.getLength());
+
+ assetsFd.close();
+
+ return true;
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to get AssetFileDescriptor for " + ANGLE_RULES_FILE
+ + " from '" + anglePkgName + "': " + e);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Failed to get AssetManager for '" + anglePkgName + "': " + e);
+ }
+
+ return false;
+ }
+
+ /**
* Pass ANGLE details down to trigger enable logic
*/
- private void setupAngle(Context context, Bundle bundle, String packageName) {
+ public void setupAngle(Context context, Bundle bundle, String packageName) {
String devOptIn = getDriverForPkg(bundle, packageName);
if (DEBUG) {
@@ -327,86 +437,29 @@
String abi = chooseAbi(angleInfo);
// Build a path that includes installed native libs and APK
- StringBuilder sb = new StringBuilder();
- sb.append(angleInfo.nativeLibraryDir)
- .append(File.pathSeparator)
- .append(angleInfo.sourceDir)
- .append("!/lib/")
- .append(abi);
- String paths = sb.toString();
+ String paths = angleInfo.nativeLibraryDir
+ + File.pathSeparator
+ + angleInfo.sourceDir
+ + "!/lib/"
+ + abi;
if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths);
- // Look up rules file to pass to ANGLE
- FileDescriptor rulesFd = null;
- long rulesOffset = 0;
- long rulesLength = 0;
-
- // Check for temporary rules if debuggable or root
- if (isDebuggable(context) || (getCanLoadSystemLibraries() == 1)) {
- String angleTempRules = SystemProperties.get(ANGLE_TEMP_RULES);
- if (angleTempRules != null && !angleTempRules.isEmpty()) {
- Log.i(TAG, "Detected system property " + ANGLE_TEMP_RULES + ": " + angleTempRules);
- File tempRulesFile = new File(angleTempRules);
- if (tempRulesFile.exists()) {
- Log.i(TAG, angleTempRules + " exists, loading file.");
- FileInputStream stream = null;
- try {
- stream = new FileInputStream(angleTempRules);
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Unable to create stream for temp ANGLE rules");
- }
-
- if (stream != null) {
- try {
- rulesFd = stream.getFD();
- rulesOffset = 0;
- rulesLength = stream.getChannel().size();
- Log.i(TAG, "Loaded temporary ANGLE rules from " + angleTempRules);
- } catch (IOException e) {
- Log.w(TAG, "Failed to get input stream for " + angleTempRules);
- }
- }
- }
- }
+ if (setupAngleWithTempRulesFile(context, packageName, paths, devOptIn)) {
+ // We setup ANGLE with a temp rules file, so we're done here.
+ return;
}
- // If no temp rules, load the real ones from the APK
- if (DEBUG && (rulesFd == null)) {
-
- // Pass the rules file to loader for ANGLE decisions
- AssetManager angleAssets = null;
- try {
- angleAssets =
- context.getPackageManager().getResourcesForApplication(angleInfo).getAssets();
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Failed to get AssetManager for '" + anglePkgName + "'");
- return;
- }
-
- AssetFileDescriptor assetsFd = null;
- try {
- assetsFd = angleAssets.openFd(ANGLE_RULES_FILE);
- } catch (IOException e) {
- Log.w(TAG, "Failed to get AssetFileDescriptor for " + ANGLE_RULES_FILE + " from "
- + "'" + anglePkgName + "'");
- return;
- }
-
- if (assetsFd != null) {
- rulesFd = assetsFd.getFileDescriptor();
- rulesOffset = assetsFd.getStartOffset();
- rulesLength = assetsFd.getLength();
- } else {
- Log.w(TAG, "Failed to get file descriptor for " + ANGLE_RULES_FILE);
- return;
- }
+ // b/121153494
+ if (setupAngleRulesDebug(packageName, paths, devOptIn)) {
+ // We setup ANGLE with defaults, so we're done here.
+ return;
}
- // Further opt-in logic is handled in native, so pass relevant info down
- // TODO: Move the ANGLE selection logic earlier so we don't need to keep these
- // file descriptors open.
- setAngleInfo(paths, packageName, devOptIn, rulesFd, rulesOffset, rulesLength);
+ if (setupAngleRulesApk(anglePkgName, angleInfo, context, packageName, paths, devOptIn)) {
+ // We setup ANGLE with rules from the APK, so we're done here.
+ return;
+ }
}
/**
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 8a03e9e..df1a713 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -16,6 +16,7 @@
package android.os.storage;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
@@ -348,6 +349,32 @@
return intent;
}
+ /**
+ * Builds an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} to allow the user to grant access to any
+ * directory subtree (or entire volume) from the {@link android.provider.DocumentsProvider}s
+ * available on the device. The initial location of the document navigation will be the root of
+ * this {@link StorageVolume}.
+ *
+ * Note that the returned {@link Intent} simply suggests that the user picks this {@link
+ * StorageVolume} by default, but the user may select a different location. Callers must respect
+ * the user's chosen location, even if it is different from the originally requested location.
+ *
+ * @return intent to {@link Intent#ACTION_OPEN_DOCUMENT_TREE} initially showing the contents
+ * of this {@link StorageVolume}
+ * @see Intent#ACTION_OPEN_DOCUMENT_TREE
+ */
+ @NonNull public Intent createOpenDocumentTreeIntent() {
+ final String rootId = isEmulated()
+ ? DocumentsContract.EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID
+ : mFsUuid;
+ final Uri rootUri = DocumentsContract.buildRootUri(
+ DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY, rootId);
+ final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
+ .putExtra(DocumentsContract.EXTRA_INITIAL_URI, rootUri)
+ .putExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, true);
+ return intent;
+ }
+
@Override
public boolean equals(Object obj) {
if (obj instanceof StorageVolume && mPath != null) {
diff --git a/core/java/android/permission/RuntimePermissionPresenter.java b/core/java/android/permission/RuntimePermissionPresenter.java
index 3ce3c9d..c607e3f 100644
--- a/core/java/android/permission/RuntimePermissionPresenter.java
+++ b/core/java/android/permission/RuntimePermissionPresenter.java
@@ -16,27 +16,28 @@
package android.permission;
+import static android.permission.RuntimePermissionPresenterService.SERVICE_INTERFACE;
+
import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.ServiceConnection;
+import android.content.pm.ResolveInfo;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Message;
import android.os.RemoteCallback;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
+import com.android.internal.infra.AbstractRemoteService;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -80,7 +81,7 @@
*/
public interface OnCountPermissionAppsResultCallback {
/**
- * The result for {@link #countPermissionApps(List, boolean,
+ * The result for {@link #countPermissionApps(List, boolean, boolean,
* OnCountPermissionAppsResultCallback, Handler)}.
*
* @param numApps The number of apps that have one of the permissions
@@ -110,8 +111,13 @@
}
}
- private RuntimePermissionPresenter(Context context) {
- mRemoteService = new RemoteService(context);
+ private RuntimePermissionPresenter(@NonNull Context context) {
+ Intent intent = new Intent(SERVICE_INTERFACE);
+ intent.setPackage(context.getPackageManager().getPermissionControllerPackageName());
+ ResolveInfo serviceInfo = context.getPackageManager().resolveService(intent, 0);
+
+ mRemoteService = new RemoteService(context,
+ serviceInfo.getComponentInfo().getComponentName());
}
/**
@@ -126,8 +132,8 @@
checkNotNull(packageName);
checkNotNull(callback);
- mRemoteService.processMessage(obtainMessage(RemoteService::getAppPermissions,
- mRemoteService, packageName, callback, handler));
+ mRemoteService.scheduleRequest(new PendingGetAppPermissionRequest(mRemoteService,
+ packageName, callback, handler == null ? mRemoteService.getHandler() : handler));
}
/**
@@ -141,8 +147,8 @@
checkNotNull(packageName);
checkNotNull(permissionName);
- mRemoteService.processMessage(obtainMessage(RemoteService::revokeAppPermissions,
- mRemoteService, packageName, permissionName));
+ mRemoteService.scheduleAsyncRequest(new PendingRevokeAppPermissionRequest(packageName,
+ permissionName));
}
/**
@@ -160,183 +166,192 @@
checkCollectionElementsNotNull(permissionNames, "permissionNames");
checkNotNull(callback);
- mRemoteService.processMessage(obtainMessage(RemoteService::countPermissionApps,
- mRemoteService, permissionNames, countOnlyGranted, countSystem, callback, handler));
+ mRemoteService.scheduleRequest(new PendingCountPermissionAppsRequest(mRemoteService,
+ permissionNames, countOnlyGranted, countSystem, callback,
+ handler == null ? mRemoteService.getHandler() : handler));
}
- private static final class RemoteService
- extends Handler implements ServiceConnection {
+ /**
+ * A connection to the remote service
+ */
+ static final class RemoteService extends
+ AbstractMultiplePendingRequestsRemoteService<RemoteService,
+ IRuntimePermissionPresenter> {
private static final long UNBIND_TIMEOUT_MILLIS = 10000;
+ private static final long MESSAGE_TIMEOUT_MILLIS = 30000;
- public static final int MSG_UNBIND = 0;
-
- private final Object mLock = new Object();
-
- private final Context mContext;
-
- @GuardedBy("mLock")
- private final List<Message> mPendingWork = new ArrayList<>();
-
- @GuardedBy("mLock")
- private IRuntimePermissionPresenter mRemoteInstance;
-
- @GuardedBy("mLock")
- private boolean mBound;
-
- RemoteService(Context context) {
- super(context.getMainLooper(), null, false);
- mContext = context;
+ /**
+ * Create a connection to the remote service
+ *
+ * @param context A context to use
+ * @param componentName The component of the service to connect to
+ */
+ RemoteService(@NonNull Context context, @NonNull ComponentName componentName) {
+ super(context, SERVICE_INTERFACE, componentName, UserHandle.myUserId(),
+ service -> Log.e(TAG, "RuntimePermPresenterService " + service + " died"),
+ false, false, 1);
}
- public void processMessage(Message message) {
- synchronized (mLock) {
- if (!mBound) {
- Intent intent = new Intent(
- RuntimePermissionPresenterService.SERVICE_INTERFACE);
- intent.setPackage(mContext.getPackageManager()
- .getPermissionControllerPackageName());
- mBound = mContext.bindService(intent, this,
- Context.BIND_AUTO_CREATE);
+ /**
+ * @return The default handler used by this service.
+ */
+ Handler getHandler() {
+ return mHandler;
+ }
+
+ @Override
+ protected @NonNull IRuntimePermissionPresenter getServiceInterface(
+ @NonNull IBinder binder) {
+ return IRuntimePermissionPresenter.Stub.asInterface(binder);
+ }
+
+ @Override
+ protected long getTimeoutIdleBindMillis() {
+ return UNBIND_TIMEOUT_MILLIS;
+ }
+
+ @Override
+ protected long getRemoteRequestMillis() {
+ return MESSAGE_TIMEOUT_MILLIS;
+ }
+
+ @Override
+ public void scheduleRequest(@NonNull PendingRequest<RemoteService,
+ IRuntimePermissionPresenter> pendingRequest) {
+ super.scheduleRequest(pendingRequest);
+ }
+
+ @Override
+ public void scheduleAsyncRequest(
+ @NonNull AsyncRequest<IRuntimePermissionPresenter> request) {
+ super.scheduleAsyncRequest(request);
+ }
+ }
+
+ /**
+ * Request for {@link #getAppPermissions}
+ */
+ private static final class PendingGetAppPermissionRequest extends
+ AbstractRemoteService.PendingRequest<RemoteService, IRuntimePermissionPresenter> {
+ private final @NonNull String mPackageName;
+ private final @NonNull OnGetAppPermissionResultCallback mCallback;
+
+ private final @NonNull RemoteCallback mRemoteCallback;
+
+ private PendingGetAppPermissionRequest(@NonNull RemoteService service,
+ @NonNull String packageName, @NonNull OnGetAppPermissionResultCallback callback,
+ @NonNull Handler handler) {
+ super(service);
+
+ mPackageName = packageName;
+ mCallback = callback;
+
+ mRemoteCallback = new RemoteCallback(result -> {
+ final List<RuntimePermissionPresentationInfo> reportedPermissions;
+ List<RuntimePermissionPresentationInfo> permissions = null;
+ if (result != null) {
+ permissions = result.getParcelableArrayList(KEY_RESULT);
}
- mPendingWork.add(message);
- scheduleNextMessageIfNeededLocked();
- }
+ if (permissions == null) {
+ permissions = Collections.emptyList();
+ }
+ reportedPermissions = permissions;
+
+ callback.onGetAppPermissions(reportedPermissions);
+
+ finish();
+ }, handler);
}
@Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- synchronized (mLock) {
- mRemoteInstance = IRuntimePermissionPresenter.Stub.asInterface(service);
- scheduleNextMessageIfNeededLocked();
- }
+ protected void onTimeout(RemoteService remoteService) {
+ mCallback.onGetAppPermissions(Collections.emptyList());
}
@Override
- public void onServiceDisconnected(ComponentName name) {
- synchronized (mLock) {
- mRemoteInstance = null;
- }
- }
-
- private void getAppPermissions(@NonNull String packageName,
- @NonNull OnGetAppPermissionResultCallback callback, @Nullable Handler handler) {
- final IRuntimePermissionPresenter remoteInstance;
- synchronized (mLock) {
- remoteInstance = mRemoteInstance;
- }
- if (remoteInstance == null) {
- return;
- }
+ public void run() {
try {
- remoteInstance.getAppPermissions(packageName,
- new RemoteCallback(result -> {
- final List<RuntimePermissionPresentationInfo> reportedPermissions;
- List<RuntimePermissionPresentationInfo> permissions = null;
- if (result != null) {
- permissions = result.getParcelableArrayList(KEY_RESULT);
- }
- if (permissions == null) {
- permissions = Collections.emptyList();
- }
- reportedPermissions = permissions;
- if (handler != null) {
- handler.post(
- () -> callback.onGetAppPermissions(reportedPermissions));
- } else {
- callback.onGetAppPermissions(reportedPermissions);
- }
- }, this));
- } catch (RemoteException re) {
- Log.e(TAG, "Error getting app permissions", re);
- }
- scheduleUnbind();
-
- synchronized (mLock) {
- scheduleNextMessageIfNeededLocked();
+ getService().getServiceInterface().getAppPermissions(mPackageName, mRemoteCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error getting app permission", e);
}
}
+ }
- private void revokeAppPermissions(@NonNull String packageName,
+ /**
+ * Request for {@link #revokeRuntimePermission}
+ */
+ private static final class PendingRevokeAppPermissionRequest
+ implements AbstractRemoteService.AsyncRequest<IRuntimePermissionPresenter> {
+ private final @NonNull String mPackageName;
+ private final @NonNull String mPermissionName;
+
+ private PendingRevokeAppPermissionRequest(@NonNull String packageName,
@NonNull String permissionName) {
- final IRuntimePermissionPresenter remoteInstance;
- synchronized (mLock) {
- remoteInstance = mRemoteInstance;
- }
- if (remoteInstance == null) {
- return;
- }
- try {
- remoteInstance.revokeRuntimePermission(packageName, permissionName);
- } catch (RemoteException re) {
- Log.e(TAG, "Error getting app permissions", re);
- }
-
- synchronized (mLock) {
- scheduleNextMessageIfNeededLocked();
- }
+ mPackageName = packageName;
+ mPermissionName = permissionName;
}
- private void countPermissionApps(@NonNull List<String> permissionNames,
- boolean countOnlyGranted, boolean countSystem,
- @NonNull OnCountPermissionAppsResultCallback callback, @Nullable Handler handler) {
- final IRuntimePermissionPresenter remoteInstance;
-
- synchronized (mLock) {
- remoteInstance = mRemoteInstance;
- }
- if (remoteInstance == null) {
- return;
- }
-
+ @Override
+ public void run(IRuntimePermissionPresenter remoteInterface) {
try {
- remoteInstance.countPermissionApps(permissionNames, countOnlyGranted, countSystem,
- new RemoteCallback(result -> {
- final int numApps;
- if (result != null) {
- numApps = result.getInt(KEY_RESULT);
- } else {
- numApps = 0;
- }
-
- if (handler != null) {
- handler.post(() -> callback.onCountPermissionApps(numApps));
- } else {
- callback.onCountPermissionApps(numApps);
- }
- }, this));
- } catch (RemoteException re) {
- Log.e(TAG, "Error counting permission apps", re);
- }
-
- scheduleUnbind();
-
- synchronized (mLock) {
- scheduleNextMessageIfNeededLocked();
+ remoteInterface.revokeRuntimePermission(mPackageName, mPermissionName);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error revoking app permission", e);
}
}
+ }
- private void unbind() {
- synchronized (mLock) {
- if (mBound) {
- mContext.unbindService(this);
- mBound = false;
+ /**
+ * Request for {@link #countPermissionApps}
+ */
+ private static final class PendingCountPermissionAppsRequest extends
+ AbstractRemoteService.PendingRequest<RemoteService, IRuntimePermissionPresenter> {
+ private final @NonNull List<String> mPermissionNames;
+ private final @NonNull OnCountPermissionAppsResultCallback mCallback;
+ private final boolean mCountOnlyGranted;
+ private final boolean mCountSystem;
+
+ private final @NonNull RemoteCallback mRemoteCallback;
+
+ private PendingCountPermissionAppsRequest(@NonNull RemoteService service,
+ @NonNull List<String> permissionNames, boolean countOnlyGranted,
+ boolean countSystem, @NonNull OnCountPermissionAppsResultCallback callback,
+ @NonNull Handler handler) {
+ super(service);
+
+ mPermissionNames = permissionNames;
+ mCountOnlyGranted = countOnlyGranted;
+ mCountSystem = countSystem;
+ mCallback = callback;
+
+ mRemoteCallback = new RemoteCallback(result -> {
+ final int numApps;
+ if (result != null) {
+ numApps = result.getInt(KEY_RESULT);
+ } else {
+ numApps = 0;
}
- mRemoteInstance = null;
- }
+
+ callback.onCountPermissionApps(numApps);
+
+ finish();
+ }, handler);
}
- @GuardedBy("mLock")
- private void scheduleNextMessageIfNeededLocked() {
- if (mBound && mRemoteInstance != null && !mPendingWork.isEmpty()) {
- Message nextMessage = mPendingWork.remove(0);
- sendMessage(nextMessage);
- }
+ @Override
+ protected void onTimeout(RemoteService remoteService) {
+ mCallback.onCountPermissionApps(0);
}
- private void scheduleUnbind() {
- removeMessages(MSG_UNBIND);
- sendMessageDelayed(PooledLambda.obtainMessage(RemoteService::unbind, this)
- .setWhat(MSG_UNBIND), UNBIND_TIMEOUT_MILLIS);
+ @Override
+ public void run() {
+ try {
+ getService().getServiceInterface().countPermissionApps(mPermissionNames,
+ mCountOnlyGranted, mCountSystem, mRemoteCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error counting permission apps", e);
+ }
}
}
}
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index cd991cc..a323ed1 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -238,6 +238,9 @@
"com.android.externalstorage.documents";
/** {@hide} */
+ public static final String EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID = "primary";
+
+ /** {@hide} */
public static final String PACKAGE_DOCUMENTS_UI = "com.android.documentsui";
/**
@@ -857,16 +860,6 @@
}
/**
- * Builds URI for user home directory on external (local) storage.
- * {@hide}
- */
- public static Uri buildHomeUri() {
- // TODO: Avoid this type of interpackage copying. Added here to avoid
- // direct coupling, but not ideal.
- return DocumentsContract.buildRootUri(EXTERNAL_STORAGE_PROVIDER_AUTHORITY, "home");
- }
-
- /**
* Build URI representing the recently modified documents of a specific root
* in a document provider. When queried, a provider will return zero or more
* rows with columns defined by {@link Document}.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a39f696a..d93985c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12820,6 +12820,17 @@
"privileged_device_identifier_3p_check_relaxed";
/**
+ * If set to 1, the device identifier check will be relaxed to the previous READ_PHONE_STATE
+ * permission check for preloaded non-privileged apps.
+ *
+ * STOPSHIP: Remove this once we ship with the new device identifier check enabled.
+ *
+ * @hide
+ */
+ public static final String PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED =
+ "privileged_device_identifier_non_priv_check_relaxed";
+
+ /**
* If set to 1, SettingsProvider's restoreAnyVersion="true" attribute will be ignored
* and restoring to lower version of platform API will be skipped.
*
@@ -12954,12 +12965,11 @@
* <p>Possible values are:
*
* <ul>
- * <li>If set to {@link #CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED_DEFAULT}, it will only
- * be set if the OEM provides and defines the service name by overlaying
- * {@code config_defaultContentCaptureService} (this is the "default" mode)
- * <li>If set to {@link #CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED_ALWAYS}, it will
- * always be enabled, even when the resource is not overlaid (this is useful during
- * development and/or to run the CTS tests on AOSP builds).
+ * <li>If set to {@code default}, it will only be set if the OEM provides and defines the
+ * service name by overlaying {@code config_defaultContentCaptureService} (this is the
+ * "default" mode)
+ * <li>If set to {@code always}, it will always be enabled, even when the resource is not
+ * overlaid (this is useful during development and to run the CTS tests on AOSP builds).
* <li>Otherwise, it's explicitly disabled (this could work as a "kill switch" so OEMs
* can disable it remotely in case of emergency by setting to something else (like
* {@code "false"}); notice that it's also disabled if the OEM doesn't explicitly set one
@@ -12971,11 +12981,6 @@
public static final String CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED =
"content_capture_service_explicitly_enabled";
- /** @hide */
- public static final String CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED_DEFAULT = "default";
- /** @hide */
- public static final String CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED_ALWAYS = "always";
-
/** {@hide} */
public static final String ISOLATED_STORAGE_LOCAL = "isolated_storage_local";
/** {@hide} */
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index 27df845..bad7ddd 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -163,14 +163,15 @@
final int height = rect.bottom - rect.top;
final int width = rect.right - rect.left;
final WindowManager.LayoutParams windowParams = window.getAttributes();
- windowParams.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
- windowParams.y = rect.top - height;
+ windowParams.gravity = Gravity.TOP | Gravity.LEFT;
+ windowParams.y = rect.top + height;
windowParams.height = height;
windowParams.x = rect.left;
windowParams.width = width;
window.setAttributes(windowParams);
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+ window.setBackgroundDrawableResource(android.R.color.transparent);
mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
final ViewGroup.LayoutParams diagParams = new ViewGroup.LayoutParams(width, height);
diff --git a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
index 028180d..784e719 100644
--- a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
+++ b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
@@ -17,26 +17,30 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.content.pm.ParceledListSlice;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.contentcapture.ContentCaptureEvent;
+import java.util.Arrays;
import java.util.List;
/**
- * Batch of content capture events.
+ * Not needed anymore...
+ *
+ * @deprecated
*
* @hide
*/
@SystemApi
+@Deprecated
public final class ContentCaptureEventsRequest implements Parcelable {
+// TODO(b/121033016): remove .java and .aidl once service implementation doesn't use it anymore
- private final ParceledListSlice<ContentCaptureEvent> mEvents;
+ private final ContentCaptureEvent mEvent;
/** @hide */
- public ContentCaptureEventsRequest(@NonNull ParceledListSlice<ContentCaptureEvent> events) {
- mEvents = events;
+ public ContentCaptureEventsRequest(@NonNull ContentCaptureEvent event) {
+ mEvent = event;
}
/**
@@ -44,7 +48,7 @@
*/
@NonNull
public List<ContentCaptureEvent> getEvents() {
- return mEvents.getList();
+ return Arrays.asList(mEvent);
}
@Override
@@ -54,7 +58,7 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeParcelable(mEvents, flags);
+ parcel.writeParcelable(mEvent, flags);
}
public static final Parcelable.Creator<ContentCaptureEventsRequest> CREATOR =
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 953ccf1..64f2355 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -40,6 +40,7 @@
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;
@@ -108,10 +109,9 @@
new IContentCaptureDirectManager.Stub() {
@Override
- public void sendEvents(String sessionId,
- @SuppressWarnings("rawtypes") ParceledListSlice events) {
+ public void sendEvents(@SuppressWarnings("rawtypes") ParceledListSlice events) {
mHandler.sendMessage(obtainMessage(ContentCaptureService::handleSendEvents,
- ContentCaptureService.this, sessionId, Binder.getCallingUid(), events));
+ ContentCaptureService.this, Binder.getCallingUid(), events));
}
};
@@ -217,18 +217,29 @@
}
/**
- * Notifies the service of {@link ContentCaptureEvent events} associated with a content capture
- * session.
*
- * @param sessionId the session's Id
- * @param request the events
+ * @deprecated use {@link #onContentCaptureEvent(ContentCaptureSessionId, ContentCaptureEvent)}
+ * instead.
*/
+ @Deprecated
public void onContentCaptureEventsRequest(@NonNull ContentCaptureSessionId sessionId,
@NonNull ContentCaptureEventsRequest request) {
if (VERBOSE) Log.v(TAG, "onContentCaptureEventsRequest(id=" + sessionId + ")");
}
/**
+ * Notifies the service of {@link ContentCaptureEvent events} associated with a content capture
+ * session.
+ *
+ * @param sessionId the session's Id
+ * @param event the event
+ */
+ public void onContentCaptureEvent(@NonNull ContentCaptureSessionId sessionId,
+ @NonNull ContentCaptureEvent event) {
+ if (VERBOSE) Log.v(TAG, "onContentCaptureEventsRequest(id=" + sessionId + ")");
+ onContentCaptureEventsRequest(sessionId, new ContentCaptureEventsRequest(event));
+ }
+ /**
* Notifies the service of {@link SnapshotData snapshot data} associated with a session.
*
* @param sessionId the session's Id
@@ -271,11 +282,37 @@
mClientInterface.asBinder());
}
- private void handleSendEvents(@NonNull String sessionId, int uid,
- @NonNull ParceledListSlice<ContentCaptureEvent> events) {
- if (handleIsRightCallerFor(sessionId, uid)) {
- onContentCaptureEventsRequest(new ContentCaptureSessionId(sessionId),
- new ContentCaptureEventsRequest(events));
+ private void handleSendEvents(int uid,
+ @NonNull ParceledListSlice<ContentCaptureEvent> parceledEvents) {
+
+ // Most events belong to the same session, so we can keep a reference to the last one
+ // to avoid creating too many ContentCaptureSessionId objects
+ String lastSessionId = null;
+ ContentCaptureSessionId sessionId = null;
+
+ 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)) {
+ sessionId = new ContentCaptureSessionId(sessionIdString);
+ lastSessionId = sessionIdString;
+ }
+ 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);
+ }
}
}
@@ -290,9 +327,18 @@
}
/**
- * 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);
@@ -323,7 +369,7 @@
final Bundle extras;
if (binder != null) {
extras = new Bundle();
- extras.putBinder(ContentCaptureSession.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 0000000..b625fd6
--- /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 0000000..b410e35
--- /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 49a7320..4be1f9c 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 @@
*/
@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 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 @@
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 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
* @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 @@
* @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 @@
* @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 @@
* 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 @@
* 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 @@
* <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 @@
* 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 @@
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 6893c85..50ecbeb 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 45be527..c2cdf09 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.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 c850a4e..dd0242a 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -19,7 +19,7 @@
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 @@
* @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 @@
* @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 @@
* @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 d100f12..680e85f 100644
--- a/core/java/android/util/SparseSetArray.java
+++ b/core/java/android/util/SparseSetArray.java
@@ -55,6 +55,13 @@
}
/**
+ * @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 9dfd43c..330d72f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -107,12 +107,6 @@
*/
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 0000000..7b9f78e
--- /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 fb4f9c0..4ab1f26 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -28,6 +28,7 @@
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
+import java.util.ArrayList;
/**
* Implements {@link WindowInsetsController} on the client.
@@ -41,6 +42,7 @@
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 @@
/**
* @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 @@
}
}
+ @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 @@
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 0cb8ad7..f8148a9 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -70,7 +70,8 @@
*
* @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 689b14f..63025dc 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 @@
/** 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 @@
* @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 @@
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 @@
}
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
similarity index 72%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
rename to core/java/android/view/SyncRtSurfaceTransactionApplier.java
index 807edf6..0270acb 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 @@
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 @@
}
}
- 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 @@
* @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 @@
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 468d922..57635ef 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9046,17 +9046,16 @@
* {@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 @@
*
* @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 0000000..b27a23d
--- /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 0000000..9de517d
--- /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 7be5f2e..a35be27 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 @@
* 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/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 699b34a..56f973e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -77,7 +77,6 @@
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-
//TODO: use java.lang.ref.Cleaner once Android supports Java 9
import sun.misc.Cleaner;
@@ -2966,12 +2965,13 @@
if (afm == null) return null;
final View view = afm.getClient().autofillClientFindViewByAutofillIdTraversal(id);
- // TODO(b/111330312): optimize (for example, use temp rect from attach info) and
- // fix (for example, take system status bar height into account) logic below
+ final Rect windowVisibleDisplayFrame = new Rect();
+ view.getWindowVisibleDisplayFrame(windowVisibleDisplayFrame);
final int[] location = new int[2];
view.getLocationOnScreen(location);
- final Rect rect = new Rect(location[0], location[1], location[0] + view.getWidth(),
- location[1] + view.getHeight());
+ final Rect rect = new Rect(location[0], location[1] - windowVisibleDisplayFrame.top,
+ location[0] + view.getWidth(),
+ location[1] - windowVisibleDisplayFrame.top + view.getHeight());
if (sVerbose) {
Log.v(TAG, "Coordinates for " + id + ": " + rect);
}
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
new file mode 100644
index 0000000..5166831
--- /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 9c11743..2d2987a 100644
--- a/core/java/android/view/contentcapture/ContentCaptureContext.java
+++ b/core/java/android/view/contentcapture/ContentCaptureContext.java
@@ -21,6 +21,7 @@
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 @@
/**
* 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 @@
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 @@
}
/**
- * 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 @@
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 @@
.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 @@
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 5d8fe5f..9e3da92 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -29,15 +29,14 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-// TODO(b/111276913): add javadocs / implement Parcelable / implement
/** @hide */
@SystemApi
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.
@@ -72,29 +71,32 @@
@Retention(RetentionPolicy.SOURCE)
public @interface EventType{}
+ private final @NonNull String mSessionId;
private final int mType;
private final long mEventTime;
private final int mFlags;
private @Nullable AutofillId mId;
private @Nullable ViewNode mNode;
private @Nullable CharSequence mText;
+ private @Nullable String mParentSessionId;
+ private @Nullable ContentCaptureContext mClientContext;
/** @hide */
- public ContentCaptureEvent(int type, long eventTime, int flags) {
+ public ContentCaptureEvent(@NonNull String sessionId, int type, long eventTime, int flags) {
+ mSessionId = sessionId;
mType = type;
mEventTime = eventTime;
mFlags = flags;
}
-
/** @hide */
- public ContentCaptureEvent(int type, int flags) {
- this(type, System.currentTimeMillis(), flags);
+ public ContentCaptureEvent(@NonNull String sessionId, int type, int flags) {
+ this(sessionId, type, System.currentTimeMillis(), flags);
}
/** @hide */
- public ContentCaptureEvent(int type) {
- this(type, /* flags= */ 0);
+ public ContentCaptureEvent(@NonNull String sessionId, int type) {
+ this(sessionId, type, /* flags= */ 0);
}
/** @hide */
@@ -103,13 +105,61 @@
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) {
mNode = Preconditions.checkNotNull(node);
return this;
}
/** @hide */
+ @NonNull
public ContentCaptureEvent setText(@Nullable CharSequence text) {
mText = text;
return this;
@@ -183,7 +233,17 @@
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");
}
}
@@ -214,12 +274,19 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeString(mSessionId);
parcel.writeInt(mType);
parcel.writeLong(mEventTime);
parcel.writeInt(mFlags);
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 =
@@ -227,10 +294,12 @@
@Override
public ContentCaptureEvent createFromParcel(Parcel parcel) {
+ final String sessionId = parcel.readString();
final int type = parcel.readInt();
final long eventTime = parcel.readLong();
final int flags = parcel.readInt();
- final ContentCaptureEvent event = new ContentCaptureEvent(type, eventTime, flags);
+ final ContentCaptureEvent event =
+ new ContentCaptureEvent(sessionId, type, eventTime, flags);
final AutofillId id = parcel.readParcelable(null);
if (id != null) {
event.setAutofillId(id);
@@ -240,6 +309,12 @@
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;
}
@@ -252,6 +327,10 @@
/** @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 7fbbfb7..9830790 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -18,13 +18,13 @@
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 @@
@NonNull
private final Handler mHandler;
- private ContentCaptureSession mMainSession;
+ private MainContentCaptureSession mMainSession;
/** @hide */
public ContentCaptureManager(@NonNull Context context,
@@ -93,46 +93,20 @@
}
/**
- * 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 ContentCaptureSession(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 ContentCaptureSession 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 ContentCaptureSession(mContext, mHandler, mService, mDisabled,
- /* contentCaptureContext= */ 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 f411cf7..9f666a4 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -15,62 +15,35 @@
*/
package android.view.contentcapture;
-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;
import static android.view.contentcapture.ContentCaptureManager.DEBUG;
import static android.view.contentcapture.ContentCaptureManager.VERBOSE;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
+import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ParceledListSlice;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
-import android.os.RemoteException;
-import android.os.SystemClock;
import android.util.Log;
-import android.util.TimeUtils;
import android.view.View;
import android.view.ViewStructure;
import android.view.autofill.AutofillId;
+import android.view.contentcapture.ViewNode.ViewStructureImpl;
-import com.android.internal.os.IResultReceiver;
import com.android.internal.util.Preconditions;
import dalvik.system.CloseGuard;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
import java.util.UUID;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
* Session used to notify a system-provided Content Capture service about events associated with
* views.
*/
-public final class ContentCaptureSession implements AutoCloseable {
-
- /*
- * IMPLEMENTATION NOTICE:
- *
- * All methods in this class should return right away, or do the real work in a handler thread.
- *
- * Hence, the only field that must be thread-safe is mEnabled, which is called at the
- * beginning of every method.
- */
-
- private static final String TAG = ContentCaptureSession.class.getSimpleName();
+public abstract class ContentCaptureSession implements AutoCloseable {
/**
* Used on {@link #notifyViewTextChanged(AutofillId, CharSequence, int)} to indicate that the
+ *
* thext change was caused by user input (for example, through IME).
*/
public static final int FLAG_USER_INPUT = 0x1;
@@ -110,106 +83,41 @@
*/
public static final int STATE_DISABLED_DUPLICATED_ID = 4;
- /**
- * Handler message used to flush the buffer.
- */
- private static final int MSG_FLUSH = 1;
+ private static final int INITIAL_CHILDREN_CAPACITY = 5;
- /**
- * Maximum number of events that are buffered before sent to the app.
- */
- // TODO(b/121044064): use settings
- private static final int MAX_BUFFER_SIZE = 100;
-
- /**
- * Frequency the buffer is flushed if stale.
- */
- // TODO(b/121044064): use settings
- private static final int FLUSHING_FREQUENCY_MS = 5_000;
-
-
- /**
- * Name of the {@link IResultReceiver} extra used to pass the binder interface to the service.
- * @hide
- */
- public static final String EXTRA_BINDER = "binder";
+ /** @hide */
+ protected final String mTag = getClass().getSimpleName();
private final CloseGuard mCloseGuard = CloseGuard.get();
- @NonNull
- private final AtomicBoolean mDisabled;
-
- @NonNull
- private final Context mContext;
-
- @NonNull
- private final Handler mHandler;
-
- /**
- * Interface to the system_server binder object - it's only used to start the session (and
- * notify when the session is finished).
- */
+ /** @hide */
@Nullable
- private final IContentCaptureManager mSystemServerInterface;
-
- /**
- * Direct interface to the service binder object - it's used to send the events, including the
- * last ones (when the session is finished)
- */
- @Nullable
- private IContentCaptureDirectManager mDirectServiceInterface;
- @Nullable
- private DeathRecipient mDirectServiceVulture;
-
- @Nullable
- private final String mId = UUID.randomUUID().toString();
+ protected final String mId = UUID.randomUUID().toString();
private int mState = STATE_UNKNOWN;
- @Nullable
- private IBinder mApplicationToken;
-
- @Nullable
- private ComponentName mComponentName;
-
- /**
- * List of events held to be sent as a batch.
- */
- // TODO(b/111276913): once we support multiple sessions, we need to move the buffer of events
- // to its own class so it's shared by all sessions
- @Nullable
- private ArrayList<ContentCaptureEvent> mEvents;
-
- // Used just for debugging purposes (on dump)
- private long mNextFlush;
-
// Lazily created on demand.
private ContentCaptureSessionId mContentCaptureSessionId;
/**
- * {@link ContentCaptureContext} set by client, or {@code null} when it's the
- * {@link ContentCaptureManager#getMainContentCaptureSession() default session} for the
- * context.
+ * 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
- private final ContentCaptureContext mClientContext;
+ private ArrayList<ContentCaptureSession> mChildren;
/** @hide */
- protected ContentCaptureSession(@NonNull Context context, @NonNull Handler handler,
- @Nullable IContentCaptureManager systemServerInterface, @NonNull AtomicBoolean disabled,
- @Nullable ContentCaptureContext clientContext) {
- mContext = context;
- mHandler = handler;
- mSystemServerInterface = systemServerInterface;
- mDisabled = disabled;
- mClientContext = clientContext;
+ protected ContentCaptureSession() {
mCloseGuard.open("destroy");
}
/**
* Gets the id used to identify this session.
*/
- public ContentCaptureSessionId getContentCaptureSessionId() {
+ public final ContentCaptureSessionId getContentCaptureSessionId() {
if (mContentCaptureSessionId == null) {
mContentCaptureSessionId = new ContentCaptureSessionId(mId);
}
@@ -217,53 +125,74 @@
}
/**
- * Starts this session.
+ * Creates a new {@link ContentCaptureSession}.
*
- * @hide
+ * <p>See {@link View#setContentCaptureSession(ContentCaptureSession)} for more info.
*/
- void start(@NonNull IBinder applicationToken, @NonNull ComponentName activityComponent) {
- if (!isContentCaptureEnabled()) return;
-
- if (VERBOSE) {
- Log.v(TAG, "start(): token=" + applicationToken + ", comp="
- + ComponentName.flattenToShortString(activityComponent));
+ @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);
}
-
- mHandler.sendMessage(obtainMessage(ContentCaptureSession::handleStartSession, this,
- applicationToken, activityComponent));
+ 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.
- *
- * @hide
*/
- void flush() {
- mHandler.sendMessage(obtainMessage(ContentCaptureSession::handleForceFlush, this));
- }
+ abstract void flush();
/**
* Destroys this session, flushing out all pending notifications to the service.
*
* <p>Once destroyed, any new notification will be dropped.
*/
- public void destroy() {
+ 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(TAG, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId);
+ Log.v(mTag, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId);
}
- flush();
+ // 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);
+ }
+ }
+ }
- mHandler.sendMessage(obtainMessage(ContentCaptureSession::handleDestroySession, this));
- mCloseGuard.close();
+ try {
+ flush();
+ } finally {
+ onDestroy();
+ }
}
+ abstract void onDestroy();
+
/** @hide */
@Override
public void close() {
@@ -282,225 +211,6 @@
}
}
- private void handleStartSession(@NonNull IBinder token, @NonNull ComponentName componentName) {
- if (mState != STATE_UNKNOWN) {
- // TODO(b/111276913): revisit this scenario
- Log.w(TAG, "ignoring handleStartSession(" + token + ") while on state "
- + getStateAsString(mState));
- return;
- }
- mState = STATE_WAITING_FOR_SERVER;
- mApplicationToken = token;
- mComponentName = componentName;
-
- if (VERBOSE) {
- Log.v(TAG, "handleStartSession(): token=" + token + ", act="
- + getActivityDebugName() + ", id=" + mId);
- }
- final int flags = 0; // TODO(b/111276913): get proper flags
-
- try {
- mSystemServerInterface.startSession(mContext.getUserId(), mApplicationToken,
- componentName, mId, mClientContext, flags, new IResultReceiver.Stub() {
- @Override
- public void send(int resultCode, Bundle resultData) {
- IBinder binder = null;
- if (resultData != null) {
- binder = resultData.getBinder(EXTRA_BINDER);
- if (binder == null) {
- Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result");
- handleResetState();
- return;
- }
- }
- handleSessionStarted(resultCode, binder);
- }
- });
- } catch (RemoteException e) {
- Log.w(TAG, "Error starting session for " + componentName.flattenToShortString() + ": "
- + e);
- }
- }
-
- /**
- * Callback from {@code system_server} after call to
- * {@link IContentCaptureManager#startSession(int, IBinder, ComponentName, String,
- * ContentCaptureContext, int, IResultReceiver)}.
- *
- * @param resultCode session state
- * @param binder handle to {@link IContentCaptureDirectManager}
- */
- private void handleSessionStarted(int resultCode, @Nullable IBinder binder) {
- mState = resultCode;
- if (binder != null) {
- mDirectServiceInterface = IContentCaptureDirectManager.Stub.asInterface(binder);
- mDirectServiceVulture = () -> {
- Log.w(TAG, "Destroying session " + mId + " because service died");
- destroy();
- };
- try {
- binder.linkToDeath(mDirectServiceVulture, 0);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to link to death on " + binder + ": " + e);
- }
- }
- if (resultCode == STATE_DISABLED || resultCode == STATE_DISABLED_DUPLICATED_ID) {
- mDisabled.set(true);
- handleResetSession(/* resetState= */ false);
- } else {
- mDisabled.set(false);
- }
- if (VERBOSE) {
- Log.v(TAG, "handleSessionStarted() result: code=" + resultCode + ", id=" + mId
- + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get()
- + ", binder=" + binder);
- }
- }
-
- private void handleSendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
- if (mEvents == null) {
- if (VERBOSE) {
- Log.v(TAG, "Creating buffer for " + MAX_BUFFER_SIZE + " events");
- }
- mEvents = new ArrayList<>(MAX_BUFFER_SIZE);
- }
- mEvents.add(event);
-
- final int numberEvents = mEvents.size();
-
- // TODO(b/120784831): need to optimize it so we buffer changes until a number of X are
- // buffered (either total or per autofillid). For
- // example, if the user typed "a", "b", "c" and the threshold is 3, we should buffer
- // "a" and "b" then send "abc".
- final boolean bufferEvent = numberEvents < MAX_BUFFER_SIZE;
-
- if (bufferEvent && !forceFlush) {
- handleScheduleFlush(/* checkExisting= */ true);
- return;
- }
-
- if (mState != STATE_ACTIVE) {
- // Callback from startSession hasn't been called yet - typically happens on system
- // apps that are started before the system service
- // TODO(b/111276913): try to ignore session while system is not ready / boot
- // not complete instead. Similarly, the manager service should return right away
- // when the user does not have a service set
- if (VERBOSE) {
- Log.v(TAG, "Closing session for " + getActivityDebugName()
- + " after " + numberEvents + " delayed events and state "
- + getStateAsString(mState));
- }
- handleResetState();
- // TODO(b/111276913): blacklist activity / use special flag to indicate that
- // when it's launched again
- return;
- }
-
- handleForceFlush();
- }
-
- private void handleScheduleFlush(boolean checkExisting) {
- if (checkExisting && mHandler.hasMessages(MSG_FLUSH)) {
- // "Renew" the flush message by removing the previous one
- mHandler.removeMessages(MSG_FLUSH);
- }
- mNextFlush = SystemClock.elapsedRealtime() + FLUSHING_FREQUENCY_MS;
- if (VERBOSE) {
- Log.v(TAG, "Scheduled to flush in " + FLUSHING_FREQUENCY_MS + "ms: " + mNextFlush);
- }
- mHandler.sendMessageDelayed(
- obtainMessage(ContentCaptureSession::handleFlushIfNeeded, this).setWhat(MSG_FLUSH),
- FLUSHING_FREQUENCY_MS);
- }
-
- private void handleFlushIfNeeded() {
- if (mEvents.isEmpty()) {
- if (VERBOSE) Log.v(TAG, "Nothing to flush");
- return;
- }
- handleForceFlush();
- }
-
- private void handleForceFlush() {
- if (mEvents == null) return;
-
- if (mDirectServiceInterface == null) {
- Log.w(TAG, "handleForceFlush(): client not available yet");
- if (!mHandler.hasMessages(MSG_FLUSH)) {
- handleScheduleFlush(/* checkExisting= */ false);
- }
- return;
- }
-
- final int numberEvents = mEvents.size();
- try {
- if (DEBUG) {
- Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getActivityDebugName());
- }
- mHandler.removeMessages(MSG_FLUSH);
-
- final ParceledListSlice<ContentCaptureEvent> events = handleClearEvents();
- mDirectServiceInterface.sendEvents(mId, events);
- } catch (RemoteException e) {
- Log.w(TAG, "Error sending " + numberEvents + " for " + getActivityDebugName()
- + ": " + e);
- }
- }
-
- /**
- * Resets the buffer and return a {@link ParceledListSlice} with the previous events.
- */
- @NonNull
- private ParceledListSlice<ContentCaptureEvent> handleClearEvents() {
- // NOTE: we must save a reference to the current mEvents and then set it to to null,
- // otherwise clearing it would clear it in the receiving side if the service is also local.
- final List<ContentCaptureEvent> events = mEvents == null
- ? Collections.emptyList()
- : mEvents;
- mEvents = null;
- return new ParceledListSlice<>(events);
- }
-
- private void handleDestroySession() {
- //TODO(b/111276913): right now both the ContentEvents and lifecycle sessions are sent
- // to system_server, so it's ok to call both in sequence here. But once we split
- // them so the events are sent directly to the service, we need to make sure they're
- // sent in order.
- if (DEBUG) {
- Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
- + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
- + getActivityDebugName());
- }
-
- try {
- mSystemServerInterface.finishSession(mContext.getUserId(), mId);
- } catch (RemoteException e) {
- Log.e(TAG, "Error destroying system-service session " + mId + " for "
- + getActivityDebugName() + ": " + e);
- }
- }
-
- private void handleResetState() {
- handleResetSession(/* resetState= */ true);
- }
-
- // TODO(b/111276913): once we support multiple sessions, we might need to move some of these
- // clearings out.
- private void handleResetSession(boolean resetState) {
- if (resetState) {
- mState = STATE_UNKNOWN;
- }
- mContentCaptureSessionId = null;
- mApplicationToken = null;
- mComponentName = null;
- mEvents = null;
- if (mDirectServiceInterface != null) {
- mDirectServiceInterface.asBinder().unlinkToDeath(mDirectServiceVulture, 0);
- }
- mDirectServiceInterface = null;
- mHandler.removeMessages(MSG_FLUSH);
- }
-
/**
* Notifies the Content Capture Service that a node has been added to the view structure.
*
@@ -510,7 +220,7 @@
*
* @param node node that has been added.
*/
- public void notifyViewAppeared(@NonNull ViewStructure node) {
+ public final void notifyViewAppeared(@NonNull ViewStructure node) {
Preconditions.checkNotNull(node);
if (!isContentCaptureEnabled()) return;
@@ -518,12 +228,11 @@
throw new IllegalArgumentException("Invalid node class: " + node.getClass());
}
- mHandler.sendMessage(obtainMessage(ContentCaptureSession::handleSendEvent, this,
- new ContentCaptureEvent(TYPE_VIEW_APPEARED)
- .setViewNode(((ViewNode.ViewStructureImpl) node).mNode),
- /* forceFlush= */ false));
+ internalNotifyViewAppeared((ViewStructureImpl) node);
}
+ abstract void internalNotifyViewAppeared(@NonNull ViewNode.ViewStructureImpl node);
+
/**
* Notifies the Content Capture Service that a node has been removed from the view structure.
*
@@ -532,15 +241,15 @@
*
* @param id id of the node that has been removed.
*/
- public void notifyViewDisappeared(@NonNull AutofillId id) {
+ public final void notifyViewDisappeared(@NonNull AutofillId id) {
Preconditions.checkNotNull(id);
if (!isContentCaptureEnabled()) return;
- mHandler.sendMessage(obtainMessage(ContentCaptureSession::handleSendEvent, this,
- new ContentCaptureEvent(TYPE_VIEW_DISAPPEARED).setAutofillId(id),
- /* forceFlush= */ false));
+ internalNotifyViewDisappeared(id);
}
+ abstract void internalNotifyViewDisappeared(@NonNull AutofillId id);
+
/**
* Notifies the Intelligence Service that the value of a text node has been changed.
*
@@ -549,24 +258,25 @@
* @param flags either {@code 0} or {@link #FLAG_USER_INPUT} when the value was explicitly
* changed by the user (for example, through the keyboard).
*/
- public void notifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
+ public final void notifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
int flags) {
Preconditions.checkNotNull(id);
if (!isContentCaptureEnabled()) return;
- mHandler.sendMessage(obtainMessage(ContentCaptureSession::handleSendEvent, this,
- new ContentCaptureEvent(TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id)
- .setText(text), /* forceFlush= */ false));
+ internalNotifyViewTextChanged(id, text, flags);
}
+ abstract void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
+ int flags);
+
/**
* Creates a {@link ViewStructure} for a "standard" view.
*
* @hide
*/
@NonNull
- public ViewStructure newViewStructure(@NonNull View view) {
+ public final ViewStructure newViewStructure(@NonNull View view) {
return new ViewNode.ViewStructureImpl(view);
}
@@ -583,69 +293,25 @@
* @hide
*/
@NonNull
- public ViewStructure newVirtualViewStructure(@NonNull AutofillId parentId, int virtualId) {
+ public final ViewStructure newVirtualViewStructure(@NonNull AutofillId parentId,
+ int virtualId) {
return new ViewNode.ViewStructureImpl(parentId, virtualId);
}
- private boolean isContentCaptureEnabled() {
- return mSystemServerInterface != null && !mDisabled.get();
- }
+ abstract boolean isContentCaptureEnabled();
+ @CallSuper
void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
- pw.print(prefix); pw.print("id: "); pw.println(mId);
- pw.print(prefix); pw.print("mContext: "); pw.println(mContext);
- pw.print(prefix); pw.print("user: "); pw.println(mContext.getUserId());
- if (mSystemServerInterface != null) {
- pw.print(prefix); pw.print("mSystemServerInterface: ");
- pw.println(mSystemServerInterface);
- }
- if (mDirectServiceInterface != null) {
- pw.print(prefix); pw.print("mDirectServiceInterface: ");
- pw.println(mDirectServiceInterface);
- }
- if (mClientContext != null) {
- // NOTE: we don't dump clientContent because it could have PII
- 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) {
- pw.print(prefix); pw.print("public id: "); pw.println(mContentCaptureSessionId);
- }
- pw.print(prefix); pw.print("state: "); pw.print(mState); pw.print(" (");
- pw.print(getStateAsString(mState)); pw.println(")");
- if (mApplicationToken != null) {
- pw.print(prefix); pw.print("app token: "); pw.println(mApplicationToken);
- }
- if (mComponentName != null) {
- pw.print(prefix); pw.print("component name: ");
- pw.println(mComponentName.flattenToShortString());
- }
- if (mEvents != null && !mEvents.isEmpty()) {
- final int numberEvents = mEvents.size();
- pw.print(prefix); pw.print("buffered events: "); pw.print(numberEvents);
- pw.print('/'); pw.println(MAX_BUFFER_SIZE);
- if (VERBOSE && numberEvents > 0) {
- final String prefix3 = prefix + " ";
- for (int i = 0; i < numberEvents; i++) {
- final ContentCaptureEvent event = mEvents.get(i);
- pw.print(prefix3); pw.print(i); pw.print(": "); event.dump(pw);
- pw.println();
- }
+ 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);
}
- pw.print(prefix); pw.print("flush frequency: "); pw.println(FLUSHING_FREQUENCY_MS);
- pw.print(prefix); pw.print("next flush: ");
- TimeUtils.formatDuration(mNextFlush - SystemClock.elapsedRealtime(), pw); pw.println();
}
- }
- /**
- * Gets a string that can be used to identify the activity on logging statements.
- */
- private String getActivityDebugName() {
- return mComponentName == null ? mContext.getPackageName()
- : mComponentName.flattenToShortString();
}
@Override
@@ -653,8 +319,11 @@
return mId;
}
+ /**
+ * @hide
+ */
@NonNull
- private static String getStateAsString(int state) {
+ protected static String getStateAsString(int state) {
switch (state) {
case STATE_UNKNOWN:
return "UNKNOWN";
diff --git a/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl b/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl
index 145fc16..8d8117b 100644
--- a/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl
+++ b/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl
@@ -26,5 +26,5 @@
* @hide
*/
oneway interface IContentCaptureDirectManager {
- void sendEvents(in String sessionId, in ParceledListSlice events);
+ void sendEvents(in ParceledListSlice events);
}
diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
index cbd3701..01776f8 100644
--- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl
+++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
@@ -33,7 +33,6 @@
*/
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/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
new file mode 100644
index 0000000..ea6f2fe
--- /dev/null
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -0,0 +1,509 @@
+/*
+ * 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 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;
+import static android.view.contentcapture.ContentCaptureManager.DEBUG;
+import static android.view.contentcapture.ContentCaptureManager.VERBOSE;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.TimeUtils;
+import android.view.autofill.AutofillId;
+import android.view.contentcapture.ViewNode.ViewStructureImpl;
+
+import com.android.internal.os.IResultReceiver;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Main session associated with a context.
+ *
+ * <p>This session is created when the activity starts and finished when it stops; clients can use
+ * it to create children activities.
+ *
+ * <p><b>NOTE: all methods in this class should return right away, or do the real work in a handler
+ * thread. Hence, the only field that must be thread-safe is {@code mEnabled}, which is called at
+ * the beginning of every method.
+ *
+ * @hide
+ */
+public final class MainContentCaptureSession extends ContentCaptureSession {
+
+ /**
+ * Handler message used to flush the buffer.
+ */
+ private static final int MSG_FLUSH = 1;
+
+ /**
+ * Maximum number of events that are buffered before sent to the app.
+ */
+ // TODO(b/121044064): use settings
+ private static final int MAX_BUFFER_SIZE = 100;
+
+ /**
+ * Frequency the buffer is flushed if stale.
+ */
+ // TODO(b/121044064): use settings
+ private static final int FLUSHING_FREQUENCY_MS = 5_000;
+
+ /**
+ * Name of the {@link IResultReceiver} extra used to pass the binder interface to the service.
+ * @hide
+ */
+ public static final String EXTRA_BINDER = "binder";
+
+ @NonNull
+ private final AtomicBoolean mDisabled;
+
+ @NonNull
+ private final Context mContext;
+
+ @NonNull
+ private final Handler mHandler;
+
+ /**
+ * Interface to the system_server binder object - it's only used to start the session (and
+ * notify when the session is finished).
+ */
+ @Nullable
+ private final IContentCaptureManager mSystemServerInterface;
+
+ /**
+ * Direct interface to the service binder object - it's used to send the events, including the
+ * last ones (when the session is finished)
+ */
+ @Nullable
+ private IContentCaptureDirectManager mDirectServiceInterface;
+ @Nullable
+ private DeathRecipient mDirectServiceVulture;
+
+ private int mState = STATE_UNKNOWN;
+
+ @Nullable
+ private IBinder mApplicationToken;
+
+ @Nullable
+ private ComponentName mComponentName;
+
+ /**
+ * List of events held to be sent as a batch.
+ */
+ @Nullable
+ private ArrayList<ContentCaptureEvent> mEvents;
+
+ // Used just for debugging purposes (on dump)
+ private long mNextFlush;
+
+ // Lazily created on demand.
+ private ContentCaptureSessionId mContentCaptureSessionId;
+
+ /** @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.
+ *
+ * @hide
+ */
+ void start(@NonNull IBinder applicationToken, @NonNull ComponentName activityComponent) {
+ if (!isContentCaptureEnabled()) return;
+
+ if (VERBOSE) {
+ Log.v(mTag, "start(): token=" + applicationToken + ", comp="
+ + ComponentName.flattenToShortString(activityComponent));
+ }
+
+ mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleStartSession, this,
+ applicationToken, activityComponent));
+ }
+
+ @Override
+ void flush() {
+ mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleForceFlush, this));
+ }
+
+ @Override
+ void onDestroy() {
+ mHandler.sendMessage(
+ obtainMessage(MainContentCaptureSession::handleDestroySession, this));
+ }
+
+ private void handleStartSession(@NonNull IBinder token, @NonNull ComponentName componentName) {
+ if (mState != STATE_UNKNOWN) {
+ // TODO(b/111276913): revisit this scenario
+ Log.w(mTag, "ignoring handleStartSession(" + token + ") while on state "
+ + getStateAsString(mState));
+ return;
+ }
+ mState = STATE_WAITING_FOR_SERVER;
+ mApplicationToken = token;
+ mComponentName = componentName;
+
+ if (VERBOSE) {
+ Log.v(mTag, "handleStartSession(): token=" + token + ", act="
+ + getActivityDebugName() + ", id=" + mId);
+ }
+ final int flags = 0; // TODO(b/111276913): get proper flags
+
+ try {
+ mSystemServerInterface.startSession(mContext.getUserId(), mApplicationToken,
+ componentName, mId, flags, new IResultReceiver.Stub() {
+ @Override
+ public void send(int resultCode, Bundle resultData) {
+ IBinder binder = null;
+ if (resultData != null) {
+ binder = resultData.getBinder(EXTRA_BINDER);
+ if (binder == null) {
+ Log.wtf(mTag, "No " + EXTRA_BINDER + " extra result");
+ handleResetState();
+ return;
+ }
+ }
+ handleSessionStarted(resultCode, binder);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.w(mTag, "Error starting session for " + componentName.flattenToShortString() + ": "
+ + e);
+ }
+ }
+
+ /**
+ * Callback from {@code system_server} after call to
+ * {@link IContentCaptureManager#startSession(int, IBinder, ComponentName, String,
+ * ContentCaptureContext, int, IResultReceiver)}.
+ *
+ * @param resultCode session state
+ * @param binder handle to {@code IContentCaptureDirectManager}
+ */
+ private void handleSessionStarted(int resultCode, @Nullable IBinder binder) {
+ mState = resultCode;
+ if (binder != null) {
+ mDirectServiceInterface = IContentCaptureDirectManager.Stub.asInterface(binder);
+ mDirectServiceVulture = () -> {
+ Log.w(mTag, "Destroying session " + mId + " because service died");
+ destroy();
+ };
+ try {
+ binder.linkToDeath(mDirectServiceVulture, 0);
+ } catch (RemoteException e) {
+ Log.w(mTag, "Failed to link to death on " + binder + ": " + e);
+ }
+ }
+ if (resultCode == STATE_DISABLED || resultCode == STATE_DISABLED_DUPLICATED_ID) {
+ mDisabled.set(true);
+ handleResetSession(/* resetState= */ false);
+ } else {
+ mDisabled.set(false);
+ }
+ if (VERBOSE) {
+ Log.v(mTag, "handleSessionStarted() result: code=" + resultCode + ", id=" + mId
+ + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get()
+ + ", binder=" + binder);
+ }
+ }
+
+ private void handleSendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
+ if (mEvents == null) {
+ if (VERBOSE) {
+ Log.v(mTag, "Creating buffer for " + MAX_BUFFER_SIZE + " events");
+ }
+ mEvents = new ArrayList<>(MAX_BUFFER_SIZE);
+ }
+ mEvents.add(event);
+
+ final int numberEvents = mEvents.size();
+
+ // TODO(b/120784831): need to optimize it so we buffer changes until a number of X are
+ // buffered (either total or per autofillid). For
+ // example, if the user typed "a", "b", "c" and the threshold is 3, we should buffer
+ // "a" and "b" then send "abc".
+ final boolean bufferEvent = numberEvents < MAX_BUFFER_SIZE;
+
+ if (bufferEvent && !forceFlush) {
+ handleScheduleFlush(/* checkExisting= */ true);
+ return;
+ }
+
+ if (mState != STATE_ACTIVE) {
+ // Callback from startSession hasn't been called yet - typically happens on system
+ // apps that are started before the system service
+ // TODO(b/111276913): try to ignore session while system is not ready / boot
+ // not complete instead. Similarly, the manager service should return right away
+ // when the user does not have a service set
+ if (VERBOSE) {
+ Log.v(mTag, "Closing session for " + getActivityDebugName()
+ + " after " + numberEvents + " delayed events and state "
+ + getStateAsString(mState));
+ }
+ handleResetState();
+ // TODO(b/111276913): blacklist activity / use special flag to indicate that
+ // when it's launched again
+ return;
+ }
+
+ handleForceFlush();
+ }
+
+ private void handleScheduleFlush(boolean checkExisting) {
+ if (checkExisting && mHandler.hasMessages(MSG_FLUSH)) {
+ // "Renew" the flush message by removing the previous one
+ mHandler.removeMessages(MSG_FLUSH);
+ }
+ mNextFlush = SystemClock.elapsedRealtime() + FLUSHING_FREQUENCY_MS;
+ if (VERBOSE) {
+ Log.v(mTag, "Scheduled to flush in " + FLUSHING_FREQUENCY_MS + "ms: " + mNextFlush);
+ }
+ mHandler.sendMessageDelayed(
+ obtainMessage(MainContentCaptureSession::handleFlushIfNeeded, this)
+ .setWhat(MSG_FLUSH), FLUSHING_FREQUENCY_MS);
+ }
+
+ private void handleFlushIfNeeded() {
+ if (mEvents.isEmpty()) {
+ if (VERBOSE) Log.v(mTag, "Nothing to flush");
+ return;
+ }
+ handleForceFlush();
+ }
+
+ private void handleForceFlush() {
+ if (mEvents == null) return;
+
+ if (mDirectServiceInterface == null) {
+ if (DEBUG) Log.d(mTag, "handleForceFlush(): hold your horses, client not ready yet!");
+ if (!mHandler.hasMessages(MSG_FLUSH)) {
+ handleScheduleFlush(/* checkExisting= */ false);
+ }
+ return;
+ }
+
+ final int numberEvents = mEvents.size();
+ try {
+ if (DEBUG) {
+ Log.d(mTag, "Flushing " + numberEvents + " event(s) for " + getActivityDebugName());
+ }
+ mHandler.removeMessages(MSG_FLUSH);
+
+ final ParceledListSlice<ContentCaptureEvent> events = handleClearEvents();
+ mDirectServiceInterface.sendEvents(events);
+ } catch (RemoteException e) {
+ Log.w(mTag, "Error sending " + numberEvents + " for " + getActivityDebugName()
+ + ": " + e);
+ }
+ }
+
+ /**
+ * Resets the buffer and return a {@link ParceledListSlice} with the previous events.
+ */
+ @NonNull
+ private ParceledListSlice<ContentCaptureEvent> handleClearEvents() {
+ // NOTE: we must save a reference to the current mEvents and then set it to to null,
+ // otherwise clearing it would clear it in the receiving side if the service is also local.
+ final List<ContentCaptureEvent> events = mEvents == null
+ ? Collections.emptyList()
+ : mEvents;
+ mEvents = null;
+ return new ParceledListSlice<>(events);
+ }
+
+ private void handleDestroySession() {
+ if (DEBUG) {
+ Log.d(mTag, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
+ + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
+ + getActivityDebugName());
+ }
+
+ try {
+ mSystemServerInterface.finishSession(mContext.getUserId(), mId);
+ } catch (RemoteException e) {
+ Log.e(mTag, "Error destroying system-service session " + mId + " for "
+ + getActivityDebugName() + ": " + e);
+ }
+ }
+
+ private void handleResetState() {
+ handleResetSession(/* resetState= */ true);
+ }
+
+ // 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;
+ mEvents = null;
+ if (mDirectServiceInterface != null) {
+ mDirectServiceInterface.asBinder().unlinkToDeath(mDirectServiceVulture, 0);
+ }
+ mDirectServiceInterface = null;
+ mHandler.removeMessages(MSG_FLUSH);
+ }
+
+ @Override
+ void internalNotifyViewAppeared(@NonNull ViewStructureImpl node) {
+ notifyViewAppeared(mId, node);
+ }
+
+ @Override
+ void internalNotifyViewDisappeared(@NonNull AutofillId id) {
+ notifyViewDisappeared(mId, id);
+ }
+
+ @Override
+ void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
+ int flags) {
+ notifyViewTextChanged(mId, id, text, flags);
+ }
+
+ @Override
+ boolean isContentCaptureEnabled() {
+ 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);
+ pw.print(prefix); pw.print("mContext: "); pw.println(mContext);
+ pw.print(prefix); pw.print("user: "); pw.println(mContext.getUserId());
+ if (mSystemServerInterface != null) {
+ pw.print(prefix); pw.print("mSystemServerInterface: ");
+ pw.println(mSystemServerInterface);
+ }
+ if (mDirectServiceInterface != null) {
+ pw.print(prefix); pw.print("mDirectServiceInterface: ");
+ pw.println(mDirectServiceInterface);
+ }
+ pw.print(prefix); pw.print("mDisabled: "); pw.println(mDisabled.get());
+ pw.print(prefix); pw.print("isEnabled(): "); pw.println(isContentCaptureEnabled());
+ if (mContentCaptureSessionId != null) {
+ pw.print(prefix); pw.print("public id: "); pw.println(mContentCaptureSessionId);
+ }
+ pw.print(prefix); pw.print("state: "); pw.print(mState); pw.print(" (");
+ pw.print(getStateAsString(mState)); pw.println(")");
+ if (mApplicationToken != null) {
+ pw.print(prefix); pw.print("app token: "); pw.println(mApplicationToken);
+ }
+ if (mComponentName != null) {
+ pw.print(prefix); pw.print("component name: ");
+ pw.println(mComponentName.flattenToShortString());
+ }
+ if (mEvents != null && !mEvents.isEmpty()) {
+ final int numberEvents = mEvents.size();
+ pw.print(prefix); pw.print("buffered events: "); pw.print(numberEvents);
+ pw.print('/'); pw.println(MAX_BUFFER_SIZE);
+ if (VERBOSE && numberEvents > 0) {
+ final String prefix3 = prefix + " ";
+ for (int i = 0; i < numberEvents; i++) {
+ final ContentCaptureEvent event = mEvents.get(i);
+ pw.print(prefix3); pw.print(i); pw.print(": "); event.dump(pw);
+ pw.println();
+ }
+ }
+ pw.print(prefix); pw.print("flush frequency: "); pw.println(FLUSHING_FREQUENCY_MS);
+ pw.print(prefix); pw.print("next flush: ");
+ TimeUtils.formatDuration(mNextFlush - SystemClock.elapsedRealtime(), pw); pw.println();
+ }
+ super.dump(prefix, pw);
+ }
+
+ /**
+ * Gets a string that can be used to identify the activity on logging statements.
+ */
+ private String getActivityDebugName() {
+ return mComponentName == null ? mContext.getPackageName()
+ : mComponentName.flattenToShortString();
+ }
+}
diff --git a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
index b41096c..77cb4cd 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.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 @@
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 3bb9ee8..f2fea02 100644
--- a/core/java/android/view/textclassifier/TextClassifierEvent.java
+++ b/core/java/android/view/textclassifier/TextClassifierEvent.java
@@ -27,6 +27,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
/**
* A text classifier event.
@@ -498,4 +499,25 @@
}
// 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 9b0f9c6..fcd06c3 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -80,6 +80,8 @@
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 @@
@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 @@
}
@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 @@
.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 @@
try {
if (pfd != null) {
mActionsImpl = new ActionsSuggestionsModel(pfd.getFd());
+ mActionModelInUse = bestModel;
}
} finally {
maybeCloseAndLogError(pfd);
diff --git a/core/java/android/webkit/WebSyncManager.java b/core/java/android/webkit/WebSyncManager.java
index 3fa1b01..e44d6eb 100644
--- a/core/java/android/webkit/WebSyncManager.java
+++ b/core/java/android/webkit/WebSyncManager.java
@@ -26,6 +26,7 @@
abstract class WebSyncManager implements Runnable {
protected static final java.lang.String LOGTAG = "websync";
protected android.webkit.WebViewDatabase mDataBase;
+ @UnsupportedAppUsage
protected android.os.Handler mHandler;
protected WebSyncManager(Context context, String name) {
diff --git a/core/java/com/android/internal/app/ColorDisplayController.java b/core/java/com/android/internal/app/ColorDisplayController.java
index 213bb75..c093fe5 100644
--- a/core/java/com/android/internal/app/ColorDisplayController.java
+++ b/core/java/com/android/internal/app/ColorDisplayController.java
@@ -37,12 +37,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.time.DateTimeException;
-import java.time.Instant;
import java.time.LocalDateTime;
import java.time.LocalTime;
-import java.time.ZoneId;
-import java.time.format.DateTimeParseException;
/**
* Controller for managing night display and color mode settings.
@@ -152,28 +148,6 @@
}
/**
- * Returns the time when Night display's activation state last changed, or {@code null} if it
- * has never been changed.
- */
- public LocalDateTime getLastActivatedTime() {
- final ContentResolver cr = mContext.getContentResolver();
- final String lastActivatedTime = Secure.getStringForUser(
- cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, mUserId);
- if (lastActivatedTime != null) {
- try {
- return LocalDateTime.parse(lastActivatedTime);
- } catch (DateTimeParseException ignored) {}
- // Uses the old epoch time.
- try {
- return LocalDateTime.ofInstant(
- Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
- ZoneId.systemDefault());
- } catch (DateTimeException|NumberFormatException ignored) {}
- }
- return null;
- }
-
- /**
* Returns the current auto mode value controlling when Night display will be automatically
* activated. One of {@link #AUTO_MODE_DISABLED}, {@link #AUTO_MODE_CUSTOM}, or
* {@link #AUTO_MODE_TWILIGHT}.
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index e571656..e59bee4 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -65,4 +65,6 @@
void startWatchingNoted(in int[] ops, IAppOpsNotedCallback callback);
void stopWatchingNoted(IAppOpsNotedCallback callback);
+
+ int checkOperationRaw(int code, int uid, String packageName);
}
diff --git a/core/java/com/android/internal/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java
index 5ab2582..3391840 100644
--- a/core/java/com/android/internal/infra/AbstractRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractRemoteService.java
@@ -19,6 +19,7 @@
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -164,6 +165,15 @@
*/
protected abstract long getRemoteRequestMillis();
+ /**
+ * Gets the currently registered service interface or {@code null} if the service is not
+ * connected.
+ */
+ @Nullable
+ public final I getServiceInterface() {
+ return mService;
+ }
+
private void handleDestroy() {
if (checkIfDestroyed()) return;
handleOnDestroy();
diff --git a/core/java/com/android/internal/widget/NumericTextView.java b/core/java/com/android/internal/widget/NumericTextView.java
index 27c5834..d215670 100644
--- a/core/java/com/android/internal/widget/NumericTextView.java
+++ b/core/java/com/android/internal/widget/NumericTextView.java
@@ -16,6 +16,7 @@
package com.android.internal.widget;
+import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
@@ -53,6 +54,7 @@
private OnValueChangedListener mListener;
+ @UnsupportedAppUsage
public NumericTextView(Context context, AttributeSet attrs) {
super(context, attrs);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 21fa75e..dc6a73a 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -271,7 +271,7 @@
"libhardware",
"libhardware_legacy",
"libselinux",
- "libicuuc",
+ "libandroidicu",
"libmedia",
"libmediametrics",
"libmeminfo",
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 12ca78a..eb7338a 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -45,7 +45,7 @@
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 9f6462e..b36b4e6 100644
--- a/core/jni/android/graphics/FontUtils.h
+++ b/core/jni/android/graphics/FontUtils.h
@@ -29,7 +29,7 @@
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 dd99b37..f84a4bd 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_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 84f53468..fc9ea76 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -102,6 +102,10 @@
return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha, flags));
}
+static jint saveUnclippedLayer(jlong canvasHandle, jint l, jint t, jint r, jint b) {
+ return reinterpret_cast<jint>(get_canvas(canvasHandle)->saveUnclippedLayer(l, t, r, b));
+}
+
static bool restore(jlong canvasHandle) {
Canvas* canvas = get_canvas(canvasHandle);
if (canvas->getSaveCount() <= 1) {
@@ -651,6 +655,7 @@
{"nSave","(JI)I", (void*) CanvasJNI::save},
{"nSaveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
{"nSaveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
+ {"nSaveUnclippedLayer","(JIIII)I", (void*) CanvasJNI::saveUnclippedLayer},
{"nGetSaveCount","(J)I", (void*) CanvasJNI::getSaveCount},
{"nRestore","(J)Z", (void*) CanvasJNI::restore},
{"nRestoreToCount","(JI)V", (void*) CanvasJNI::restoreToCount},
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 283eb03..29d8f30 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -32,6 +32,7 @@
#include <nativehelper/ScopedLocalRef.h>
#include <system/audio.h>
#include <system/audio_policy.h>
+#include "android_media_AudioEffectDescriptor.h"
#include "android_media_AudioFormat.h"
#include "android_media_AudioErrors.h"
#include "android_media_MicrophoneInfo.h"
@@ -427,9 +428,14 @@
}
static void
-android_media_AudioSystem_recording_callback(int event, const record_client_info_t *clientInfo,
- const audio_config_base_t *clientConfig, const audio_config_base_t *deviceConfig,
- audio_patch_handle_t patchHandle)
+android_media_AudioSystem_recording_callback(int event,
+ const record_client_info_t *clientInfo,
+ const audio_config_base_t *clientConfig,
+ std::vector<effect_descriptor_t> clientEffects,
+ const audio_config_base_t *deviceConfig,
+ std::vector<effect_descriptor_t> effects __unused,
+ audio_patch_handle_t patchHandle,
+ audio_source_t source)
{
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (env == NULL) {
@@ -460,14 +466,24 @@
recParamData[6] = (jint) patchHandle;
env->SetIntArrayRegion(recParamArray, 0, REC_PARAM_SIZE, recParamData);
+ jobjectArray jClientEffects;
+ convertAudioEffectDescriptorVectorFromNative(env, &jClientEffects, clientEffects);
+
+ jobjectArray jEffects;
+ convertAudioEffectDescriptorVectorFromNative(env, &jEffects, effects);
+
// callback into java
jclass clazz = env->FindClass(kClassPathName);
- env->CallStaticVoidMethod(clazz,
- gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative,
- event, (jint) clientInfo->uid, clientInfo->session, clientInfo->source, recParamArray);
- env->DeleteLocalRef(clazz);
+ env->CallStaticVoidMethod(clazz,
+ gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative,
+ event, (jint) clientInfo->uid, clientInfo->session,
+ clientInfo->source, clientInfo->port_id, clientInfo->silenced,
+ recParamArray, jClientEffects, jEffects, source);
+ env->DeleteLocalRef(clazz);
env->DeleteLocalRef(recParamArray);
+ env->DeleteLocalRef(jClientEffects);
+ env->DeleteLocalRef(jEffects);
}
static jint
@@ -2260,7 +2276,7 @@
"dynamicPolicyCallbackFromNative", "(ILjava/lang/String;I)V");
gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative =
GetStaticMethodIDOrDie(env, env->FindClass(kClassPathName),
- "recordingCallbackFromNative", "(IIII[I)V");
+ "recordingCallbackFromNative", "(IIIIIZ[I[Landroid/media/audiofx/AudioEffect$Descriptor;[Landroid/media/audiofx/AudioEffect$Descriptor;I)V");
jclass audioMixClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMix");
gAudioMixClass = MakeGlobalRefOrDie(env, audioMixClass);
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 516093e..1065738 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 108fa00..a1a0351 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 f6e5c7a..c33405d 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 @@
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 @@
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 @@
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 @@
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 4ab8db4..182a621 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_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 @@
};
-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 @@
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 @@
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 @@
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 @@
NATIVE_METHOD(StrictJarFile, nativeClose, "(J)V"),
};
+} // namespace
+
+namespace android {
+
int register_android_util_jar_StrictJarFile(JNIEnv* env) {
- jniRegisterNativeMethods(env, "android/util/jar/StrictJarFile", gMethods, NELEM(gMethods));
-
- 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>");
-
- return 0;
+ 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 33b2689..bd87dcc 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -152,7 +152,7 @@
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 a3570d7..09022a2 100644
--- a/core/jni/fd_utils.h
+++ b/core/jni/fd_utils.h
@@ -86,7 +86,7 @@
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 8b6f33f..c6343a8 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 4b97fe75..7c95d1e 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -42,7 +42,7 @@
<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/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 0ed8212..56265cc 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1442,7 +1442,7 @@
<style name="Theme.DeviceDefault.Settings" parent="Theme.Material.Settings">
<!-- action bar -->
<item name="actionBarStyle">@style/Widget.DeviceDefault.Light.ActionBar.Solid</item>
- <item name="actionBarTheme">@style/ThemeOverlay.DeviceDefault.ActionBar.Accent</item>
+ <item name="actionBarTheme">@style/ThemeOverlay.DeviceDefault.ActionBar</item>
<item name="popupTheme">@style/ThemeOverlay.DeviceDefault.Popup.Light</item>
<!-- Color palette -->
@@ -1678,11 +1678,8 @@
<style name="ThemeOverlay.DeviceDefault" />
- <!-- @hide Theme overlay that inherits from material actionbar, and use accent color for
- primary text -->
- <style name="ThemeOverlay.DeviceDefault.ActionBar.Accent" parent="ThemeOverlay.Material.ActionBar">
- <item name="textColorPrimary">@color/btn_colored_borderless_text_material</item>
- </style>
+ <!-- @hide Theme overlay that inherits from material actionbar -->
+ <style name="ThemeOverlay.DeviceDefault.ActionBar" parent="ThemeOverlay.Material.ActionBar" />
<!-- @hide Theme overlay for a light popup in action bar -->
<style name="ThemeOverlay.DeviceDefault.Popup.Light" parent="@style/ThemeOverlay.Material.Light" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 6d1aae1..f8bd4e3 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -384,6 +384,7 @@
Settings.Global.PRIV_APP_OOB_LIST,
Settings.Global.PRIVATE_DNS_DEFAULT_MODE,
Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED,
+ Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED,
Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED,
Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED,
Settings.Global.PROVISIONING_APN_ALARM_DELAY_IN_MS,
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 0000000..d520f15
--- /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 2ad6028..d3d274a 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -28,6 +28,7 @@
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 6bb9539..d41a718 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.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 @@
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 @@
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 @@
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 @@
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 c216425..58b57e5 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -33,6 +33,10 @@
<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/data/fonts/fonts.xml b/data/fonts/fonts.xml
index c84c035..f7541e0 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -365,7 +365,7 @@
<font weight="400" style="normal">NotoSansCarian-Regular.ttf</font>
</family>
<family lang="und-Cakm">
- <font weight="400" style="normal">NotoSansChakma-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansChakma-Regular.otf</font>
</family>
<family lang="und-Cher">
<font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 6798ab2..63a806e 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -556,7 +556,7 @@
* @hide
*/
public int saveUnclippedLayer(int left, int top, int right, int bottom) {
- return nSaveLayer(mNativeCanvasWrapper, left, top, right, bottom, 0, 0);
+ return nSaveUnclippedLayer(mNativeCanvasWrapper, left, top, right, bottom);
}
/**
@@ -1395,6 +1395,8 @@
private static native int nSaveLayerAlpha(long nativeCanvas, float l, float t, float r, float b,
int alpha, int layerFlags);
@CriticalNative
+ private static native int nSaveUnclippedLayer(long nativeCanvas, int l, int t, int r, int b);
+ @CriticalNative
private static native boolean nRestore(long canvasHandle);
@CriticalNative
private static native void nRestoreToCount(long canvasHandle, int saveCount);
diff --git a/graphics/java/android/graphics/Insets.java b/graphics/java/android/graphics/Insets.java
index d9da27c..8258b57 100644
--- a/graphics/java/android/graphics/Insets.java
+++ b/graphics/java/android/graphics/Insets.java
@@ -93,6 +93,41 @@
}
/**
+ * 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 6e6ed30..9361c7c 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -55,6 +55,7 @@
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 @@
}
/**
- * 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 @@
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/DisplayListOps.in b/libs/hwui/DisplayListOps.in
index 04cf611..bd1e6c5 100644
--- a/libs/hwui/DisplayListOps.in
+++ b/libs/hwui/DisplayListOps.in
@@ -18,6 +18,7 @@
X(Save)
X(Restore)
X(SaveLayer)
+X(SaveBehind)
X(Concat)
X(SetMatrix)
X(Translate)
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 0b847af..4de25f9 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -18,6 +18,7 @@
#include "VectorDrawable.h"
+#include "SkAndroidFrameworkUtils.h"
#include "SkCanvas.h"
#include "SkData.h"
#include "SkDrawShadowInfo.h"
@@ -116,6 +117,16 @@
clipMatrix.isIdentity() ? nullptr : &clipMatrix, flags});
}
};
+struct SaveBehind final : Op {
+ static const auto kType = Type::SaveBehind;
+ SaveBehind(const SkRect* subset) {
+ if (subset) { this->subset = *subset; }
+ }
+ SkRect subset = kUnset;
+ void draw(SkCanvas* c, const SkMatrix&) const {
+ SkAndroidFrameworkUtils::SaveBehind(c, &subset);
+ }
+};
struct Concat final : Op {
static const auto kType = Type::Concat;
@@ -579,6 +590,10 @@
this->push<SaveLayer>(0, bounds, paint, backdrop, clipMask, clipMatrix, flags);
}
+void DisplayListData::saveBehind(const SkRect* subset) {
+ this->push<SaveBehind>(0, subset);
+}
+
void DisplayListData::concat(const SkMatrix& matrix) {
this->push<Concat>(0, matrix);
}
@@ -848,6 +863,11 @@
fDL->restore();
}
+bool RecordingCanvas::onDoSaveBehind(const SkRect* subset) {
+ fDL->saveBehind(subset);
+ return false;
+}
+
void RecordingCanvas::didConcat(const SkMatrix& matrix) {
fDL->concat(matrix);
}
@@ -912,10 +932,6 @@
fDL->drawAnnotation(rect, key, val);
}
-void RecordingCanvas::onDrawTextRSXform(const void* text, size_t bytes, const SkRSXform xform[],
- const SkRect* cull, const SkPaint& paint) {
- fDL->drawTextRSXform(text, bytes, xform, cull, paint);
-}
void RecordingCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
const SkPaint& paint) {
fDL->drawTextBlob(blob, x, y, paint);
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index de8777b..ae3c4f05 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -75,6 +75,7 @@
void save();
void saveLayer(const SkRect*, const SkPaint*, const SkImageFilter*, const SkImage*,
const SkMatrix*, SkCanvas::SaveLayerFlags);
+ void saveBehind(const SkRect*);
void restore();
void concat(const SkMatrix&);
@@ -146,6 +147,7 @@
void willSave() override;
SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override;
void willRestore() override;
+ bool onDoSaveBehind(const SkRect*) override;
void onFlush() override;
@@ -171,8 +173,6 @@
void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override;
void onDrawAnnotation(const SkRect&, const char[], SkData*) override;
- void onDrawTextRSXform(const void*, size_t, const SkRSXform[], const SkRect*,
- const SkPaint&) override;
void onDrawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&) override;
void onDrawBitmap(const SkBitmap&, SkScalar, SkScalar, const SkPaint*) override;
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 6be7ef7..83b9e7f 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -24,6 +24,7 @@
#include "hwui/PaintFilter.h"
#include "pipeline/skia/AnimatedDrawables.h"
+#include <SkAndroidFrameworkUtils.h>
#include <SkAnimatedImage.h>
#include <SkCanvasStateUtils.h>
#include <SkColorFilter.h>
@@ -185,6 +186,11 @@
return this->saveLayer(left, top, right, bottom, nullptr, flags);
}
+int SkiaCanvas::saveUnclippedLayer(int left, int top, int right, int bottom) {
+ SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
+ return SkAndroidFrameworkUtils::SaveBehind(mCanvas, &bounds);
+}
+
class SkiaCanvas::Clip {
public:
Clip(const SkRect& rect, SkClipOp op, const SkMatrix& m)
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 24d9c08..4ab0a59 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -74,6 +74,7 @@
SaveFlags::Flags flags) override;
virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
SaveFlags::Flags flags) override;
+ virtual int saveUnclippedLayer(int left, int top, int right, int bottom) override;
virtual void getMatrix(SkMatrix* outMatrix) const override;
virtual void setMatrix(const SkMatrix& matrix) override;
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 71814c3..4c5365d 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -196,6 +196,7 @@
SaveFlags::Flags flags) = 0;
virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
SaveFlags::Flags flags) = 0;
+ virtual int saveUnclippedLayer(int, int, int, int) = 0;
// Matrix
virtual void getMatrix(SkMatrix* outMatrix) const = 0;
diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index 2b5d580..0eb526a 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -82,11 +82,6 @@
mOutput << mIdent << "drawDRRect" << std::endl;
}
- void onDrawTextRSXform(const void*, size_t, const SkRSXform[], const SkRect*,
- const SkPaint&) override {
- mOutput << mIdent << "drawTextRSXform" << std::endl;
- }
-
void onDrawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&) override {
mOutput << mIdent << "drawTextBlob" << std::endl;
}
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index f2906de..ff87313 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -30,7 +30,7 @@
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 daa4c18..dc8420f 100644
--- a/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
+++ b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
@@ -23,7 +23,7 @@
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 02874c7..53ffc44 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -25,7 +25,7 @@
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 74e48ce..5e892aa 100644
--- a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h
+++ b/libs/hwui/pipeline/skia/VectorDrawableAtlas.h
@@ -62,8 +62,8 @@
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 35fc91a..66f04f1 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -59,7 +59,7 @@
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 69ca23a..9eb942c 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -160,6 +160,7 @@
fPtr = ptr;
return *this;
}
+ // NOLINTNEXTLINE(google-explicit-constructor)
operator FNPTR_TYPE() const { return fPtr; }
private:
diff --git a/libs/hwui/tests/unit/FatalTestCanvas.h b/libs/hwui/tests/unit/FatalTestCanvas.h
index 146662b..1723c2e 100644
--- a/libs/hwui/tests/unit/FatalTestCanvas.h
+++ b/libs/hwui/tests/unit/FatalTestCanvas.h
@@ -30,10 +30,6 @@
void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) {
ADD_FAILURE() << "onDrawDRRect not expected in this test";
}
- void onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform[],
- const SkRect* cullRect, const SkPaint& paint) {
- ADD_FAILURE() << "onDrawTextRSXform not expected in this test";
- }
void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) {
ADD_FAILURE() << "onDrawTextBlob not expected in this test";
}
@@ -116,4 +112,4 @@
int mDrawCounter = 0; // counts how may draw calls of any kind were made to this canvas
};
-}
\ No newline at end of file
+}
diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include/android/os/IncidentReportArgs.h
index c56f689..ee1e33c 100644
--- a/libs/incident/include/android/os/IncidentReportArgs.h
+++ b/libs/incident/include/android/os/IncidentReportArgs.h
@@ -40,7 +40,7 @@
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 21f6306..1188b13b 100644
--- a/location/java/android/location/GnssMeasurementCallbackTransport.java
+++ b/location/java/android/location/GnssMeasurementCallbackTransport.java
@@ -19,6 +19,8 @@
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 @@
*/
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 @@
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 0000000..b05eb54
--- /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 0000000..b81bf90
--- /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 0000000..64b3752
--- /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 0000000..6c757f9
--- /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 05d49e5..bdc84da 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -21,6 +21,7 @@
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 @@
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 1cd3d86..040e4f9 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -22,6 +22,7 @@
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 @@
}
/**
- * 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/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index 9bd5994..b531325 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -17,6 +17,7 @@
package com.android.internal.location;
import java.io.UnsupportedEncodingException;
+import java.util.concurrent.TimeUnit;
import android.app.Notification;
import android.app.NotificationManager;
@@ -27,19 +28,17 @@
import android.content.IntentFilter;
import android.location.LocationManager;
import android.location.INetInitiatedListener;
+import android.os.SystemClock;
import android.telephony.TelephonyManager;
import android.telephony.PhoneNumberUtils;
import android.telephony.PhoneStateListener;
-import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.os.SystemProperties;
import android.util.Log;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.R;
import com.android.internal.telephony.GsmAlphabet;
-import com.android.internal.telephony.TelephonyProperties;
/**
* A GPS Network-initiated Handler class used by LocationManager.
@@ -50,8 +49,7 @@
private static final String TAG = "GpsNetInitiatedHandler";
- private static final boolean DEBUG = true;
- private static final boolean VERBOSE = false;
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
// NI verify activity for bringing up UI (not used yet)
public static final String ACTION_NI_VERIFY = "android.intent.action.NETWORK_INITIATED_VERIFY";
@@ -94,6 +92,9 @@
public static final int GPS_ENC_SUPL_UCS2 = 3;
public static final int GPS_ENC_UNKNOWN = -1;
+ // Limit on SUPL NI emergency mode time extension after emergency sessions ends
+ private static final int MAX_EMERGENCY_MODE_EXTENSION_SECONDS = 300; // 5 minute maximum
+
private final Context mContext;
private final TelephonyManager mTelephonyManager;
private final PhoneStateListener mPhoneStateListener;
@@ -109,7 +110,7 @@
private volatile boolean mIsSuplEsEnabled;
// Set to true if the phone is having emergency call.
- private volatile boolean mIsInEmergency;
+ private volatile boolean mIsInEmergencyCall;
// If Location function is enabled.
private volatile boolean mIsLocationEnabled = false;
@@ -119,6 +120,10 @@
// Set to true if string from HAL is encoded as Hex, e.g., "3F0039"
static private boolean mIsHexInput = true;
+ // End time of emergency call, and extension, if set
+ private long mCallEndElapsedRealtimeMillis = 0;
+ private long mEmergencyExtensionMillis = 0;
+
public static class GpsNiNotification
{
public int notificationId;
@@ -146,16 +151,12 @@
if (action.equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
/*
- Emergency Mode is when during emergency call or in emergency call back mode.
- For checking if it is during emergency call:
- mIsInEmergency records if the phone is in emergency call or not. It will
+ Tracks the emergency call:
+ mIsInEmergencyCall records if the phone is in emergency call or not. It will
be set to true when the phone is having emergency call, and then will
be set to false by mPhoneStateListener when the emergency call ends.
- For checking if it is in emergency call back mode:
- Emergency call back mode will be checked by reading system properties
- when necessary: SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)
*/
- setInEmergency(PhoneNumberUtils.isEmergencyNumber(phoneNumber));
+ mIsInEmergencyCall = PhoneNumberUtils.isEmergencyNumber(phoneNumber);
if (DEBUG) Log.v(TAG, "ACTION_NEW_OUTGOING_CALL - " + getInEmergency());
} else if (action.equals(LocationManager.MODE_CHANGED_ACTION)) {
updateLocationMode();
@@ -195,7 +196,10 @@
if (DEBUG) Log.d(TAG, "onCallStateChanged(): state is "+ state);
// listening for emergency call ends
if (state == TelephonyManager.CALL_STATE_IDLE) {
- setInEmergency(false);
+ if (mIsInEmergencyCall) {
+ mCallEndElapsedRealtimeMillis = SystemClock.elapsedRealtime();
+ mIsInEmergencyCall = false;
+ }
}
}
};
@@ -229,22 +233,35 @@
return mIsLocationEnabled;
}
- // Note: Currently, there are two mechanisms involved to determine if a
- // phone is in emergency mode:
- // 1. If the user is making an emergency call, this is provided by activly
- // monitoring the outgoing phone number;
- // 2. If the device is in a emergency callback state, this is provided by
- // system properties.
- // If either one of above exists, the phone is considered in an emergency
- // mode. Because of this complexity, we need to be careful about how to set
- // and clear the emergency state.
- public void setInEmergency(boolean isInEmergency) {
- mIsInEmergency = isInEmergency;
+ /**
+ * Determines whether device is in user-initiated emergency session based on the following
+ * 1. If the user is making an emergency call, this is provided by actively
+ * monitoring the outgoing phone number;
+ * 2. If the user has recently ended an emergency call, and the device is in a configured time
+ * window after the end of that call.
+ * 3. If the device is in a emergency callback state, this is provided by querying
+ * TelephonyManager.
+ * @return true if is considered in user initiated emergency mode for NI purposes
+ */
+ public boolean getInEmergency() {
+ boolean isInEmergencyExtension =
+ (SystemClock.elapsedRealtime() - mCallEndElapsedRealtimeMillis) <
+ mEmergencyExtensionMillis;
+ boolean isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode();
+ return mIsInEmergencyCall || isInEmergencyCallback || isInEmergencyExtension;
}
- public boolean getInEmergency() {
- boolean isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode();
- return mIsInEmergency || isInEmergencyCallback;
+ public void setEmergencyExtensionSeconds(int emergencyExtensionSeconds) {
+ if (emergencyExtensionSeconds > MAX_EMERGENCY_MODE_EXTENSION_SECONDS) {
+ Log.w(TAG, "emergencyExtensionSeconds " + emergencyExtensionSeconds
+ + " too high, reset to " + MAX_EMERGENCY_MODE_EXTENSION_SECONDS);
+ emergencyExtensionSeconds = MAX_EMERGENCY_MODE_EXTENSION_SECONDS;
+ } else if (emergencyExtensionSeconds < 0) {
+ Log.w(TAG, "emergencyExtensionSeconds " + emergencyExtensionSeconds
+ + " is negative, reset to zero.");
+ emergencyExtensionSeconds = 0;
+ }
+ mEmergencyExtensionMillis = TimeUnit.SECONDS.toMillis(emergencyExtensionSeconds);
}
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 0000000..c18d58f
--- /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 0000000..d7a3378
--- /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 0000000..2e54ae4
--- /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 4b2353c..33f81f1 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.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 @@
* 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 @@
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/AudioRecordingConfiguration.java b/media/java/android/media/AudioRecordingConfiguration.java
index 9ada216..de76aef 100644
--- a/media/java/android/media/AudioRecordingConfiguration.java
+++ b/media/java/android/media/AudioRecordingConfiguration.java
@@ -18,7 +18,9 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
+import android.media.audiofx.AudioEffect;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -27,6 +29,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.Objects;
/**
@@ -48,7 +52,7 @@
public final class AudioRecordingConfiguration implements Parcelable {
private final static String TAG = new String("AudioRecordingConfiguration");
- private final int mSessionId;
+ private final int mClientSessionId;
private final int mClientSource;
@@ -60,18 +64,50 @@
private final int mPatchHandle;
+ private final int mClientPortId;
+
+ private boolean mClientSilenced;
+
+ private final int mDeviceSource;
+
+ private final AudioEffect.Descriptor[] mClientEffects;
+
+ private final AudioEffect.Descriptor[] mDeviceEffects;
+
/**
* @hide
*/
+ @TestApi
public AudioRecordingConfiguration(int uid, int session, int source, AudioFormat clientFormat,
- AudioFormat devFormat, int patchHandle, String packageName) {
+ AudioFormat devFormat, int patchHandle, String packageName, int clientPortId,
+ boolean clientSilenced, int deviceSource,
+ AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] deviceEffects) {
mClientUid = uid;
- mSessionId = session;
+ mClientSessionId = session;
mClientSource = source;
mClientFormat = clientFormat;
mDeviceFormat = devFormat;
mPatchHandle = patchHandle;
mClientPackageName = packageName;
+ mClientPortId = clientPortId;
+ mClientSilenced = clientSilenced;
+ mDeviceSource = deviceSource;
+ mClientEffects = clientEffects;
+ mDeviceEffects = deviceEffects;
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ public AudioRecordingConfiguration(int uid, int session, int source,
+ AudioFormat clientFormat, AudioFormat devFormat,
+ int patchHandle, String packageName) {
+ this(uid, session, source, clientFormat,
+ devFormat, patchHandle, packageName, 0 /*clientPortId*/,
+ false /*clientSilenced*/, MediaRecorder.AudioSource.DEFAULT /*deviceSource*/,
+ new AudioEffect.Descriptor[0] /*clientEffects*/,
+ new AudioEffect.Descriptor[0] /*deviceEffects*/);
}
/**
@@ -87,13 +123,26 @@
* @hide
*/
public static String toLogFriendlyString(AudioRecordingConfiguration arc) {
- return new String("session:" + arc.mSessionId
- + " -- source:" + MediaRecorder.toLogFriendlyAudioSource(arc.mClientSource)
+ String clientEffects = new String();
+ for (AudioEffect.Descriptor desc : arc.mClientEffects) {
+ clientEffects += "'" + desc.name + "' ";
+ }
+ String deviceEffects = new String();
+ for (AudioEffect.Descriptor desc : arc.mDeviceEffects) {
+ deviceEffects += "'" + desc.name + "' ";
+ }
+
+ return new String("session:" + arc.mClientSessionId
+ + " -- source client=" + MediaRecorder.toLogFriendlyAudioSource(arc.mClientSource)
+ + ", dev=" + arc.mDeviceFormat.toLogFriendlyString()
+ " -- uid:" + arc.mClientUid
+ " -- patch:" + arc.mPatchHandle
+ " -- pack:" + arc.mClientPackageName
+ " -- format client=" + arc.mClientFormat.toLogFriendlyString()
- + ", dev=" + arc.mDeviceFormat.toLogFriendlyString());
+ + ", dev=" + arc.mDeviceFormat.toLogFriendlyString()
+ + " -- silenced:" + arc.mClientSilenced
+ + " -- effects client=" + clientEffects
+ + ", dev=" + deviceEffects);
}
// Note that this method is called server side, so no "privileged" information is ever sent
@@ -106,8 +155,10 @@
*/
public static AudioRecordingConfiguration anonymizedCopy(AudioRecordingConfiguration in) {
return new AudioRecordingConfiguration( /*anonymized uid*/ -1,
- in.mSessionId, in.mClientSource, in.mClientFormat,
- in.mDeviceFormat, in.mPatchHandle, "" /*empty package name*/);
+ in.mClientSessionId, in.mClientSource, in.mClientFormat,
+ in.mDeviceFormat, in.mPatchHandle, "" /*empty package name*/,
+ in.mClientPortId, in.mClientSilenced, in.mDeviceSource, in.mClientEffects,
+ in.mDeviceEffects);
}
// matches the sources that return false in MediaRecorder.isSystemOnlyAudioSource(source)
@@ -129,16 +180,8 @@
// documented return values match the sources that return false
// in MediaRecorder.isSystemOnlyAudioSource(source)
/**
- * Returns the audio source being used for the recording.
- * @return one of {@link MediaRecorder.AudioSource#DEFAULT},
- * {@link MediaRecorder.AudioSource#MIC},
- * {@link MediaRecorder.AudioSource#VOICE_UPLINK},
- * {@link MediaRecorder.AudioSource#VOICE_DOWNLINK},
- * {@link MediaRecorder.AudioSource#VOICE_CALL},
- * {@link MediaRecorder.AudioSource#CAMCORDER},
- * {@link MediaRecorder.AudioSource#VOICE_RECOGNITION},
- * {@link MediaRecorder.AudioSource#VOICE_COMMUNICATION},
- * {@link MediaRecorder.AudioSource#UNPROCESSED}.
+ * Returns the audio source selected by the client.
+ * @return the audio source selected by the client.
*/
public @AudioSource int getClientAudioSource() { return mClientSource; }
@@ -146,7 +189,9 @@
* Returns the session number of the recording, see {@link AudioRecord#getAudioSessionId()}.
* @return the session number.
*/
- public int getClientAudioSessionId() { return mSessionId; }
+ public int getClientAudioSessionId() {
+ return mClientSessionId;
+ }
/**
* Returns the audio format at which audio is recorded on this Android device.
@@ -223,6 +268,54 @@
return null;
}
+ /**
+ * Returns the system unique ID assigned for the AudioRecord object corresponding to this
+ * AudioRecordingConfiguration client.
+ * @return the port ID.
+ */
+ int getClientPortId() {
+ return mClientPortId;
+ }
+
+ /**
+ * Returns true if the audio returned to the client is currently being silenced by the
+ * audio framework due to concurrent capture policy (e.g the capturing application does not have
+ * an active foreground process or service anymore).
+ * @return true if captured audio is silenced, false otherwise .
+ */
+ public boolean isClientSilenced() {
+ return mClientSilenced;
+ }
+
+ /**
+ * Returns the audio source currently used to configure the capture path. It can be different
+ * from the source returned by {@link #getClientAudioSource()} if another capture is active.
+ * @return the audio source active on the capture path.
+ */
+ public @AudioSource int getAudioSource() {
+ return mDeviceSource;
+ }
+
+ /**
+ * Returns the list of {@link AudioEffect.Descriptor} for all effects currently enabled on
+ * the audio capture client (e.g. {@link AudioRecord} or {@link MediaRecorder}).
+ * @return List of {@link AudioEffect.Descriptor} containing all effects enabled for the client.
+ */
+ public @NonNull List<AudioEffect.Descriptor> getClientEffects() {
+ return new ArrayList<AudioEffect.Descriptor>(Arrays.asList(mClientEffects));
+ }
+
+ /**
+ * Returns the list of {@link AudioEffect.Descriptor} for all effects currently enabled on
+ * the capture stream.
+ * @return List of {@link AudioEffect.Descriptor} containing all effects enabled on the
+ * capture stream. This can be different from the list returned by {@link #getClientEffects()}
+ * if another capture is active.
+ */
+ public @NonNull List<AudioEffect.Descriptor> getEffects() {
+ return new ArrayList<AudioEffect.Descriptor>(Arrays.asList(mDeviceEffects));
+ }
+
public static final Parcelable.Creator<AudioRecordingConfiguration> CREATOR
= new Parcelable.Creator<AudioRecordingConfiguration>() {
/**
@@ -240,7 +333,7 @@
@Override
public int hashCode() {
- return Objects.hash(mSessionId, mClientSource);
+ return Objects.hash(mClientSessionId, mClientSource);
}
@Override
@@ -250,23 +343,45 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mSessionId);
+ dest.writeInt(mClientSessionId);
dest.writeInt(mClientSource);
mClientFormat.writeToParcel(dest, 0);
mDeviceFormat.writeToParcel(dest, 0);
dest.writeInt(mPatchHandle);
dest.writeString(mClientPackageName);
dest.writeInt(mClientUid);
+ dest.writeInt(mClientPortId);
+ dest.writeBoolean(mClientSilenced);
+ dest.writeInt(mDeviceSource);
+ dest.writeInt(mClientEffects.length);
+ for (int i = 0; i < mClientEffects.length; i++) {
+ mClientEffects[i].writeToParcel(dest, 0);
+ }
+ dest.writeInt(mDeviceEffects.length);
+ for (int i = 0; i < mDeviceEffects.length; i++) {
+ mDeviceEffects[i].writeToParcel(dest, 0);
+ }
}
private AudioRecordingConfiguration(Parcel in) {
- mSessionId = in.readInt();
+ mClientSessionId = in.readInt();
mClientSource = in.readInt();
mClientFormat = AudioFormat.CREATOR.createFromParcel(in);
mDeviceFormat = AudioFormat.CREATOR.createFromParcel(in);
mPatchHandle = in.readInt();
mClientPackageName = in.readString();
mClientUid = in.readInt();
+ mClientPortId = in.readInt();
+ mClientSilenced = in.readBoolean();
+ mDeviceSource = in.readInt();
+ mClientEffects = AudioEffect.Descriptor.CREATOR.newArray(in.readInt());
+ for (int i = 0; i < mClientEffects.length; i++) {
+ mClientEffects[i] = AudioEffect.Descriptor.CREATOR.createFromParcel(in);
+ }
+ mDeviceEffects = AudioEffect.Descriptor.CREATOR.newArray(in.readInt());
+ for (int i = 0; i < mClientEffects.length; i++) {
+ mDeviceEffects[i] = AudioEffect.Descriptor.CREATOR.createFromParcel(in);
+ }
}
@Override
@@ -277,11 +392,16 @@
AudioRecordingConfiguration that = (AudioRecordingConfiguration) o;
return ((mClientUid == that.mClientUid)
- && (mSessionId == that.mSessionId)
+ && (mClientSessionId == that.mClientSessionId)
&& (mClientSource == that.mClientSource)
&& (mPatchHandle == that.mPatchHandle)
&& (mClientFormat.equals(that.mClientFormat))
&& (mDeviceFormat.equals(that.mDeviceFormat))
- && (mClientPackageName.equals(that.mClientPackageName)));
+ && (mClientPackageName.equals(that.mClientPackageName))
+ && (mClientPortId == that.mClientPortId)
+ && (mClientSilenced == that.mClientSilenced)
+ && (mDeviceSource == that.mDeviceSource)
+ && (mClientEffects.equals(that.mClientEffects))
+ && (mDeviceEffects.equals(that.mDeviceEffects)));
}
}
diff --git a/media/java/android/media/AudioRecordingMonitor.java b/media/java/android/media/AudioRecordingMonitor.java
new file mode 100644
index 0000000..e2605d0
--- /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 0000000..7578d9b
--- /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 0000000..c2cd4bc
--- /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/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 36f635a..58fc1ab 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -20,6 +20,7 @@
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.media.audiofx.AudioEffect;
import android.media.audiopolicy.AudioMix;
import android.os.Build;
import android.util.Log;
@@ -334,7 +335,9 @@
* @param packName package name of the client app performing the recording. NOT SUPPORTED
*/
void onRecordingConfigurationChanged(int event, int uid, int session, int source,
- int[] recordingFormat, String packName);
+ int portId, boolean silenced, int[] recordingFormat,
+ AudioEffect.Descriptor[] clienteffects, AudioEffect.Descriptor[] effects,
+ int activeSource, String packName);
}
private static AudioRecordingCallback sRecordingCallback;
@@ -352,19 +355,27 @@
* @param session
* @param source
* @param recordingFormat see
- * {@link AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int, int, int[])}
+ * {@link AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int, int, int,\
+ boolean, int[], AudioEffect.Descriptor[], AudioEffect.Descriptor[], int, String)}
* for the description of the record format.
*/
@UnsupportedAppUsage
private static void recordingCallbackFromNative(int event, int uid, int session, int source,
- int[] recordingFormat) {
+ int portId, boolean silenced, int[] recordingFormat,
+ AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] effects,
+ int activeSource) {
AudioRecordingCallback cb = null;
synchronized (AudioSystem.class) {
cb = sRecordingCallback;
}
+
+ String clientEffectName = clientEffects.length == 0 ? "None" : clientEffects[0].name;
+ String effectName = effects.length == 0 ? "None" : effects[0].name;
+
if (cb != null) {
// TODO receive package name from native
- cb.onRecordingConfigurationChanged(event, uid, session, source, recordingFormat, "");
+ cb.onRecordingConfigurationChanged(event, uid, session, source, portId, silenced,
+ recordingFormat, clientEffects, effects, activeSource, "");
}
}
diff --git a/media/java/android/media/FileDataSourceDesc.java b/media/java/android/media/FileDataSourceDesc.java
index aca8dbe..e29bd00 100644
--- a/media/java/android/media/FileDataSourceDesc.java
+++ b/media/java/android/media/FileDataSourceDesc.java
@@ -44,6 +44,8 @@
private ParcelFileDescriptor mPFD;
private long mOffset = 0;
private long mLength = FD_LENGTH_UNKNOWN;
+ private int mCount = 0;
+ private boolean mClosed = false;
private FileDataSourceDesc() {
super();
@@ -55,23 +57,48 @@
@Override
void close() {
super.close();
- closeFD();
+ decCount();
}
/**
- * Releases the file descriptor held by this {@code FileDataSourceDesc} object.
+ * Decrements usage count by {@link MediaPlayer2}.
+ * If this is the last usage, also releases the file descriptor held by this
+ * {@code FileDataSourceDesc} object.
*/
- void closeFD() {
+ void decCount() {
synchronized (this) {
- if (mPFD != null) {
- try {
- mPFD.close();
- } catch (IOException e) {
- Log.e(TAG, "failed to close pfd: " + e);
- }
-
- mPFD = null;
+ --mCount;
+ if (mCount > 0) {
+ return;
}
+
+ try {
+ mPFD.close();
+ mClosed = true;
+ } catch (IOException e) {
+ Log.e(TAG, "failed to close pfd: " + e);
+ }
+ }
+ }
+
+ /**
+ * Increments usage count by {@link MediaPlayer2} if PFD has not been closed.
+ */
+ void incCount() {
+ synchronized (this) {
+ if (!mClosed) {
+ ++mCount;
+ }
+ }
+ }
+
+ /**
+ * Return the status of underline ParcelFileDescriptor
+ * @return true if underline ParcelFileDescriptor is closed, false otherwise.
+ */
+ boolean isPFDClosed() {
+ synchronized (this) {
+ return mClosed;
}
}
@@ -150,6 +177,16 @@
* @return a new {@link FileDataSourceDesc} object
*/
public @NonNull FileDataSourceDesc build() {
+ if (mPFD == null) {
+ throw new IllegalStateException(
+ "underline ParcelFileDescriptor should not be null");
+ }
+ try {
+ mPFD.getFd();
+ } catch (IllegalStateException e) {
+ throw new IllegalStateException("ParcelFileDescriptor has been closed");
+ }
+
FileDataSourceDesc dsd = new FileDataSourceDesc();
super.build(dsd);
dsd.mPFD = mPFD;
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index b137ce2..34345b3 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -696,7 +696,7 @@
return addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
@Override
void process() throws IOException {
- Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+ checkDataSourceDesc(dsd);
int state = getState();
try {
if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) {
@@ -729,7 +729,7 @@
return addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
@Override
void process() {
- Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+ checkDataSourceDesc(dsd);
synchronized (mSrcLock) {
clearNextSourceInfos_l();
mNextSourceInfos.add(new SourceInfo(dsd));
@@ -755,15 +755,35 @@
if (dsds == null || dsds.size() == 0) {
throw new IllegalArgumentException("data source list cannot be null or empty.");
}
+ boolean hasError = false;
+ for (DataSourceDesc dsd : dsds) {
+ if (dsd != null) {
+ hasError = true;
+ continue;
+ }
+ if (dsd instanceof FileDataSourceDesc) {
+ FileDataSourceDesc fdsd = (FileDataSourceDesc) dsd;
+ if (fdsd.isPFDClosed()) {
+ hasError = true;
+ continue;
+ }
+
+ fdsd.incCount();
+ }
+ }
+ if (hasError) {
+ for (DataSourceDesc dsd : dsds) {
+ if (dsd != null) {
+ dsd.close();
+ }
+ }
+ throw new IllegalArgumentException("invalid data source list");
+ }
synchronized (mSrcLock) {
clearNextSourceInfos_l();
for (DataSourceDesc dsd : dsds) {
- if (dsd != null) {
- mNextSourceInfos.add(new SourceInfo(dsd));
- } else {
- Log.w(TAG, "DataSourceDesc in the source list shall not be null.");
- }
+ mNextSourceInfos.add(new SourceInfo(dsd));
}
}
prepareNextDataSource();
@@ -771,6 +791,20 @@
});
}
+ // throws IllegalArgumentException if dsd is null or underline PFD of dsd has been closed.
+ private void checkDataSourceDesc(DataSourceDesc dsd) {
+ if (dsd == null) {
+ throw new IllegalArgumentException("dsd is expected to be non null");
+ }
+ if (dsd instanceof FileDataSourceDesc) {
+ FileDataSourceDesc fdsd = (FileDataSourceDesc) dsd;
+ if (fdsd.isPFDClosed()) {
+ throw new IllegalArgumentException("the underline FileDescriptor has been closed");
+ }
+ fdsd.incCount();
+ }
+ }
+
/**
* Removes all data sources pending to be played.
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 8ced021..1cdc291 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.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 @@
* <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 @@
/**
* 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 @@
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 b3a8b21..ca30f32 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -763,6 +763,20 @@
}
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 @@
{"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 c527711..2e05c38 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/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
index 7039a2c..3c0a297 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
@@ -21,9 +21,11 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.car.CarNotificationEntryManager;
+import com.android.systemui.car.CarNotificationInterruptionStateProvider;
import com.android.systemui.statusbar.car.CarFacetButtonController;
import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.volume.CarVolumeDialogComponent;
import com.android.systemui.volume.VolumeDialogComponent;
@@ -67,6 +69,12 @@
return new CarNotificationEntryManager(context);
}
+ @Override
+ public NotificationInterruptionStateProvider provideNotificationInterruptionStateProvider(
+ Context context) {
+ return new CarNotificationInterruptionStateProvider(context);
+ }
+
@Module
protected static class ContextHolder {
private Context mContext;
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index 0563418..323cae0 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -18,7 +18,6 @@
import android.content.Context;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -39,16 +38,4 @@
// long click listener.
return null;
}
-
- @Override
- public boolean shouldHeadsUp(NotificationData.Entry entry) {
- // Because space is usually constrained in the auto use-case, there should not be a
- // pinned notification when the shade has been expanded. Ensure this by not pinning any
- // notification if the shade is already opened.
- if (!getPresenter().isPresenterFullyCollapsed()) {
- return false;
- }
-
- return super.shouldHeadsUp(entry);
- }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
new file mode 100644
index 0000000..62502ef
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.car;
+
+import android.content.Context;
+
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+
+/** Auto-specific implementation of {@link NotificationInterruptionStateProvider}. */
+public class CarNotificationInterruptionStateProvider extends
+ NotificationInterruptionStateProvider {
+ public CarNotificationInterruptionStateProvider(Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean shouldHeadsUp(NotificationData.Entry entry) {
+ // Because space is usually constrained in the auto use-case, there should not be a
+ // pinned notification when the shade has been expanded. Ensure this by not pinning any
+ // notification if the shade is already opened.
+ if (!getPresenter().isPresenterFullyCollapsed()) {
+ return false;
+ }
+
+ return super.shouldHeadsUp(entry);
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
index aec31ee..30429ed 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
@@ -16,7 +16,11 @@
package com.android.systemui.statusbar.car.hvac;
+import static android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
+import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS;
+
import android.car.Car;
+import android.car.VehicleUnit;
import android.car.hardware.CarPropertyValue;
import android.car.hardware.hvac.CarHvacManager;
import android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback;
@@ -167,7 +171,17 @@
private void initComponent(TemperatureView view) {
int id = view.getPropertyId();
int zone = view.getAreaId();
+
try {
+ if (mHvacManager != null
+ && mHvacManager.isPropertyAvailable(HVAC_TEMPERATURE_DISPLAY_UNITS,
+ VEHICLE_AREA_TYPE_GLOBAL)) {
+ if (mHvacManager.getIntProperty(HVAC_TEMPERATURE_DISPLAY_UNITS,
+ VEHICLE_AREA_TYPE_GLOBAL) == VehicleUnit.FAHRENHEIT) {
+ view.setDisplayInFahrenheit(true);
+ }
+
+ }
if (mHvacManager == null || !mHvacManager.isPropertyAvailable(id, zone)) {
view.setTemp(Float.NaN);
return;
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
index 507c60f..17ef3c0 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
@@ -36,6 +36,7 @@
private final int mAreaId;
private final int mPropertyId;
private final String mTempFormat;
+ private boolean mDisplayFahrenheit = false;
public TemperatureTextView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -57,9 +58,17 @@
setText("--");
return;
}
+ if (mDisplayFahrenheit) {
+ temp = convertToFahrenheit(temp);
+ }
setText(String.format(mTempFormat, temp));
}
+ @Override
+ public void setDisplayInFahrenheit(boolean displayFahrenheit) {
+ mDisplayFahrenheit = displayFahrenheit;
+ }
+
/**
* @return propertiyId Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385)
*/
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
index 7651356..c17da18 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
@@ -23,10 +23,26 @@
/**
* Formats the float for display
*
- * @param temp - The current temp or NaN
+ * @param temp - The current temp in Celsius or NaN
*/
void setTemp(float temp);
+ /**
+ * Render the displayed temperature in Fahrenheit
+ *
+ * @param displayFahrenheit - True if temperature should be displayed in Fahrenheit
+ */
+ void setDisplayInFahrenheit(boolean displayFahrenheit);
+
+ /**
+ * Convert the given temperature in Celsius into Fahrenheit
+ *
+ * @param realTemp - The temperature in Celsius
+ * @return Temperature in Fahrenheit.
+ */
+ default float convertToFahrenheit(float realTemp) {
+ return (realTemp * 9f / 5f) + 32;
+ }
/**
* @return propertiyId Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385)
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java
index 0467bff..76126fc 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java
@@ -99,6 +99,7 @@
private final TemperatureColorStore mColorStore = new TemperatureColorStore();
private final TemperatureBackgroundAnimator mBackgroundAnimator;
private final TemperatureTextAnimator mTextAnimator;
+ boolean mDisplayInFahrenheit = false;
public AnimatedTemperatureView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -193,6 +194,9 @@
*/
@Override
public void setTemp(float temp) {
+ if (mDisplayInFahrenheit) {
+ temp = convertToFahrenheit(temp);
+ }
mTextAnimator.setTemp(temp);
if (Float.isNaN(temp)) {
mBackgroundAnimator.hideCircle();
@@ -219,6 +223,11 @@
mBackgroundAnimator.animateOpen();
}
+ @Override
+ public void setDisplayInFahrenheit(boolean displayInFahrenheit) {
+ mDisplayInFahrenheit = displayInFahrenheit;
+ }
+
boolean isMinValue(float temp) {
return !Float.isNaN(mMinValue) && isApproxEqual(temp, mMinValue);
}
diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml
index 0be71e6..42885e8 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 133d8ba..af52c00 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 @@
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 @@
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 @@
synchronized (mkeyToImpressions) {
ChannelImpressions ci = mkeyToImpressions.getOrDefault(key,
createChannelImpressionsWithThresholds());
- if (stats.hasSeen()) {
+ if (stats != null && stats.hasSeen()) {
ci.incrementViews();
updatedImpressions = true;
}
@@ -343,6 +341,7 @@
if (entry != null) {
entry.setSeen();
mAgingHelper.onNotificationSeen(entry);
+ mSmartActionsHelper.onNotificationSeen(entry);
}
}
} catch (Throwable e) {
@@ -351,34 +350,46 @@
}
@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 6f437bd5..71fd9ce 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 @@
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 @@
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 38df9b0..b041842 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.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 @@
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 @@
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 @@
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 @@
}
// 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 @@
.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 @@
/** 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 @@
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 @@
// 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 0352ebc..da382a0 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.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.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 @@
@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 @@
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 @@
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 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 @@
.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 @@
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/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 1eb4b74..8d04702 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -67,7 +67,7 @@
private static final boolean DEBUG = false;
- public static final String AUTHORITY = "com.android.externalstorage.documents";
+ public static final String AUTHORITY = DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY;
private static final Uri BASE_URI =
new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY).build();
@@ -96,7 +96,8 @@
public boolean reportAvailableBytes = true;
}
- private static final String ROOT_ID_PRIMARY_EMULATED = "primary";
+ private static final String ROOT_ID_PRIMARY_EMULATED =
+ DocumentsContract.EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID;
private static final String ROOT_ID_HOME = "home";
private StorageManager mStorageManager;
diff --git a/packages/ExternalStorageProvider/tests/Android.bp b/packages/ExternalStorageProvider/tests/Android.bp
index 83427d4..04cf01a 100644
--- a/packages/ExternalStorageProvider/tests/Android.bp
+++ b/packages/ExternalStorageProvider/tests/Android.bp
@@ -14,7 +14,7 @@
],
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 58b6e86..f1a6af0 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 e5fa73f..f8438b2 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 a88b3e1..fbf2e4b 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.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/MtpDocumentsProvider/perf_tests/Android.mk b/packages/MtpDocumentsProvider/perf_tests/Android.mk
index e873157..d2f1c7a 100644
--- a/packages/MtpDocumentsProvider/perf_tests/Android.mk
+++ b/packages/MtpDocumentsProvider/perf_tests/Android.mk
@@ -3,7 +3,7 @@
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
LOCAL_PACKAGE_NAME := MtpDocumentsProviderPerfTests
LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_INSTRUMENTATION_FOR := MtpDocumentsProvider
diff --git a/packages/MtpDocumentsProvider/perf_tests/AndroidManifest.xml b/packages/MtpDocumentsProvider/perf_tests/AndroidManifest.xml
index 26e109d..4367652 100644
--- a/packages/MtpDocumentsProvider/perf_tests/AndroidManifest.xml
+++ b/packages/MtpDocumentsProvider/perf_tests/AndroidManifest.xml
@@ -7,7 +7,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.mtp"
android:label="Performance tests for MtpDocumentsProvider." />
</manifest>
diff --git a/packages/MtpDocumentsProvider/perf_tests/src/com/android/mtp/AppFusePerfTest.java b/packages/MtpDocumentsProvider/perf_tests/src/com/android/mtp/AppFusePerfTest.java
index 36f6fe9..58b9c67 100644
--- a/packages/MtpDocumentsProvider/perf_tests/src/com/android/mtp/AppFusePerfTest.java
+++ b/packages/MtpDocumentsProvider/perf_tests/src/com/android/mtp/AppFusePerfTest.java
@@ -23,17 +23,15 @@
import android.os.ProxyFileDescriptorCallback;
import android.os.storage.StorageManager;
import android.system.ErrnoException;
-import android.system.Os;
-import android.support.test.filters.LargeTest;
-import android.support.test.InstrumentationRegistry;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.Arrays;
-import libcore.io.IoUtils;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
+import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import org.junit.Test;
+
+import java.io.IOException;
@RunWith(JUnit4.class)
public class AppFusePerfTest {
diff --git a/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java b/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java
index 0312b81..28b05396 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.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 @@
@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/PrintSpooler/tests/outofprocess/Android.bp b/packages/PrintSpooler/tests/outofprocess/Android.bp
index e88074e..c6dc263 100644
--- a/packages/PrintSpooler/tests/outofprocess/Android.bp
+++ b/packages/PrintSpooler/tests/outofprocess/Android.bp
@@ -19,7 +19,7 @@
libs: ["android.test.runner.stubs"],
static_libs: [
- "android-support-test",
+ "androidx.test.rules",
"ub-uiautomator",
"mockito-target-minus-junit4",
"print-test-util-lib",
diff --git a/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml b/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml
index 307cc93..fdcaa52 100644
--- a/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml
+++ b/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml
@@ -54,7 +54,7 @@
</application>
<!-- This runs in its own process, hence it instruments itself -->
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.printspooler.outofprocess.tests"
android:label="PrintSpooler Out of Process Test Cases">
</instrumentation>
diff --git a/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml b/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml
index d21a2e4..b649e82 100644
--- a/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml
+++ b/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml
@@ -24,7 +24,7 @@
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.printspooler.outofprocess.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/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
index 7ebf93d..61c2f54 100644
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
+++ b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
@@ -35,7 +35,6 @@
import android.print.test.services.FirstPrintService;
import android.print.test.services.PrinterDiscoverySessionCallbacks;
import android.print.test.services.StubbablePrinterDiscoverySession;
-import android.support.test.filters.LargeTest;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiObjectNotFoundException;
@@ -43,6 +42,8 @@
import android.support.test.uiautomator.Until;
import android.util.Log;
+import androidx.test.filters.LargeTest;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
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 89ebf4d..305862a 100644
--- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
@@ -31,7 +31,7 @@
import java.util.Arrays;
/**
- * This BarChartPreference shows four bar views in this preference at most.
+ * This BarChartPreference shows up to four bar views in this preference at most.
*
* <p>The following code sample shows a typical use, with an XML layout and code to initialize the
* contents of the BarChartPreference:
@@ -74,71 +74,28 @@
};
private int mMaxBarHeight;
- private @StringRes int mTitleId;
- private @StringRes int mDetailsId;
+ @StringRes
+ private int mTitleId;
+ @StringRes
+ private int mDetailsId;
private BarViewInfo[] mBarViewsInfo;
private View.OnClickListener mDetailsOnClickListener;
- /**
- * Constructs a new BarChartPreference with the given context's theme.
- * It sets a layout with settings bar chart style
- *
- * @param context The Context the view is running in, through which it can
- * access the current theme, resources, etc.
- */
public BarChartPreference(Context context) {
super(context);
init();
}
- /**
- * Constructs a new BarChartPreference with the given context's theme and the supplied
- * attribute set.
- * It sets a layout with settings bar chart style
- *
- * @param context the Context the view is running in
- * @param attrs the attributes of the XML tag that is inflating the view.
- */
public BarChartPreference(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
- /**
- * Constructs a new BarChartPreference with the given context's theme, the supplied
- * attribute set, and default style attribute.
- * It sets a layout with settings bar chart style
- *
- * @param context The Context the view is running in, through which it can
- * access the current theme, resources, etc.
- * @param attrs The attributes of the XML tag that is inflating the view.
- * @param defStyleAttr An attribute in the current theme that contains a
- * reference to a style resource that supplies default
- * values for the view. Can be 0 to not look for
- * defaults.
- */
public BarChartPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
- /**
- * Constructs a new BarChartPreference with the given context's theme, the supplied
- * attribute set, and default styles.
- * It sets a layout with settings bar chart style
- *
- * @param context The Context the view is running in, through which it can
- * access the current theme, resources, etc.
- * @param attrs The attributes of the XML tag that is inflating the view.
- * @param defStyleAttr An attribute in the current theme that contains a
- * reference to a style resource that supplies default
- * values for the view. Can be 0 to not look for
- * defaults.
- * @param defStyleRes A resource identifier of a style resource that
- * supplies default values for the view, used only if
- * defStyleAttr is 0 or can not be found in the theme.
- * Can be 0 to not look for defaults.
- */
public BarChartPreference(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
@@ -172,8 +129,6 @@
/**
* Set all bar view information which you'd like to show in preference.
*
- * <p>This method helps you do a sort by {@linkBarViewInfo#mBarNumber} in descending order.
- *
* @param barViewsInfo the barViewsInfo contain at least one {@link BarViewInfo}.
*/
public void setAllBarViewsInfo(@NonNull BarViewInfo[] barViewsInfo) {
@@ -181,7 +136,7 @@
// Do a sort in descending order, the first element would have max {@link
// BarViewInfo#mBarNumber}
Arrays.sort(mBarViewsInfo);
- caculateAllBarViewsHeight();
+ calculateAllBarViewHeights();
notifyChanged();
}
@@ -210,8 +165,12 @@
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) {
@@ -224,19 +183,19 @@
continue;
}
barView.setVisibility(View.VISIBLE);
- barView.updateBarViewUI(mBarViewsInfo[index]);
+ barView.updateView(mBarViewsInfo[index]);
}
}
- private void caculateAllBarViewsHeight() {
+ private void calculateAllBarViewHeights() {
// Since we sorted this array in advance, the first element must have the max {@link
- // BarViewInfo#mBarNumber}.
- final int maxBarViewNumber = mBarViewsInfo[0].getBarNumber();
- // If the max number of bar view is zero, then we don't caculate the unit for bar height.
- final int unit = maxBarViewNumber == 0 ? 0 : mMaxBarHeight / maxBarViewNumber;
+ // BarViewInfo#mHeight}.
+ final int maxBarHeight = mBarViewsInfo[0].getHeight();
+ // If the max number of bar view is zero, then we don't calculate the unit for bar height.
+ final int unit = maxBarHeight == 0 ? 0 : mMaxBarHeight / maxBarHeight;
for (BarViewInfo barView : mBarViewsInfo) {
- barView.setBarHeight(barView.getBarNumber() * unit);
+ barView.setNormalizedHeight(barView.getHeight() * unit);
}
}
}
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
index 6243a2d..6bf61ae 100644
--- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
@@ -30,7 +30,7 @@
import androidx.annotation.VisibleForTesting;
/**
- * A extension view for bar chart.
+ * {@link View} for a single vertical bar with icon and summary.
*/
public class BarView extends LinearLayout {
@@ -41,24 +41,11 @@
private TextView mBarTitle;
private TextView mBarSummary;
- /**
- * Constructs a new BarView with the given context's theme.
- *
- * @param context The Context the view is running in, through which it can
- * access the current theme, resources, etc.
- */
public BarView(Context context) {
super(context);
init();
}
- /**
- * Constructs a new BarView with the given context's theme and the supplied
- * attribute set.
- *
- * @param context the Context the view is running in
- * @param attrs the attributes of the XML tag that is inflating the view.
- */
public BarView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
@@ -77,17 +64,16 @@
}
/**
- * This helps update the bar view UI with a {@link BarViewInfo}.
- *
- * @param barViewInfo A {@link BarViewInfo} saves bar view status.
+ * Updates the view with a {@link BarViewInfo}.
*/
- public void updateBarViewUI(BarViewInfo barViewInfo) {
+ void updateView(BarViewInfo barViewInfo) {
+ setOnClickListener(barViewInfo.getClickListener());
//Set height of bar view
- mBarView.getLayoutParams().height = barViewInfo.getBarHeight();
+ mBarView.getLayoutParams().height = barViewInfo.getNormalizedHeight();
mIcon.setImageDrawable(barViewInfo.getIcon());
// For now, we use the bar number as title.
- mBarTitle.setText(Integer.toString(barViewInfo.getBarNumber()));
- mBarSummary.setText(barViewInfo.getSummaryRes());
+ mBarTitle.setText(Integer.toString(barViewInfo.getHeight()));
+ mBarSummary.setText(barViewInfo.getSummary());
}
@VisibleForTesting
@@ -106,9 +92,9 @@
setGravity(Gravity.CENTER);
mBarView = findViewById(R.id.bar_view);
- mIcon = (ImageView) findViewById(R.id.icon_view);
- mBarTitle = (TextView) findViewById(R.id.bar_title);
- mBarSummary = (TextView) findViewById(R.id.bar_summary);
+ mIcon = findViewById(R.id.icon_view);
+ mBarTitle = findViewById(R.id.bar_title);
+ mBarSummary = findViewById(R.id.bar_summary);
}
private void setOnClickListner(View.OnClickListener listener) {
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java
index aa83ce9..409f9ea 100644
--- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java
@@ -31,116 +31,71 @@
public class BarViewInfo implements Comparable<BarViewInfo> {
private final Drawable mIcon;
- private View.OnClickListener mListener;
- private @StringRes int mSummaryRes;
+ private View.OnClickListener mClickListener;
+ @StringRes
+ private int mSummary;
// A number indicates this bar's height. The larger number shows a higher bar view.
- private int mBarNumber;
+ private int mHeight;
// A real height of bar view.
- private int mBarHeight;
+ private int mNormalizedHeight;
/**
* Construct a BarViewInfo instance.
*
- * @param icon the icon of bar view.
- * @param barNumber the number of bar view. The larger number show a more height of bar view.
- * @param summaryRes the resource identifier of the string resource to be displayed
- * @return BarViewInfo object.
+ * @param icon The icon of bar view.
+ * @param barHeight The height of bar view. Larger number shows a higher bar view.
+ * @param summary The string resource id for summary.
*/
- public BarViewInfo(Drawable icon, @IntRange(from = 0) int barNumber,
- @StringRes int summaryRes) {
+ public BarViewInfo(Drawable icon, @IntRange(from = 0) int barHeight, @StringRes int summary) {
mIcon = icon;
- mBarNumber = barNumber;
- mSummaryRes = summaryRes;
+ mHeight = barHeight;
+ mSummary = summary;
}
/**
- * Set number for bar view.
- *
- * @param barNumber the number of bar view. The larger number shows a higher bar view.
- */
- public void setBarNumber(@IntRange(from = 0) int barNumber) {
- mBarNumber = barNumber;
- }
-
- /**
- * Set summary resource for bar view
- *
- * @param resId the resource identifier of the string resource to be displayed
- */
- public void setSummary(@StringRes int resId) {
- mSummaryRes = resId;
- }
-
- /**
- * Set a click listner for bar view.
- *
- * @param listener the click listner is attached on bar view.
+ * Set a click listener for bar view.
*/
public void setClickListener(@Nullable View.OnClickListener listener) {
- mListener = listener;
- }
-
- /**
- * Get the icon of bar view.
- *
- * @return Drawable the icon of bar view.
- */
- public Drawable getIcon() {
- return mIcon;
- }
-
- /**
- * Get the OnClickListener of bar view.
- *
- * @return View.OnClickListener the click listner of bar view.
- */
- public View.OnClickListener getListener() {
- return mListener;
- }
-
- /**
- * Get the real height of bar view.
- *
- * @return the real height of bar view.
- */
- public int getBarHeight() {
- return mBarHeight;
- }
-
- /**
- * Get summary resource of bar view.
- *
- * @return summary resource of bar view.
- */
- public int getSummaryRes() {
- return mSummaryRes;
- }
-
- /**
- * Get the number of app uses this permisssion.
- *
- * @return the number of app uses this permission.
- */
- public int getBarNumber() {
- return mBarNumber;
+ mClickListener = listener;
}
@Override
public int compareTo(BarViewInfo other) {
// Descending order
- return Comparator.comparingInt((BarViewInfo barViewInfo) -> barViewInfo.mBarNumber)
+ return Comparator.comparingInt((BarViewInfo barViewInfo) -> barViewInfo.mHeight)
.compare(other, this);
}
- /**
- * Set a real height for bar view.
- *
- * <p>This method should not be called by outside. It usually should be called by
- * {@link BarChartPreference#caculateAllBarViewsHeight}
- *
- * @param barHeight the real bar height for bar view.
- */
- void setBarHeight(@IntRange(from = 0) int barHeight) {
- mBarHeight = barHeight;
+ void setHeight(@IntRange(from = 0) int height) {
+ mHeight = height;
+ }
+
+ void setSummary(@StringRes int resId) {
+ mSummary = resId;
+ }
+
+ Drawable getIcon() {
+ return mIcon;
+ }
+
+ int getHeight() {
+ return mHeight;
+ }
+
+ View.OnClickListener getClickListener() {
+ return mClickListener;
+ }
+
+ @StringRes
+ int getSummary() {
+ return mSummary;
+ }
+
+ void setNormalizedHeight(@IntRange(from = 0) int barHeight) {
+ mNormalizedHeight = barHeight;
+ }
+
+ int getNormalizedHeight() {
+ return mNormalizedHeight;
}
}
diff --git a/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml b/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
index a046332..7e65848 100644
--- a/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
+++ b/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
@@ -20,7 +20,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?android:attr/colorAccent">
+ android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="#FF000000"
android:pathData="M20.49,19l-5.73,-5.73C15.53,12.2 16,10.91 16,9.5C16,5.91 13.09,3 9.5,3S3,5.91 3,9.5C3,13.09 5.91,16 9.5,16c1.41,0 2.7,-0.47 3.77,-1.24L19,20.49L20.49,19zM5,9.5C5,7.01 7.01,5 9.5,5S14,7.01 14,9.5S11.99,14 9.5,14S5,11.99 5,9.5z"/>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 2b7babd0..177ba00 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -321,6 +321,11 @@
// Dispatch device add callback to show bonded but
// not connected devices in discovery mode
dispatchDeviceAdded(cachedDevice);
+ Log.d(TAG, "DeviceFoundHandler found bonded and not connected device:"
+ + cachedDevice);
+ } else {
+ Log.d(TAG, "DeviceFoundHandler found existing CachedBluetoothDevice:"
+ + cachedDevice);
}
cachedDevice.setRssi(rssi);
cachedDevice.setJustDiscovered(true);
@@ -463,6 +468,16 @@
private class AclStateChangedHandler implements Handler {
@Override
public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ if (device == null) {
+ Log.w(TAG, "AclStateChangedHandler: device is null");
+ return;
+ }
+
+ // Avoid to notify Settings UI for Hearing Aid sub device.
+ if (mDeviceManager.isSubDevice(device)) {
+ return;
+ }
+
final String action = intent.getAction();
if (action == null) {
Log.w(TAG, "AclStateChangedHandler: action is null");
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 1bffff7..e28c894 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -116,8 +116,8 @@
void onProfileStateChanged(LocalBluetoothProfile profile, int newProfileState) {
if (BluetoothUtils.D) {
- Log.d(TAG, "onProfileStateChanged: profile " + profile +
- " newProfileState " + newProfileState);
+ Log.d(TAG, "onProfileStateChanged: profile " + profile + ", device=" + mDevice
+ + ", newProfileState " + newProfileState);
}
if (mLocalAdapter.getState() == BluetoothAdapter.STATE_TURNING_OFF)
{
@@ -570,7 +570,7 @@
}
if (BluetoothUtils.D) {
- Log.e(TAG, "updating profiles for " + mDevice.getAliasName());
+ Log.e(TAG, "updating profiles for " + mDevice.getAliasName() + ", " + mDevice);
BluetoothClass bluetoothClass = mDevice.getBluetoothClass();
if (bluetoothClass != null) Log.v(TAG, "Class: " + bluetoothClass.toString());
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 3a62838..5b4a8b4f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -127,6 +127,25 @@
}
/**
+ * Search for existing sub device {@link CachedBluetoothDevice}.
+ *
+ * @param device the address of the Bluetooth device
+ * @return true for found sub device or false.
+ */
+ public synchronized boolean isSubDevice(BluetoothDevice device) {
+ for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
+ if (!cachedDevice.getDevice().equals(device)) {
+ // Check sub devices if it exists
+ CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
+ if (subDevice != null && subDevice.getDevice().equals(device)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
* Updates the Hearing Aid devices; specifically the HiSyncId's. This routine is called when the
* Hearing Aid Service is connected and the HiSyncId's are now available.
* @param LocalBluetoothProfileManager profileManager
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 6c536f0..b1c4f14 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -53,7 +53,7 @@
*
* @return true if the MediaDevice is be connected to transfer, false otherwise.
*/
- protected boolean isConnected() {
+ public boolean isConnected() {
return mIsConnected;
}
diff --git a/packages/SettingsLib/tests/integ/Android.mk b/packages/SettingsLib/tests/integ/Android.mk
index c893b6d..4a814df 100644
--- a/packages/SettingsLib/tests/integ/Android.mk
+++ b/packages/SettingsLib/tests/integ/Android.mk
@@ -31,8 +31,8 @@
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 e8e0b41..da808dd 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 d7ee618..b5d0947 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 d0ab46a..50f5b9d 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.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 3fa2ce5..a436cb5 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.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 dddfa7a..08484bc 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,19 +1,7 @@
package com.android.settingslib.graph;
-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 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;
@@ -21,6 +9,21 @@
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 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;
+
@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 93b038e..9962e1c 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.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 e591d8c..f1c0beae 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 54510b2..46557d3 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 @@
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.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 ee03d50..37f2600 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 0ec75ec..ff8dbda 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 03247999..fc3034e 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.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.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 1860b31..42eb0b9 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.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.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/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index 27b8dfc..0dcdaed 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -18,6 +18,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -154,4 +155,30 @@
verify(mBluetoothCallback).onAclConnectionStateChanged(mCachedBluetoothDevice,
BluetoothAdapter.STATE_CONNECTED);
}
+
+ @Test
+ public void dispatchAclConnectionStateChanged_aclDisconnected_shouldNotCallbackSubDevice() {
+ when(mCachedDeviceManager.isSubDevice(mBluetoothDevice)).thenReturn(true);
+ mBluetoothEventManager.registerCallback(mBluetoothCallback);
+ mIntent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+ mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
+ mContext.sendBroadcast(mIntent);
+
+ verify(mBluetoothCallback, never()).onAclConnectionStateChanged(mCachedBluetoothDevice,
+ BluetoothAdapter.STATE_DISCONNECTED);
+ }
+
+ @Test
+ public void dispatchAclConnectionStateChanged_aclConnected_shouldNotCallbackSubDevice() {
+ when(mCachedDeviceManager.isSubDevice(mBluetoothDevice)).thenReturn(true);
+ mBluetoothEventManager.registerCallback(mBluetoothCallback);
+ mIntent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
+ mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
+ mContext.sendBroadcast(mIntent);
+
+ verify(mBluetoothCallback, never()).onAclConnectionStateChanged(mCachedBluetoothDevice,
+ BluetoothAdapter.STATE_CONNECTED);
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 47b1210..43b2894 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -334,6 +335,27 @@
}
/**
+ * Test to verify isSubDevice_validSubDevice().
+ */
+ @Test
+ public void isSubDevice_validSubDevice() {
+ doReturn(HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
+ mCachedDeviceManager.addDevice(mDevice1);
+
+ // Both device are not sub device in default value.
+ assertThat(mCachedDeviceManager.isSubDevice(mDevice1)).isFalse();
+ assertThat(mCachedDeviceManager.isSubDevice(mDevice2)).isFalse();
+
+ // Add Device-2 as sub device of Device-1 with same HiSyncId.
+ doReturn(HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice2);
+ mCachedDeviceManager.addDevice(mDevice2);
+
+ // Verify Device-2 is sub device, but Device-1 is not.
+ assertThat(mCachedDeviceManager.isSubDevice(mDevice2)).isTrue();
+ assertThat(mCachedDeviceManager.isSubDevice(mDevice1)).isFalse();
+ }
+
+ /**
* Test to verify updateHearingAidsDevices().
*/
@Test
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 371c3d4..375b45c 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 @@
private BarView mBarView2;
private BarView mBarView3;
private BarView mBarView4;
+ private TextView mDetailsView;
private PreferenceViewHolder mHolder;
private BarChartPreference mPreference;
@@ -51,14 +52,13 @@
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 @@
}
@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
@@ -208,4 +213,18 @@
assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mBarView1.getSummary()).isEqualTo(mContext.getText(R.string.debug_app));
}
+
+ @Test
+ public void setAllBarViewsInfo_setClickListenerForBarView_barViewAttachClickListener() {
+ final BarViewInfo viewInfo = new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app);
+ viewInfo.setClickListener(v -> {
+ });
+ final BarViewInfo[] barViewsInfo = new BarViewInfo[]{viewInfo};
+
+ mPreference.setAllBarViewsInfo(barViewsInfo);
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView1.hasOnClickListeners()).isTrue();
+ }
}
diff --git a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
index 8e8d9f7..abe82a8 100644
--- a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
+++ b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
@@ -18,7 +18,7 @@
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 @@
// 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 8bfa1c2..7751bb1 100644
--- a/packages/SystemUI/docs/dagger.md
+++ b/packages/SystemUI/docs/dagger.md
@@ -36,7 +36,8 @@
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
@@ -147,12 +148,53 @@
FragmentHostManager.get(view).create(NavigationBarFragment.class);
```
+### Using injection with Views
+
+Generally, you shouldn't need to inject for a view, as the view should
+be relatively self contained and logic that requires injection should be
+moved to a higher level construct such as a Fragment or a top-level SystemUI
+component, see above for how to do injection for both of which.
+
+Still here? Yeah, ok, sysui has a lot of pre-existing views that contain a
+lot of code that could benefit from injection and will need to be migrated
+off from Dependency#get uses. Similar to how fragments are injected, the view
+needs to be added to the interface
+com.android.systemui.util.InjectionInflationController$ViewInstanceCreator.
+
+```java
+public interface ViewInstanceCreator {
++ QuickStatusBarHeader createQsHeader();
+}
+```
+
+Presumably you need to inflate that view from XML (otherwise why do you
+need anything special? see earlier sections about generic injection). To obtain
+an inflater that supports injected objects, call InjectionInflationController#injectable,
+which will wrap the inflater it is passed in one that can create injected
+objects when needed.
+
+```java
+@Override
+public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+ Bundle savedInstanceState) {
+ return mInjectionInflater.injectable(inflater).inflate(R.layout.my_layout, container, false);
+}
+```
+
+There is one other important thing to note about injecting with views. SysUI
+already has a Context in its global dagger component, so if you simply inject
+a Context, you will not get the one that the view should have with proper
+theming. Because of this, always ensure to tag views that have @Inject with
+the @Named view context.
+
+```java
+public CustomView(@Named(VIEW_CONTEXT) Context themedViewContext, AttributeSet attrs,
+ OtherCustomDependency something) {
+ ...
+}
+```
+
## 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 6135aeb..ac69043 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 @@
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/proguard.flags b/packages/SystemUI/proguard.flags
index d57fe8a..22b0ab7 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -31,4 +31,7 @@
-keep class com.android.systemui.fragments.FragmentService$FragmentCreator {
*;
}
+-keep class com.android.systemui.util.InjectionInflationController$ViewInstanceCreator {
+ *;
+}
-keep class androidx.core.app.CoreComponentFactory
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 ed18dc7..4e0cbe0 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 2674f07c..75c0ec3 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 07375ad..c94438f 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 0000000..c0a1d89
--- /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 22a23a8..570d351 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -35,7 +35,11 @@
/**
* 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 @@
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 @@
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 @@
}
/**
+ * 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 @@
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/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index e051317..583dac7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -28,6 +28,7 @@
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
+import android.view.DisplayInfo;
import android.view.View;
import android.view.WindowManager;
@@ -45,6 +46,7 @@
private final Context mContext;
private boolean mShowing;
+ private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
private final SparseArray<Presentation> mPresentations = new SparseArray<>();
@@ -86,6 +88,22 @@
mDisplayService.registerDisplayListener(mDisplayListener, null /* handler */);
}
+ private boolean isKeyguardShowable(Display display) {
+ if (display == null) {
+ if (DEBUG) Log.i(TAG, "Cannot show Keyguard on null display");
+ return false;
+ }
+ if (display.getDisplayId() == DEFAULT_DISPLAY) {
+ if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on the default display");
+ return false;
+ }
+ display.getDisplayInfo(mTmpDisplayInfo);
+ if ((mTmpDisplayInfo.flags & Display.FLAG_PRIVATE) != 0) {
+ if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on a private display");
+ return false;
+ }
+ return true;
+ }
/**
* @param display The display to show the presentation on.
* @return {@code true} if a presentation was added.
@@ -93,7 +111,7 @@
* was already there.
*/
private boolean showPresentation(Display display) {
- if (display == null || display.getDisplayId() == DEFAULT_DISPLAY) return false;
+ if (!isKeyguardShowable(display)) return false;
if (DEBUG) Log.i(TAG, "Keyguard enabled on display: " + display);
final int displayId = display.getDisplayId();
Presentation presentation = mPresentations.get(displayId);
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
index 9e7c5ba..b461f69 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -19,14 +19,22 @@
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 445d156..f51004a 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -56,6 +56,8 @@
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationFilter;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationRowBinder;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -218,6 +220,7 @@
@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;
@@ -256,6 +259,8 @@
@Inject Lazy<NotificationLogger> mNotificationLogger;
@Inject Lazy<NotificationViewHierarchyManager> mNotificationViewHierarchyManager;
@Inject Lazy<NotificationRowBinder> mNotificationRowBinder;
+ @Inject Lazy<NotificationFilter> mNotificationFilter;
+ @Inject Lazy<NotificationInterruptionStateProvider> mNotificationInterruptionStateProvider;
@Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil;
@Inject Lazy<SmartReplyController> mSmartReplyController;
@Inject Lazy<RemoteInputQuickSettingsDisabler> mRemoteInputQuickSettingsDisabler;
@@ -425,6 +430,9 @@
mProviders.put(NotificationViewHierarchyManager.class,
mNotificationViewHierarchyManager::get);
mProviders.put(NotificationRowBinder.class, mNotificationRowBinder::get);
+ mProviders.put(NotificationFilter.class, mNotificationFilter::get);
+ mProviders.put(NotificationInterruptionStateProvider.class,
+ mNotificationInterruptionStateProvider::get);
mProviders.put(KeyguardDismissUtil.class, mKeyguardDismissUtil::get);
mProviders.put(SmartReplyController.class, mSmartReplyController::get);
mProviders.put(RemoteInputQuickSettingsDisabler.class,
@@ -523,10 +531,18 @@
sDependency.destroyDependency(cls, destroy);
}
+ /**
+ * @deprecated see docs/dagger.md
+ */
+ @Deprecated
public static <T> T get(Class<T> cls) {
return sDependency.getDependency(cls);
}
+ /**
+ * @deprecated see docs/dagger.md
+ */
+ @Deprecated
public static <T> T get(DependencyKey<T> cls) {
return sDependency.getDependency(cls);
}
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 0000000..23ef030
--- /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 76336bb..2b521c5 100644
--- a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
@@ -18,13 +18,11 @@
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;
@@ -33,7 +31,6 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.DisplayMetrics;
-import android.util.Log;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
@@ -41,82 +38,16 @@
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;
@@ -166,256 +97,13 @@
@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,
@@ -424,73 +112,8 @@
@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() {
- Log.d("TestTest", "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
@@ -501,19 +124,6 @@
@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));
@@ -522,16 +132,9 @@
@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();
}
@Singleton
@@ -542,30 +145,32 @@
@Singleton
@Provides
- public NotificationRemoteInputManager.Callback provideNotificationRemoteInputManager(
- Context context) {
- return new StatusBarRemoteInputCallback(context);
-
- }
-
- @Singleton
- @Provides
- public AppOpsController provideAppOpsController(Context context,
- @Named(BG_LOOPER_NAME) Looper bgLooper) {
- return new AppOpsControllerImpl(context, bgLooper);
-
- }
-
- @Singleton
- @Provides
- public DisplayNavigationBarController provideDisplayNavigationBarController(Context context,
- @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
- return new DisplayNavigationBarController(context, mainHandler);
- }
-
- @Singleton
- @Provides
public SensorPrivacyManager provideSensorPrivacyManager(Context context) {
return context.getSystemService(SensorPrivacyManager.class);
}
+
+ @Singleton
+ @Provides
+ public LeakDetector provideLeakDetector() {
+ return LeakDetector.create();
+
+ }
+
+ @Singleton
+ @Provides
+ public ColorDisplayController provideColorDisplayController(Context context) {
+ return new ColorDisplayController(context);
+ }
+
+ @Singleton
+ @Provides
+ public PluginManager providePluginManager(Context context) {
+ return new PluginManagerImpl(context, new PluginInitializerImpl());
+ }
+
+ @Singleton
+ @Provides
+ 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 bab472c..ae446dd 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java
@@ -29,9 +29,13 @@
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 @@
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 81d3251..a2dd123 100644
--- a/packages/SystemUI/src/com/android/systemui/InitController.java
+++ b/packages/SystemUI/src/com/android/systemui/InitController.java
@@ -16,10 +16,14 @@
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 @@
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 1a2473e..8bac7c5 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -42,7 +42,7 @@
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
@@ -55,6 +55,7 @@
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.InjectionInflationController;
import com.android.systemui.volume.VolumeDialogComponent;
import java.util.function.Consumer;
@@ -198,6 +199,13 @@
return new NotificationListener(context);
}
+ @Singleton
+ @Provides
+ public NotificationInterruptionStateProvider provideNotificationInterruptionStateProvider(
+ Context context) {
+ return new NotificationInterruptionStateProvider(context);
+ }
+
@Module
protected static class ContextHolder {
private Context mContext;
@@ -213,7 +221,8 @@
}
@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();
@@ -223,5 +232,10 @@
*/
@Singleton
FragmentService.FragmentCreator createFragmentCreator();
+
+ /**
+ * ViewCreator generates all Views that need injection.
+ */
+ InjectionInflationController.ViewCreator createViewCreator();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java b/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java
index 82fd9b3..a726b42 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.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 52d1260..e3bcb37 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.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 @@
@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};
+ 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
+ };
- static {
- int numOps = OPS_STRING.length;
- OPS = new int[numOps];
- for (int i = 0; i < numOps; i++) {
- OPS[i] = AppOpsManager.strOpToOp(OPS_STRING[i]);
- }
- }
-
- 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 @@
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 @@
}
@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 3c8a461d..fdf18ce 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.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 @@
// 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 201c7e6..9a8a6d0 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.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 java.util.Date;
import java.util.Locale;
+import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
/**
@@ -100,21 +103,28 @@
@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 @@
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 @@
}
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 b6fce44..834bca5 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 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 @@
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 59c7f23..52a0214 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 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 @@
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 03daa95..6949531 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.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 b722f9f..50dda1c 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.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 @@
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 bbdae29..3991c19 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 @@
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 519df19..2894621 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 @@
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 85e99f0..9252167 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 @@
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 @@
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 a87d634..d5b807d 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.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 @@
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 @@
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 8903a38..aba9bb8 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 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/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index fa775c0..bedba9a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -18,7 +18,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.app.Fragment;
+import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
@@ -45,10 +45,12 @@
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
+import com.android.systemui.util.InjectionInflationController;
+import com.android.systemui.util.LifecycleFragment;
import javax.inject.Inject;
-public class QSFragment extends Fragment implements QS, CommandQueue.Callbacks {
+public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Callbacks {
private static final String TAG = "QS";
private static final boolean DEBUG = false;
private static final String EXTRA_EXPANDED = "expanded";
@@ -76,16 +78,23 @@
private boolean mQsDisabled;
private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
+ private final InjectionInflationController mInjectionInflater;
@Inject
- public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler) {
+ public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
+ InjectionInflationController injectionInflater,
+ Context context) {
mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
+ mInjectionInflater = injectionInflater;
+ SysUiServiceProvider.getComponent(context, CommandQueue.class)
+ .observe(getLifecycle(), this);
}
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
Bundle savedInstanceState) {
- inflater = inflater.cloneInContext(new ContextThemeWrapper(getContext(), R.style.qs_theme));
+ inflater = mInjectionInflater.injectable(
+ inflater.cloneInContext(new ContextThemeWrapper(getContext(), R.style.qs_theme)));
return inflater.inflate(R.layout.qs_panel, container, false);
}
@@ -113,13 +122,6 @@
mQSPanel.getTileLayout().restoreInstanceState(savedInstanceState);
}
}
- SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallback(this);
- }
-
- @Override
- public void onDestroyView() {
- SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallback(this);
- super.onDestroyView();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 3cecff0..bec027f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -17,6 +17,8 @@
import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
+import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.ColorInt;
@@ -58,7 +60,6 @@
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
-import com.android.systemui.Dependency;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
@@ -84,6 +85,9 @@
import java.util.Locale;
import java.util.Objects;
+import javax.inject.Inject;
+import javax.inject.Named;
+
/**
* View that contains the top-most bits of the screen (primarily the status bar with date, time, and
* battery) and also contains the {@link QuickQSPanel} along with some of the panel's inner
@@ -102,6 +106,11 @@
public static final int MAX_TOOLTIP_SHOWN_COUNT = 2;
private final Handler mHandler = new Handler();
+ private final BatteryController mBatteryController;
+ private final NextAlarmController mAlarmController;
+ private final ZenModeController mZenController;
+ private final StatusBarIconController mStatusBarIconController;
+ private final ActivityStarter mActivityStarter;
private QSPanel mQsPanel;
@@ -141,8 +150,6 @@
private TextView mBatteryRemainingText;
private boolean mShowBatteryPercentAndEstimate;
- private NextAlarmController mAlarmController;
- private ZenModeController mZenController;
private PrivacyItemController mPrivacyItemController;
/** Counts how many times the long press tooltip has been shown to the user. */
private int mShownCount;
@@ -172,10 +179,17 @@
}
};
- public QuickStatusBarHeader(Context context, AttributeSet attrs) {
+ @Inject
+ public QuickStatusBarHeader(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
+ NextAlarmController nextAlarmController, ZenModeController zenModeController,
+ BatteryController batteryController, StatusBarIconController statusBarIconController,
+ ActivityStarter activityStarter) {
super(context, attrs);
- mAlarmController = Dependency.get(NextAlarmController.class);
- mZenController = Dependency.get(ZenModeController.class);
+ mAlarmController = nextAlarmController;
+ mZenController = zenModeController;
+ mBatteryController = batteryController;
+ mStatusBarIconController = statusBarIconController;
+ mActivityStarter = activityStarter;
mPrivacyItemController = new PrivacyItemController(context, mPICCallback);
mShownCount = getStoredShownCount();
}
@@ -405,15 +419,13 @@
if (!mShowBatteryPercentAndEstimate) {
return;
}
- mBatteryRemainingText.setText(
- Dependency.get(BatteryController.class).getEstimatedTimeRemainingString());
+ mBatteryRemainingText.setText(mBatteryController.getEstimatedTimeRemainingString());
}
public void setExpanded(boolean expanded) {
if (mExpanded == expanded) return;
mExpanded = expanded;
mHeaderQsPanel.setExpanded(expanded);
- updateEverything();
}
/**
@@ -472,7 +484,7 @@
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
- Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager);
+ mStatusBarIconController.addIconGroup(mIconManager);
requestApplyInsets();
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mPercentSettingObserver,
@@ -515,7 +527,7 @@
@VisibleForTesting
public void onDetachedFromWindow() {
setListening(false);
- Dependency.get(StatusBarIconController.class).removeIconGroup(mIconManager);
+ mStatusBarIconController.removeIconGroup(mIconManager);
mContext.getContentResolver().unregisterContentObserver(mPercentSettingObserver);
super.onDetachedFromWindow();
}
@@ -544,10 +556,10 @@
@Override
public void onClick(View v) {
if (v == mClockView) {
- Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent(
+ mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
AlarmClock.ACTION_SHOW_ALARMS),0);
} else if (v == mBatteryMeterView) {
- Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent(
+ mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
Intent.ACTION_POWER_USAGE_SUMMARY),0);
} else if (v == mPrivacyChip) {
Handler mUiHandler = new Handler(Looper.getMainLooper());
@@ -685,10 +697,6 @@
.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/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 9d2be39..4dcacd4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -87,6 +87,8 @@
if (current != null) {
// The setting QS_TILES is not populated immediately upon Factory Reset
possibleTiles.addAll(Arrays.asList(current.split(",")));
+ } else {
+ current = "";
}
String[] stockSplit = stock.split(",");
for (String spec : stockSplit) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 12b6f67..538e0f0 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.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 @@
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 2f26109..78172f1 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 @@
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 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 @@
/** 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 7d1b640..158430f 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 @@
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.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 @@
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 @@
* //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 @@
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/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index ee0d1a2..c945afd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -15,7 +15,6 @@
*/
package com.android.systemui.statusbar;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationRowBinder;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -29,7 +28,6 @@
*/
public interface NotificationPresenter extends ExpandableNotificationRow.OnExpandClickListener,
ActivatableNotificationView.OnActivatedListener,
- NotificationEntryManager.Callback,
NotificationRowBinder.BindRowCallback {
/**
* Returns true if the presenter is not visible. For example, it may not be necessary to do
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 2524747..017a9c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -25,7 +25,6 @@
import android.view.View;
import android.view.ViewGroup;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.statusbar.notification.NotificationData;
@@ -44,6 +43,8 @@
import javax.inject.Inject;
import javax.inject.Singleton;
+import dagger.Lazy;
+
/**
* NotificationViewHierarchyManager manages updating the view hierarchy of notification views based
* on their group structure. For example, if a notification becomes bundled with another,
@@ -60,20 +61,15 @@
mTmpChildOrderMap = new HashMap<>();
// Dependencies:
- protected final NotificationLockscreenUserManager mLockscreenUserManager =
- Dependency.get(NotificationLockscreenUserManager.class);
- protected final NotificationGroupManager mGroupManager =
- Dependency.get(NotificationGroupManager.class);
- protected final VisualStabilityManager mVisualStabilityManager =
- Dependency.get(VisualStabilityManager.class);
- private final StatusBarStateController mStatusBarStateController =
- Dependency.get(StatusBarStateController.class);
- private final NotificationEntryManager mEntryManager =
- Dependency.get(NotificationEntryManager.class);
- private final BubbleController mBubbleController = Dependency.get(BubbleController.class);
+ protected final NotificationLockscreenUserManager mLockscreenUserManager;
+ protected final NotificationGroupManager mGroupManager;
+ protected final VisualStabilityManager mVisualStabilityManager;
+ private final StatusBarStateController mStatusBarStateController;
+ private final NotificationEntryManager mEntryManager;
+ private final BubbleController mBubbleController;
// Lazy
- private ShadeController mShadeController;
+ private final Lazy<ShadeController> mShadeController;
/**
* {@code true} if notifications not part of a group should by default be rendered in their
@@ -120,21 +116,27 @@
}
}
- private ShadeController getShadeController() {
- if (mShadeController == null) {
- mShadeController = Dependency.get(ShadeController.class);
- }
- return mShadeController;
- }
-
@Inject
- public NotificationViewHierarchyManager(Context context) {
+ public NotificationViewHierarchyManager(Context context,
+ NotificationLockscreenUserManager notificationLockscreenUserManager,
+ NotificationGroupManager groupManager,
+ VisualStabilityManager visualStabilityManager,
+ StatusBarStateController statusBarStateController,
+ NotificationEntryManager notificationEntryManager,
+ BubbleController bubbleController,
+ Lazy<ShadeController> shadeController) {
+ mLockscreenUserManager = notificationLockscreenUserManager;
+ mGroupManager = groupManager;
+ mVisualStabilityManager = visualStabilityManager;
+ mStatusBarStateController = statusBarStateController;
+ mEntryManager = notificationEntryManager;
+ mBubbleController = bubbleController;
+ mShadeController = shadeController;
Resources res = context.getResources();
mAlwaysExpandNonGroupedNotification =
res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);
mStatusBarStateListener = new StatusBarStateListener(mBubbleController);
- mEntryManager.setStatusBarStateListener(mStatusBarStateListener);
- Dependency.get(StatusBarStateController.class).addCallback(mStatusBarStateListener);
+ mStatusBarStateController.addCallback(mStatusBarStateListener);
}
public void setUpWithPresenter(NotificationPresenter presenter,
@@ -397,7 +399,7 @@
&& !row.isLowPriority()));
}
- entry.getRow().setOnAmbient(getShadeController().isDozing());
+ entry.getRow().setOnAmbient(mShadeController.get().isDozing());
int userId = entry.notification.getUserId();
boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
entry.notification) && !entry.isRowRemoved();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
index 6560f8f..442416f 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.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 @@
}
};
+ /**
+ */
+ @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 f899863..e1b231b 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.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 @@
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/notification/AlertTransferListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AlertTransferListener.java
deleted file mode 100644
index 13e991b..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AlertTransferListener.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.statusbar.notification;
-
-/**
- * Listener interface for when NotificationEntryManager needs to tell
- * NotificationGroupAlertTransferHelper things. Will eventually grow to be a general-purpose
- * listening interface for the NotificationEntryManager.
- */
-public interface AlertTransferListener {
- /**
- * Called when a new notification is posted. At this point, the notification is "pending": its
- * views haven't been inflated yet and most of the system pretends like it doesn't exist yet.
- */
- void onPendingEntryAdded(NotificationData.Entry entry);
-
- /**
- * Called when an existing notification's views are reinflated (usually due to an update being
- * posted to that notification).
- */
- void onEntryReinflated(NotificationData.Entry entry);
-
- /**
- * Called when a notification has been removed (either because the user swiped it away or
- * because the developer retracted it).
- */
- void onEntryRemoved(NotificationData.Entry entry);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index ef7e0fe..433a994 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -27,20 +27,15 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
-import android.Manifest;
import android.annotation.NonNull;
-import android.app.AppGlobals;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Person;
import android.content.Context;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.Parcelable;
-import android.os.RemoteException;
import android.os.SystemClock;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
@@ -58,17 +53,13 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Dependency;
-import com.android.systemui.ForegroundServiceController;
import com.android.systemui.statusbar.InflationTask;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.io.PrintWriter;
@@ -83,14 +74,13 @@
*/
public class NotificationData {
+ private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class);
+
/**
* These dependencies are late init-ed
*/
private KeyguardEnvironment mEnvironment;
- private ShadeController mShadeController;
private NotificationMediaManager mMediaManager;
- private ForegroundServiceController mFsc;
- private NotificationLockscreenUserManager mUserManager;
private HeadsUpManager mHeadsUpManager;
@@ -120,6 +110,9 @@
@NonNull
public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
public CharSequence[] smartReplies = new CharSequence[0];
+ @VisibleForTesting
+ public int suppressedVisualEffects;
+ public boolean suspended;
private Entry parent; // our parent (if we're in a group)
private ArrayList<Entry> children = new ArrayList<Entry>();
@@ -183,6 +176,8 @@
smartReplies = ranking.getSmartReplies() == null
? new CharSequence[0]
: ranking.getSmartReplies().toArray(new CharSequence[0]);
+ suppressedVisualEffects = ranking.getSuppressedVisualEffects();
+ suspended = ranking.isSuspended();
}
public void setInterruption() {
@@ -625,6 +620,71 @@
if (row == null) return true;
return row.canViewBeDismissed();
}
+
+ boolean isExemptFromDndVisualSuppression() {
+ if (isNotificationBlockedByPolicy(notification.getNotification())) {
+ return false;
+ }
+
+ if ((notification.getNotification().flags
+ & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+ return true;
+ }
+ if (notification.getNotification().isMediaNotification()) {
+ return true;
+ }
+ if (mIsSystemNotification != null && mIsSystemNotification) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean shouldSuppressVisualEffect(int effect) {
+ if (isExemptFromDndVisualSuppression()) {
+ return false;
+ }
+ return (suppressedVisualEffects & effect) != 0;
+ }
+
+ /**
+ * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_FULL_SCREEN_INTENT}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressFullScreenIntent() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
+ }
+
+ /**
+ * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_PEEK}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressPeek() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_PEEK);
+ }
+
+ /**
+ * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_STATUS_BAR}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressStatusBar() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_STATUS_BAR);
+ }
+
+ /**
+ * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_AMBIENT}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressAmbient() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_AMBIENT);
+ }
+
+ /**
+ * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_NOTIFICATION_LIST}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressNotificationList() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_NOTIFICATION_LIST);
+ }
}
private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
@@ -706,13 +766,6 @@
return mEnvironment;
}
- private ShadeController getShadeController() {
- if (mShadeController == null) {
- mShadeController = Dependency.get(ShadeController.class);
- }
- return mShadeController;
- }
-
private NotificationMediaManager getMediaManager() {
if (mMediaManager == null) {
mMediaManager = Dependency.get(NotificationMediaManager.class);
@@ -720,20 +773,6 @@
return mMediaManager;
}
- private ForegroundServiceController getFsc() {
- if (mFsc == null) {
- mFsc = Dependency.get(ForegroundServiceController.class);
- }
- return mFsc;
- }
-
- private NotificationLockscreenUserManager getUserManager() {
- if (mUserManager == null) {
- mUserManager = Dependency.get(NotificationLockscreenUserManager.class);
- }
- return mUserManager;
- }
-
/**
* Returns the sorted list of active notifications (depending on {@link KeyguardEnvironment}
*
@@ -778,7 +817,7 @@
}
public Entry remove(String key, RankingMap ranking) {
- Entry removed = null;
+ Entry removed;
synchronized (mEntries) {
removed = mEntries.remove(key);
}
@@ -849,62 +888,12 @@
return Ranking.VISIBILITY_NO_OVERRIDE;
}
- public boolean shouldSuppressFullScreenIntent(Entry entry) {
- return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
- }
-
- public boolean shouldSuppressPeek(Entry entry) {
- return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_PEEK);
- }
-
- public boolean shouldSuppressStatusBar(Entry entry) {
- return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_STATUS_BAR);
- }
-
- public boolean shouldSuppressAmbient(Entry entry) {
- return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_AMBIENT);
- }
-
- public boolean shouldSuppressNotificationList(Entry entry) {
- return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_NOTIFICATION_LIST);
- }
-
- private boolean shouldSuppressVisualEffect(Entry entry, int effect) {
- if (isExemptFromDndVisualSuppression(entry)) {
- return false;
- }
- String key = entry.key;
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return (mTmpRanking.getSuppressedVisualEffects() & effect) != 0;
- }
- return false;
- }
-
- protected boolean isExemptFromDndVisualSuppression(Entry entry) {
- if (isNotificationBlockedByPolicy(entry.notification.getNotification())) {
- return false;
- }
-
- if ((entry.notification.getNotification().flags
- & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
- return true;
- }
- if (entry.notification.getNotification().isMediaNotification()) {
- return true;
- }
- if (entry.mIsSystemNotification != null && entry.mIsSystemNotification) {
- return true;
- }
- return false;
- }
-
/**
* Categories that are explicitly called out on DND settings screens are always blocked, if
* DND has flagged them, even if they are foreground or system notifications that might
* otherwise visually bypass DND.
*/
- protected boolean isNotificationBlockedByPolicy(Notification n) {
+ private static boolean isNotificationBlockedByPolicy(Notification n) {
if (isCategory(CATEGORY_CALL, n)
|| isCategory(CATEGORY_MESSAGE, n)
|| isCategory(CATEGORY_ALARM, n)
@@ -915,7 +904,7 @@
return false;
}
- private boolean isCategory(String category, Notification n) {
+ private static boolean isCategory(String category, Notification n) {
return Objects.equals(n.category, category);
}
@@ -1013,7 +1002,7 @@
for (int i = 0; i < N; i++) {
Entry entry = mEntries.valueAt(i);
- if (shouldFilterOut(entry)) {
+ if (mNotificationFilter.shouldFilterOut(entry)) {
continue;
}
@@ -1024,87 +1013,6 @@
Collections.sort(mSortedAndFiltered, mRankingComparator);
}
- /**
- * @return true if this notification should NOT be shown right now
- */
- public boolean shouldFilterOut(Entry entry) {
- final StatusBarNotification sbn = entry.notification;
- if (!(getEnvironment().isDeviceProvisioned() ||
- showNotificationEvenIfUnprovisioned(sbn))) {
- return true;
- }
-
- if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
- return true;
- }
-
- if (getUserManager().isLockscreenPublicMode(sbn.getUserId()) &&
- (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
- || getUserManager().shouldHideNotifications(sbn.getUserId())
- || getUserManager().shouldHideNotifications(sbn.getKey()))) {
- return true;
- }
-
- if (getShadeController().isDozing() && shouldSuppressAmbient(entry)) {
- return true;
- }
-
- if (!getShadeController().isDozing() && shouldSuppressNotificationList(entry)) {
- return true;
- }
-
- if (shouldHide(sbn.getKey())) {
- return true;
- }
-
- if (!StatusBar.ENABLE_CHILD_NOTIFICATIONS
- && mGroupManager.isChildInGroupWithSummary(sbn)) {
- return true;
- }
-
- if (getFsc().isDungeonNotification(sbn)
- && !getFsc().isDungeonNeededForUser(sbn.getUserId())) {
- // this is a foreground-service disclosure for a user that does not need to show one
- return true;
- }
- if (getFsc().isSystemAlertNotification(sbn)) {
- final String[] apps = sbn.getNotification().extras.getStringArray(
- Notification.EXTRA_FOREGROUND_APPS);
- if (apps != null && apps.length >= 1) {
- if (!getFsc().isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- // Q: What kinds of notifications should show during setup?
- // A: Almost none! Only things coming from packages with permission
- // android.permission.NOTIFICATION_DURING_SETUP that also have special "kind" tags marking them
- // as relevant for setup (see below).
- public static boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
- return showNotificationEvenIfUnprovisioned(AppGlobals.getPackageManager(), sbn);
- }
-
- @VisibleForTesting
- static boolean showNotificationEvenIfUnprovisioned(IPackageManager packageManager,
- StatusBarNotification sbn) {
- return checkUidPermission(packageManager, Manifest.permission.NOTIFICATION_DURING_SETUP,
- sbn.getUid()) == PackageManager.PERMISSION_GRANTED
- && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
- }
-
- private static int checkUidPermission(IPackageManager packageManager, String permission,
- int uid) {
- try {
- return packageManager.checkUidPermission(permission, uid);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
public void dump(PrintWriter pw, String indent) {
int N = mSortedAndFiltered.size();
pw.print(indent);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
new file mode 100644
index 0000000..361ae8b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification;
+
+import android.service.notification.StatusBarNotification;
+
+/**
+ * Listener interface for changes sent by NotificationEntryManager.
+ */
+public interface NotificationEntryListener {
+ /**
+ * Called when a new notification is posted. At this point, the notification is "pending": its
+ * views haven't been inflated yet and most of the system pretends like it doesn't exist yet.
+ */
+ default void onPendingEntryAdded(NotificationData.Entry entry) {
+ }
+
+ /**
+ * Called when a new entry is created.
+ */
+ default void onNotificationAdded(NotificationData.Entry entry) {
+ }
+
+ /**
+ * Called when a notification was updated.
+ */
+ default void onNotificationUpdated(StatusBarNotification notification) {
+ }
+
+ /**
+ * Called when an existing notification's views are reinflated (usually due to an update being
+ * posted to that notification).
+ */
+ default void onEntryReinflated(NotificationData.Entry entry) {
+ }
+
+ /**
+ * Called when a notification has been removed (either because the user swiped it away or
+ * because the developer retracted it).
+ *
+ * TODO: combine this with onNotificationRemoved().
+ */
+ default void onEntryRemoved(NotificationData.Entry entry) {
+ }
+
+ /**
+ * Called when a notification was removed.
+ *
+ * @param key key of notification that was removed
+ * @param old StatusBarNotification of the notification before it was removed
+ */
+ default void onNotificationRemoved(String key, StatusBarNotification old) {
+ }
+
+ /**
+ * Removes a notification immediately.
+ *
+ * TODO: combine this with onNotificationRemoved().
+ */
+ default void onPerformRemoveNotification(StatusBarNotification statusBarNotification) {
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 535ea62..98ddd6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -17,43 +17,30 @@
import static com.android.systemui.bubbles.BubbleController.DEBUG_DEMOTE_TO_NOTIF;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
-import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
import android.annotation.Nullable;
import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
import android.content.Context;
-import android.database.ContentObserver;
-import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
-import android.provider.Settings;
-import android.service.dreams.DreamService;
-import android.service.dreams.IDreamManager;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.EventLog;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
-import com.android.systemui.EventLogTags;
import com.android.systemui.ForegroundServiceController;
-import com.android.systemui.UiOffloadThread;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.AmbientPulseManager;
@@ -64,7 +51,6 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.NotificationUpdateHandler;
-import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -97,8 +83,6 @@
BubbleController.BubbleDismissListener {
private static final String TAG = "NotificationEntryMgr";
protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final boolean ENABLE_HEADS_UP = true;
- private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
public static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
@@ -109,7 +93,6 @@
Dependency.get(NotificationGroupManager.class);
private final NotificationGutsManager mGutsManager =
Dependency.get(NotificationGutsManager.class);
- private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private final DeviceProvisionedController mDeviceProvisionedController =
Dependency.get(DeviceProvisionedController.class);
private final VisualStabilityManager mVisualStabilityManager =
@@ -119,6 +102,8 @@
private final AmbientPulseManager mAmbientPulseManager =
Dependency.get(AmbientPulseManager.class);
private final BubbleController mBubbleController = Dependency.get(BubbleController.class);
+ private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+ Dependency.get(NotificationInterruptionStateProvider.class);
// Lazily retrieved dependencies
private NotificationRemoteInputManager mRemoteInputManager;
@@ -130,23 +115,18 @@
private final Handler mDeferredNotificationViewUpdateHandler;
private Runnable mUpdateNotificationViewsCallback;
- protected IDreamManager mDreamManager;
protected IStatusBarService mBarService;
private NotificationPresenter mPresenter;
- private Callback mCallback;
+ private NotificationEntryListener mCallback;
protected PowerManager mPowerManager;
private NotificationListenerService.RankingMap mLatestRankingMap;
protected HeadsUpManager mHeadsUpManager;
protected NotificationData mNotificationData;
- private ContentObserver mHeadsUpObserver;
- protected boolean mUseHeadsUp = false;
- private boolean mDisableNotificationAlerts;
protected NotificationListContainer mListContainer;
@VisibleForTesting
final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
= new ArrayList<>();
- private NotificationViewHierarchyManager.StatusBarStateListener mStatusBarStateListener;
- @Nullable private AlertTransferListener mAlertTransferListener;
+ private final List<NotificationEntryListener> mNotificationEntryListeners = new ArrayList<>();
private final DeviceProvisionedController.DeviceProvisionedListener
mDeviceProvisionedListener =
@@ -157,11 +137,6 @@
}
};
- public void setDisableNotificationAlerts(boolean disableNotificationAlerts) {
- mDisableNotificationAlerts = disableNotificationAlerts;
- mHeadsUpObserver.onChange(true);
- }
-
public void destroy() {
mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener);
}
@@ -177,8 +152,6 @@
pw.println(entry.notification);
}
}
- pw.print(" mUseHeadsUp=");
- pw.println(mUseHeadsUp);
}
public NotificationEntryManager(Context context) {
@@ -186,15 +159,14 @@
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
- mDreamManager = IDreamManager.Stub.asInterface(
- ServiceManager.checkService(DreamService.DREAM_SERVICE));
mBubbleController.setDismissListener(this /* bubbleEventListener */);
mNotificationData = new NotificationData();
mDeferredNotificationViewUpdateHandler = new Handler();
}
- public void setAlertTransferListener(AlertTransferListener listener) {
- mAlertTransferListener = listener;
+ /** Adds a {@link NotificationEntryListener}. */
+ public void addNotificationEntryListener(NotificationEntryListener listener) {
+ mNotificationEntryListeners.add(listener);
}
/**
@@ -236,7 +208,7 @@
}
public void setUpWithPresenter(NotificationPresenter presenter,
- NotificationListContainer listContainer, Callback callback,
+ NotificationListContainer listContainer, NotificationEntryListener callback,
HeadsUpManager headsUpManager) {
mPresenter = presenter;
mUpdateNotificationViewsCallback = mPresenter::updateNotificationViews;
@@ -245,36 +217,6 @@
mNotificationData.setHeadsUpManager(mHeadsUpManager);
mListContainer = listContainer;
- mHeadsUpObserver = new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) {
- @Override
- public void onChange(boolean selfChange) {
- boolean wasUsing = mUseHeadsUp;
- mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts
- && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
- mContext.getContentResolver(),
- Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
- Settings.Global.HEADS_UP_OFF);
- Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
- if (wasUsing != mUseHeadsUp) {
- if (!mUseHeadsUp) {
- Log.d(TAG,
- "dismissing any existing heads up notification on disable event");
- mHeadsUpManager.releaseAllImmediately();
- }
- }
- }
- };
-
- if (ENABLE_HEADS_UP) {
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED),
- true,
- mHeadsUpObserver);
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
- mHeadsUpObserver);
- }
-
mNotificationLifetimeExtenders.add(mHeadsUpManager);
mNotificationLifetimeExtenders.add(mAmbientPulseManager);
mNotificationLifetimeExtenders.add(mGutsManager);
@@ -285,20 +227,6 @@
}
mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
-
- mHeadsUpObserver.onChange(true); // set up
-
- getRowBinder().setInterruptionStateProvider(new InterruptionStateProvider() {
- @Override
- public boolean shouldHeadsUp(NotificationData.Entry entry) {
- return NotificationEntryManager.this.shouldHeadsUp(entry);
- }
-
- @Override
- public boolean shouldPulse(NotificationData.Entry entry) {
- return NotificationEntryManager.this.shouldPulse(entry);
- }
- });
}
public NotificationData getNotificationData() {
@@ -322,14 +250,6 @@
updateNotifications();
}
- private boolean shouldSuppressFullScreenIntent(NotificationData.Entry entry) {
- if (mPresenter.isDeviceInVrMode()) {
- return true;
- }
-
- return mNotificationData.shouldSuppressFullScreenIntent(entry);
- }
-
public void performRemoveNotification(StatusBarNotification n) {
final int rank = mNotificationData.getRank(n.getKey());
final int count = mNotificationData.getActiveNotifications().size();
@@ -441,7 +361,7 @@
if ((inflatedFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
// Possible for shouldHeadsUp to change between the inflation starting and ending.
// If it does and we no longer need to heads up, we should free the view.
- if (shouldHeadsUp(entry)) {
+ if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
mHeadsUpManager.showNotification(entry);
// Mark as seen immediately
setNotificationShown(entry.notification);
@@ -450,7 +370,7 @@
}
}
if ((inflatedFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
- if (shouldPulse(entry)) {
+ if (mNotificationInterruptionStateProvider.shouldPulse(entry)) {
mAmbientPulseManager.showNotification(entry);
} else {
entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
@@ -474,8 +394,8 @@
mVisualStabilityManager.onLowPriorityUpdated(entry);
mPresenter.updateNotificationViews();
}
- if (mAlertTransferListener != null) {
- mAlertTransferListener.onEntryReinflated(entry);
+ for (NotificationEntryListener listener : mNotificationEntryListeners) {
+ listener.onEntryReinflated(entry);
}
}
}
@@ -492,8 +412,10 @@
final NotificationData.Entry entry = mNotificationData.get(key);
abortExistingInflation(key);
- if (mAlertTransferListener != null && entry != null) {
- mAlertTransferListener.onEntryRemoved(entry);
+ if (entry != null) {
+ for (NotificationEntryListener listener : mNotificationEntryListeners) {
+ listener.onEntryRemoved(entry);
+ }
}
// Attempt to remove notifications from their alert managers (heads up, ambient pulse).
@@ -650,51 +572,15 @@
mNotificationData.updateRanking(rankingMap);
NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
rankingMap.getRanking(key, ranking);
- NotificationData.Entry shadeEntry = createNotificationEntry(notification, ranking);
- boolean isHeadsUped = shouldHeadsUp(shadeEntry);
- if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
- if (shouldSuppressFullScreenIntent(shadeEntry)) {
- if (DEBUG) {
- Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + key);
- }
- } else if (mNotificationData.getImportance(key)
- < NotificationManager.IMPORTANCE_HIGH) {
- if (DEBUG) {
- Log.d(TAG, "No Fullscreen intent: not important enough: "
- + key);
- }
- } else {
- // Stop screensaver if the notification has a fullscreen intent.
- // (like an incoming phone call)
- Dependency.get(UiOffloadThread.class).submit(() -> {
- try {
- mDreamManager.awaken();
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- });
-
- // not immersive & a fullscreen alert should be shown
- if (DEBUG)
- Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
- try {
- EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
- key);
- notification.getNotification().fullScreenIntent.send();
- shadeEntry.notifyFullScreenIntentLaunched();
- mMetricsLogger.count("note_fullscreen", 1);
- } catch (PendingIntent.CanceledException e) {
- }
- }
- }
+ NotificationData.Entry entry = createNotificationEntry(notification, ranking);
abortExistingInflation(key);
mForegroundServiceController.addNotification(notification,
mNotificationData.getImportance(key));
- mPendingNotifications.put(key, shadeEntry);
- if (mAlertTransferListener != null) {
- mAlertTransferListener.onPendingEntryAdded(shadeEntry);
+ mPendingNotifications.put(key, entry);
+ for (NotificationEntryListener listener : mNotificationEntryListeners) {
+ listener.onPendingEntryAdded(entry);
}
}
@@ -767,9 +653,11 @@
boolean alertAgain = alertAgain(entry, entry.notification.getNotification());
if (getShadeController().isDozing()) {
- updateAlertState(entry, shouldPulse(entry), alertAgain, mAmbientPulseManager);
+ updateAlertState(entry, mNotificationInterruptionStateProvider.shouldPulse(entry),
+ alertAgain, mAmbientPulseManager);
} else {
- updateAlertState(entry, shouldHeadsUp(entry), alertAgain, mHeadsUpManager);
+ updateAlertState(entry, mNotificationInterruptionStateProvider.shouldHeadsUp(entry),
+ alertAgain, mHeadsUpManager);
}
updateNotifications();
@@ -828,7 +716,7 @@
// By comparing the old and new UI adjustments, reinflate the view accordingly.
for (NotificationData.Entry entry : entries) {
- mNotificationRowBinder.onNotificationRankingUpdated(
+ getRowBinder().onNotificationRankingUpdated(
entry,
oldImportances.get(entry.key),
oldAdjustments.get(entry.key),
@@ -851,182 +739,6 @@
}
}
- public void setStatusBarStateListener(
- NotificationViewHierarchyManager.StatusBarStateListener listener) {
- mStatusBarStateListener = listener;
- }
-
- /**
- * Whether the notification should peek in from the top and alert the user.
- *
- * @param entry the entry to check
- * @return true if the entry should heads up, false otherwise
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
- public boolean shouldHeadsUp(NotificationData.Entry entry) {
- StatusBarNotification sbn = entry.notification;
-
- if (getShadeController().isDozing()) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: device is dozing: " + sbn.getKey());
- }
- return false;
- }
-
- // TODO: need to changes this, e.g. should still heads up in expanded shade, might want
- // message bubble from the bubble to go through heads up path
- boolean inShade = mStatusBarStateListener != null
- && mStatusBarStateListener.getCurrentState() == SHADE;
- if (entry.isBubble() && !entry.isBubbleDismissed() && inShade) {
- return false;
- }
-
- if (!canAlertCommon(entry)) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: notification shouldn't alert: " + sbn.getKey());
- }
- return false;
- }
-
- if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: no huns or vr mode");
- }
- return false;
- }
-
- boolean isDreaming = false;
- try {
- isDreaming = mDreamManager.isDreaming();
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to query dream manager.", e);
- }
- boolean inUse = mPowerManager.isScreenOn() && !isDreaming;
-
- if (!inUse) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: not in use: " + sbn.getKey());
- }
- return false;
- }
-
- if (mNotificationData.shouldSuppressPeek(entry)) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: suppressed by DND: " + sbn.getKey());
- }
- return false;
- }
-
- if (isSnoozedPackage(sbn)) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: snoozed package: " + sbn.getKey());
- }
- return false;
- }
-
- if (entry.hasJustLaunchedFullScreenIntent()) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: recent fullscreen: " + sbn.getKey());
- }
- return false;
- }
-
- if (mNotificationData.getImportance(sbn.getKey()) < NotificationManager.IMPORTANCE_HIGH) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: unimportant notification: " + sbn.getKey());
- }
- return false;
- }
-
- if (!mCallback.canHeadsUp(entry, sbn)) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Whether or not the notification should "pulse" on the user's display when the phone is
- * dozing. This displays the ambient view of the notification.
- *
- * @param entry the entry to check
- * @return true if the entry should ambient pulse, false otherwise
- */
- private boolean shouldPulse(NotificationData.Entry entry) {
- StatusBarNotification sbn = entry.notification;
-
- if (!getShadeController().isDozing()) {
- if (DEBUG) {
- Log.d(TAG, "No pulsing: not dozing: " + sbn.getKey());
- }
- return false;
- }
-
- if (!canAlertCommon(entry)) {
- if (DEBUG) {
- Log.d(TAG, "No pulsing: notification shouldn't alert: " + sbn.getKey());
- }
- return false;
- }
-
- if (mNotificationData.shouldSuppressAmbient(entry)) {
- if (DEBUG) {
- Log.d(TAG, "No pulsing: ambient effect suppressed: " + sbn.getKey());
- }
- return false;
- }
-
- if (mNotificationData.getImportance(sbn.getKey())
- < NotificationManager.IMPORTANCE_DEFAULT) {
- if (DEBUG) {
- Log.d(TAG, "No pulsing: not important enough: " + sbn.getKey());
- }
- return false;
- }
-
- Bundle extras = sbn.getNotification().extras;
- CharSequence title = extras.getCharSequence(Notification.EXTRA_TITLE);
- CharSequence text = extras.getCharSequence(Notification.EXTRA_TEXT);
- if (TextUtils.isEmpty(title) && TextUtils.isEmpty(text)) {
- if (DEBUG) {
- Log.d(TAG, "No pulsing: title and text are empty: " + sbn.getKey());
- }
- return false;
- }
-
- return true;
- }
-
- /**
- * Common checks between heads up alerting and ambient pulse alerting. See
- * {@link NotificationEntryManager#shouldHeadsUp(NotificationData.Entry)} and
- * {@link NotificationEntryManager#shouldPulse(NotificationData.Entry)}. Notifications that
- * fail any of these checks should not alert at all.
- *
- * @param entry the entry to check
- * @return true if these checks pass, false if the notification should not alert
- */
- protected boolean canAlertCommon(NotificationData.Entry entry) {
- StatusBarNotification sbn = entry.notification;
-
- if (mNotificationData.shouldFilterOut(entry)) {
- if (DEBUG) {
- Log.d(TAG, "No alerting: filtered notification: " + sbn.getKey());
- }
- return false;
- }
-
- // Don't alert notifications that are suppressed due to group alert behavior
- if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
- if (DEBUG) {
- Log.d(TAG, "No alerting: suppressed due to group alert behavior");
- }
- return false;
- }
-
- return true;
- }
-
private void setNotificationShown(StatusBarNotification n) {
setNotificationsShown(new String[]{n.getKey()});
}
@@ -1039,10 +751,6 @@
}
}
- private boolean isSnoozedPackage(StatusBarNotification sbn) {
- return mHeadsUpManager.isSnoozed(sbn.getPackageName());
- }
-
/**
* Update the entry's alert state and call the appropriate {@link AlertingNotificationManager}
* method.
@@ -1076,64 +784,4 @@
public Iterable<NotificationData.Entry> getPendingNotificationsIterator() {
return mPendingNotifications.values();
}
-
- /**
- * Interface for retrieving heads-up and pulsing state for an entry.
- */
- public interface InterruptionStateProvider {
- /**
- * Whether the provided entry should be marked as heads-up when inflated.
- */
- boolean shouldHeadsUp(NotificationData.Entry entry);
-
- /**
- * Whether the provided entry should be marked as pulsing (displayed in ambient) when
- * inflated.
- */
- boolean shouldPulse(NotificationData.Entry entry);
- }
-
- /**
- * Callback for NotificationEntryManager.
- */
- public interface Callback {
-
- /**
- * Called when a new entry is created.
- *
- * @param shadeEntry entry that was created
- */
- void onNotificationAdded(NotificationData.Entry shadeEntry);
-
- /**
- * Called when a notification was updated.
- *
- * @param notification notification that was updated
- */
- void onNotificationUpdated(StatusBarNotification notification);
-
- /**
- * Called when a notification was removed.
- *
- * @param key key of notification that was removed
- * @param old StatusBarNotification of the notification before it was removed
- */
- void onNotificationRemoved(String key, StatusBarNotification old);
-
- /**
- * Removes a notification immediately.
- *
- * @param statusBarNotification notification that is being removed
- */
- void onPerformRemoveNotification(StatusBarNotification statusBarNotification);
-
- /**
- * Returns true if NotificationEntryManager can heads up this notification.
- *
- * @param entry entry of the notification that might be heads upped
- * @param sbn notification that might be heads upped
- * @return true if the notification can be heads upped
- */
- boolean canHeadsUp(NotificationData.Entry entry, StatusBarNotification sbn);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
new file mode 100644
index 0000000..5e99c38
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import android.Manifest;
+import android.app.AppGlobals;
+import android.app.Notification;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.service.notification.StatusBarNotification;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** Component which manages the various reasons a notification might be filtered out. */
+@Singleton
+public class NotificationFilter {
+
+ private final NotificationGroupManager mGroupManager = Dependency.get(
+ NotificationGroupManager.class);
+
+ private NotificationData.KeyguardEnvironment mEnvironment;
+ private ShadeController mShadeController;
+ private ForegroundServiceController mFsc;
+ private NotificationLockscreenUserManager mUserManager;
+
+ @Inject
+ public NotificationFilter() {}
+
+ private NotificationData.KeyguardEnvironment getEnvironment() {
+ if (mEnvironment == null) {
+ mEnvironment = Dependency.get(NotificationData.KeyguardEnvironment.class);
+ }
+ return mEnvironment;
+ }
+
+ private ShadeController getShadeController() {
+ if (mShadeController == null) {
+ mShadeController = Dependency.get(ShadeController.class);
+ }
+ return mShadeController;
+ }
+
+ private ForegroundServiceController getFsc() {
+ if (mFsc == null) {
+ mFsc = Dependency.get(ForegroundServiceController.class);
+ }
+ return mFsc;
+ }
+
+ private NotificationLockscreenUserManager getUserManager() {
+ if (mUserManager == null) {
+ mUserManager = Dependency.get(NotificationLockscreenUserManager.class);
+ }
+ return mUserManager;
+ }
+
+
+ /**
+ * @return true if the provided notification should NOT be shown right now.
+ */
+ public boolean shouldFilterOut(NotificationData.Entry entry) {
+ final StatusBarNotification sbn = entry.notification;
+ if (!(getEnvironment().isDeviceProvisioned()
+ || showNotificationEvenIfUnprovisioned(sbn))) {
+ return true;
+ }
+
+ if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
+ return true;
+ }
+
+ if (getUserManager().isLockscreenPublicMode(sbn.getUserId())
+ && (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
+ || getUserManager().shouldHideNotifications(sbn.getUserId())
+ || getUserManager().shouldHideNotifications(sbn.getKey()))) {
+ return true;
+ }
+
+ if (getShadeController().isDozing() && entry.shouldSuppressAmbient()) {
+ return true;
+ }
+
+ if (!getShadeController().isDozing() && entry.shouldSuppressNotificationList()) {
+ return true;
+ }
+
+ if (entry.suspended) {
+ return true;
+ }
+
+ if (!StatusBar.ENABLE_CHILD_NOTIFICATIONS
+ && mGroupManager.isChildInGroupWithSummary(sbn)) {
+ return true;
+ }
+
+ if (getFsc().isDungeonNotification(sbn)
+ && !getFsc().isDungeonNeededForUser(sbn.getUserId())) {
+ // this is a foreground-service disclosure for a user that does not need to show one
+ return true;
+ }
+ if (getFsc().isSystemAlertNotification(sbn)) {
+ final String[] apps = sbn.getNotification().extras.getStringArray(
+ Notification.EXTRA_FOREGROUND_APPS);
+ if (apps != null && apps.length >= 1) {
+ if (!getFsc().isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // Q: What kinds of notifications should show during setup?
+ // A: Almost none! Only things coming from packages with permission
+ // android.permission.NOTIFICATION_DURING_SETUP that also have special "kind" tags marking them
+ // as relevant for setup (see below).
+ private static boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
+ return showNotificationEvenIfUnprovisioned(AppGlobals.getPackageManager(), sbn);
+ }
+
+ @VisibleForTesting
+ static boolean showNotificationEvenIfUnprovisioned(IPackageManager packageManager,
+ StatusBarNotification sbn) {
+ return checkUidPermission(packageManager, Manifest.permission.NOTIFICATION_DURING_SETUP,
+ sbn.getUid()) == PackageManager.PERMISSION_GRANTED
+ && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
+ }
+
+ private static int checkUidPermission(IPackageManager packageManager, String permission,
+ int uid) {
+ try {
+ return packageManager.checkUidPermission(permission, uid);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
new file mode 100644
index 0000000..8bd0e9a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import static com.android.systemui.statusbar.StatusBarState.SHADE;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
+import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+/**
+ * Provides heads-up and pulsing state for notification entries.
+ */
+public class NotificationInterruptionStateProvider {
+
+ private static final String TAG = "InterruptionStateProvider";
+ private static final boolean DEBUG = false;
+ private static final boolean ENABLE_HEADS_UP = true;
+ private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
+
+ private final StatusBarStateController mStatusBarStateController =
+ Dependency.get(StatusBarStateController.class);
+ private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class);
+
+ private final Context mContext;
+ private final PowerManager mPowerManager;
+ private final IDreamManager mDreamManager;
+
+ private NotificationPresenter mPresenter;
+ private ShadeController mShadeController;
+ private HeadsUpManager mHeadsUpManager;
+ private HeadsUpSuppressor mHeadsUpSuppressor;
+
+ private ContentObserver mHeadsUpObserver;
+ @VisibleForTesting
+ protected boolean mUseHeadsUp = false;
+ private boolean mDisableNotificationAlerts;
+
+ public NotificationInterruptionStateProvider(Context context) {
+ this(context,
+ (PowerManager) context.getSystemService(Context.POWER_SERVICE),
+ IDreamManager.Stub.asInterface(
+ ServiceManager.checkService(DreamService.DREAM_SERVICE)));
+ }
+
+ @VisibleForTesting
+ protected NotificationInterruptionStateProvider(
+ Context context,
+ PowerManager powerManager,
+ IDreamManager dreamManager) {
+ mContext = context;
+ mPowerManager = powerManager;
+ mDreamManager = dreamManager;
+ }
+
+ /** Sets up late-binding dependencies for this component. */
+ public void setUpWithPresenter(
+ NotificationPresenter notificationPresenter,
+ HeadsUpManager headsUpManager,
+ HeadsUpSuppressor headsUpSuppressor) {
+ mPresenter = notificationPresenter;
+ mHeadsUpManager = headsUpManager;
+ mHeadsUpSuppressor = headsUpSuppressor;
+
+ mHeadsUpObserver = new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) {
+ @Override
+ public void onChange(boolean selfChange) {
+ boolean wasUsing = mUseHeadsUp;
+ mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts
+ && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
+ Settings.Global.HEADS_UP_OFF);
+ Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
+ if (wasUsing != mUseHeadsUp) {
+ if (!mUseHeadsUp) {
+ Log.d(TAG,
+ "dismissing any existing heads up notification on disable event");
+ mHeadsUpManager.releaseAllImmediately();
+ }
+ }
+ }
+ };
+
+ if (ENABLE_HEADS_UP) {
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED),
+ true,
+ mHeadsUpObserver);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
+ mHeadsUpObserver);
+ }
+ mHeadsUpObserver.onChange(true); // set up
+ }
+
+ private ShadeController getShadeController() {
+ if (mShadeController == null) {
+ mShadeController = Dependency.get(ShadeController.class);
+ }
+ return mShadeController;
+ }
+
+ /**
+ * Whether the notification should peek in from the top and alert the user.
+ *
+ * @param entry the entry to check
+ * @return true if the entry should heads up, false otherwise
+ */
+ public boolean shouldHeadsUp(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+
+ if (getShadeController().isDozing()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: device is dozing: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ // TODO: need to changes this, e.g. should still heads up in expanded shade, might want
+ // message bubble from the bubble to go through heads up path
+ boolean inShade = mStatusBarStateController.getState() == SHADE;
+ if (entry.isBubble() && !entry.isBubbleDismissed() && inShade) {
+ return false;
+ }
+
+ if (!canAlertCommon(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: notification shouldn't alert: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: no huns or vr mode");
+ }
+ return false;
+ }
+
+ boolean isDreaming = false;
+ try {
+ isDreaming = mDreamManager.isDreaming();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to query dream manager.", e);
+ }
+ boolean inUse = mPowerManager.isScreenOn() && !isDreaming;
+
+ if (!inUse) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: not in use: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (entry.shouldSuppressPeek()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: suppressed by DND: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (isSnoozedPackage(sbn)) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: snoozed package: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (entry.hasJustLaunchedFullScreenIntent()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: recent fullscreen: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (entry.importance < NotificationManager.IMPORTANCE_HIGH) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: unimportant notification: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (!mHeadsUpSuppressor.canHeadsUp(entry, sbn)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Whether or not the notification should "pulse" on the user's display when the phone is
+ * dozing. This displays the ambient view of the notification.
+ *
+ * @param entry the entry to check
+ * @return true if the entry should ambient pulse, false otherwise
+ */
+ public boolean shouldPulse(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+
+ if (!getShadeController().isDozing()) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: not dozing: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (!canAlertCommon(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: notification shouldn't alert: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (entry.shouldSuppressAmbient()) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: ambient effect suppressed: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (entry.importance < NotificationManager.IMPORTANCE_DEFAULT) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: not important enough: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ Bundle extras = sbn.getNotification().extras;
+ CharSequence title = extras.getCharSequence(Notification.EXTRA_TITLE);
+ CharSequence text = extras.getCharSequence(Notification.EXTRA_TEXT);
+ if (TextUtils.isEmpty(title) && TextUtils.isEmpty(text)) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: title and text are empty: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Common checks between heads up alerting and ambient pulse alerting. See
+ * {@link #shouldHeadsUp(NotificationData.Entry)} and
+ * {@link #shouldPulse(NotificationData.Entry)}. Notifications that fail any of these checks
+ * should not alert at all.
+ *
+ * @param entry the entry to check
+ * @return true if these checks pass, false if the notification should not alert
+ */
+ protected boolean canAlertCommon(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+
+ if (mNotificationFilter.shouldFilterOut(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No alerting: filtered notification: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ // Don't alert notifications that are suppressed due to group alert behavior
+ if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
+ if (DEBUG) {
+ Log.d(TAG, "No alerting: suppressed due to group alert behavior");
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean isSnoozedPackage(StatusBarNotification sbn) {
+ return mHeadsUpManager.isSnoozed(sbn.getPackageName());
+ }
+
+ /** Sets whether to disable all alerts. */
+ public void setDisableNotificationAlerts(boolean disableNotificationAlerts) {
+ mDisableNotificationAlerts = disableNotificationAlerts;
+ mHeadsUpObserver.onChange(true);
+ }
+
+ protected NotificationPresenter getPresenter() {
+ return mPresenter;
+ }
+
+ /** A component which can suppress heads-up notifications due to the overall state of the UI. */
+ public interface HeadsUpSuppressor {
+ /**
+ * Returns false if the provided notification is ineligible for heads-up according to this
+ * component.
+ *
+ * @param entry entry of the notification that might be heads upped
+ * @param sbn notification that might be heads upped
+ * @return false if the notification can not be heads upped
+ */
+ boolean canHeadsUp(NotificationData.Entry entry, StatusBarNotification sbn);
+
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
index 824bd81..b241b8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
@@ -64,6 +64,8 @@
private final NotificationGutsManager mGutsManager =
Dependency.get(NotificationGutsManager.class);
private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
+ private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+ Dependency.get(NotificationInterruptionStateProvider.class);
private final Context mContext;
private final IStatusBarService mBarService;
@@ -79,7 +81,6 @@
private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
private BindRowCallback mBindRowCallback;
private NotificationClicker mNotificationClicker;
- private NotificationEntryManager.InterruptionStateProvider mInterruptionStateProvider;
@Inject
public NotificationRowBinder(Context context) {
@@ -116,11 +117,6 @@
mNotificationClicker = clicker;
}
- public void setInterruptionStateProvider(
- NotificationEntryManager.InterruptionStateProvider interruptionStateProvider) {
- mInterruptionStateProvider = interruptionStateProvider;
- }
-
/**
* Inflates the views for the given entry (possibly asynchronously).
*/
@@ -253,10 +249,10 @@
row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
row.setEntry(entry);
- if (mInterruptionStateProvider.shouldHeadsUp(entry)) {
+ if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, true /* shouldInflate */);
}
- if (mInterruptionStateProvider.shouldPulse(entry)) {
+ if (mNotificationInterruptionStateProvider.shouldPulse(entry)) {
row.updateInflationFlag(FLAG_CONTENT_VIEW_AMBIENT, true /* shouldInflate */);
}
row.setNeedsRedaction(
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 5906dc2..5b44a77 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 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 @@
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 7c30e48..e156e77 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 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 @@
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 7876aa5..dd07ec4 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 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 7621887..1281953 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.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 0f8d59b..fbd8d8a 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.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 @@
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 9ecee18..b4f850b 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 @@
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/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index 6732bbe..b1f74c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -29,8 +29,8 @@
import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.AlertTransferListener;
import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.row.NotificationInflater.AsyncInflationTask;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
@@ -95,7 +95,7 @@
// not being up to date.
mEntryManager = entryManager;
- mEntryManager.setAlertTransferListener(mAlertTransferListener);
+ mEntryManager.addNotificationEntryListener(mNotificationEntryListener);
groupManager.addOnGroupChangeListener(mOnGroupChangeListener);
}
@@ -186,7 +186,8 @@
}
}
- private final AlertTransferListener mAlertTransferListener = new AlertTransferListener() {
+ private final NotificationEntryListener mNotificationEntryListener =
+ new NotificationEntryListener() {
// Called when a new notification has been posted but is not inflated yet. We use this to
// see as early as we can if we need to abort a transfer.
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 2d5d562..e40835f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -207,7 +207,7 @@
}
// showAmbient == show in shade but not shelf
- if (!showAmbient && mEntryManager.getNotificationData().shouldSuppressStatusBar(entry)) {
+ if (!showAmbient && entry.shouldSuppressStatusBar()) {
return false;
}
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 c7e4d34..c0909e3 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 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 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 @@
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 ee1eb42..d6f2fd7 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.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 @@
* 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 @@
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 @@
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 @@
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 @@
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/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 1b43f8f..008c21d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -191,6 +191,7 @@
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationData.Entry;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationRowBinder;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -377,6 +378,7 @@
private NotificationGutsManager mGutsManager;
protected NotificationLogger mNotificationLogger;
protected NotificationEntryManager mEntryManager;
+ private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
private NotificationRowBinder mNotificationRowBinder;
protected NotificationViewHierarchyManager mViewHierarchyManager;
protected ForegroundServiceController mForegroundServiceController;
@@ -622,6 +624,8 @@
mGutsManager = Dependency.get(NotificationGutsManager.class);
mMediaManager = Dependency.get(NotificationMediaManager.class);
mEntryManager = Dependency.get(NotificationEntryManager.class);
+ mNotificationInterruptionStateProvider =
+ Dependency.get(NotificationInterruptionStateProvider.class);
mNotificationRowBinder = Dependency.get(NotificationRowBinder.class);
mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class);
mForegroundServiceController = Dependency.get(ForegroundServiceController.class);
@@ -1413,7 +1417,7 @@
}
if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
- mEntryManager.setDisableNotificationAlerts(
+ mNotificationInterruptionStateProvider.setDisableNotificationAlerts(
(state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 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 4f25349..db7589d 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.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 @@
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/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index c93d151..8d1b911 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -24,6 +24,7 @@
import android.app.ActivityTaskManager;
import android.app.KeyguardManager;
import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.TaskStackBuilder;
import android.content.Context;
@@ -33,15 +34,21 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
+import android.util.EventLog;
import android.util.Log;
import android.view.RemoteAnimationAdapter;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.Dependency;
+import com.android.systemui.EventLogTags;
+import com.android.systemui.UiOffloadThread;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.CommandQueue;
@@ -54,7 +61,9 @@
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -66,6 +75,7 @@
public class StatusBarNotificationActivityStarter implements NotificationActivityStarter {
private static final String TAG = "NotificationClickHandler";
+ protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final AssistManager mAssistManager = Dependency.get(AssistManager.class);
private final NotificationGroupManager mGroupManager =
@@ -84,6 +94,9 @@
Dependency.get(NotificationEntryManager.class);
private final StatusBarStateController mStatusBarStateController =
Dependency.get(StatusBarStateController.class);
+ private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+ Dependency.get(NotificationInterruptionStateProvider.class);
+ private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private final Context mContext;
private final NotificationPanelView mNotificationPanel;
@@ -94,6 +107,7 @@
private final ActivityLaunchAnimator mActivityLaunchAnimator;
private final IStatusBarService mBarService;
private final CommandQueue mCommandQueue;
+ private final IDreamManager mDreamManager;
private boolean mIsCollapsingToShowActivityOverLockscreen;
@@ -112,6 +126,15 @@
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mCommandQueue = getComponent(context, CommandQueue.class);
+ mDreamManager = IDreamManager.Stub.asInterface(
+ ServiceManager.checkService(DreamService.DREAM_SERVICE));
+
+ mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
+ @Override
+ public void onPendingEntryAdded(NotificationData.Entry entry) {
+ handleFullScreenIntent(entry);
+ }
+ });
}
/**
@@ -322,6 +345,45 @@
}, null, false /* afterKeyguardGone */);
}
+ private void handleFullScreenIntent(NotificationData.Entry entry) {
+ boolean isHeadsUped = mNotificationInterruptionStateProvider.shouldHeadsUp(entry);
+ if (!isHeadsUped && entry.notification.getNotification().fullScreenIntent != null) {
+ if (shouldSuppressFullScreenIntent(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + entry.key);
+ }
+ } else if (entry.importance < NotificationManager.IMPORTANCE_HIGH) {
+ if (DEBUG) {
+ Log.d(TAG, "No Fullscreen intent: not important enough: " + entry.key);
+ }
+ } else {
+ // Stop screensaver if the notification has a fullscreen intent.
+ // (like an incoming phone call)
+ Dependency.get(UiOffloadThread.class).submit(() -> {
+ try {
+ mDreamManager.awaken();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ });
+
+ // not immersive & a fullscreen alert should be shown
+ if (DEBUG) {
+ Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
+ }
+ try {
+ EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
+ entry.key);
+ entry.notification.getNotification().fullScreenIntent.send();
+ entry.notifyFullScreenIntentLaunched();
+ mMetricsLogger.count("note_fullscreen", 1);
+ } catch (PendingIntent.CanceledException e) {
+ // ignore
+ }
+ }
+ }
+ }
+
@Override
public boolean isCollapsingToShowActivityOverLockscreen() {
return mIsCollapsingToShowActivityOverLockscreen;
@@ -351,6 +413,14 @@
|| !mActivityLaunchAnimator.isAnimationPending();
}
+ private boolean shouldSuppressFullScreenIntent(NotificationData.Entry entry) {
+ if (mPresenter.isDeviceInVrMode()) {
+ return true;
+ }
+
+ return entry.shouldSuppressFullScreenIntent();
+ }
+
private void removeNotification(StatusBarNotification notification) {
// We have to post it to the UI thread for synchronization
Dependency.get(MAIN_HANDLER).post(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index c8c9ebe5..d643f07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -57,7 +57,9 @@
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationRowBinder;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -91,6 +93,8 @@
Dependency.get(NotificationEntryManager.class);
private final NotificationRowBinder mNotificationRowBinder =
Dependency.get(NotificationRowBinder.class);
+ private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+ Dependency.get(NotificationInterruptionStateProvider.class);
private final NotificationMediaManager mMediaManager =
Dependency.get(NotificationMediaManager.class);
protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
@@ -169,10 +173,37 @@
NotificationListContainer notifListContainer = (NotificationListContainer) stackScroller;
Dependency.get(InitController.class).addPostInitTask(() -> {
+ NotificationEntryListener notificationEntryListener = new NotificationEntryListener() {
+ @Override
+ public void onNotificationAdded(Entry entry) {
+ // Recalculate the position of the sliding windows and the titles.
+ mShadeController.updateAreThereNotifications();
+ }
+
+ @Override
+ public void onNotificationUpdated(StatusBarNotification notification) {
+ mShadeController.updateAreThereNotifications();
+ }
+
+ @Override
+ public void onNotificationRemoved(String key, StatusBarNotification old) {
+ StatusBarNotificationPresenter.this.onNotificationRemoved(key, old);
+ }
+
+ @Override
+ public void onPerformRemoveNotification(
+ StatusBarNotification statusBarNotification) {
+ StatusBarNotificationPresenter.this.onPerformRemoveNotification();
+ }
+ };
+
mViewHierarchyManager.setUpWithPresenter(this, notifListContainer);
- mEntryManager.setUpWithPresenter(this, notifListContainer, this, mHeadsUpManager);
+ mEntryManager.setUpWithPresenter(
+ this, notifListContainer, notificationEntryListener, mHeadsUpManager);
mNotificationRowBinder.setUpWithPresenter(this, notifListContainer, mHeadsUpManager,
mEntryManager, this);
+ mNotificationInterruptionStateProvider.setUpWithPresenter(
+ this, mHeadsUpManager, this::canHeadsUp);
mLockscreenUserManager.setUpWithPresenter(this);
mMediaManager.setUpWithPresenter(this);
Dependency.get(NotificationGutsManager.class).setUpWithPresenter(this,
@@ -222,10 +253,9 @@
|| mActivityLaunchAnimator.isAnimationRunning();
}
- @Override
- public void onPerformRemoveNotification(StatusBarNotification n) {
+ private void onPerformRemoveNotification() {
if (mNotificationPanel.hasPulsingNotifications() &&
- !mAmbientPulseManager.hasNotifications()) {
+ !mAmbientPulseManager.hasNotifications()) {
// We were showing a pulse for a notification, but no notifications are pulsing anymore.
// Finish the pulse.
mDozeScrimController.pulseOutNow();
@@ -249,18 +279,6 @@
mNotificationPanel.updateNotificationViews();
}
- @Override
- public void onNotificationAdded(Entry shadeEntry) {
- // Recalculate the position of the sliding windows and the titles.
- mShadeController.updateAreThereNotifications();
- }
-
- @Override
- public void onNotificationUpdated(StatusBarNotification notification) {
- mShadeController.updateAreThereNotifications();
- }
-
- @Override
public void onNotificationRemoved(String key, StatusBarNotification old) {
if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
@@ -282,7 +300,6 @@
return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
}
- @Override
public boolean canHeadsUp(Entry entry, StatusBarNotification sbn) {
if (mShadeController.isDozing()) {
return false;
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 78f5374..116ecc8 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.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 @@
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 cb6e300..88f9048 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.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 @@
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 cc431dd..ebfdb3f 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.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 @@
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 67da8a5..1395e13 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.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 ddcfbf6..5d61f4a 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.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 @@
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 6f64a563..c855000 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.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 @@
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 ccfe073..c7d337a 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.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 @@
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 f2283a5..c995162 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 java.util.ArrayList;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
public class DeviceProvisionedControllerImpl extends CurrentUserTracker implements
DeviceProvisionedController {
@@ -36,6 +42,9 @@
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 a6146a6..fd030d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
@@ -19,7 +19,6 @@
import android.os.Handler;
import android.util.ArrayMap;
-import com.android.systemui.Dependency;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.plugins.PluginManager;
@@ -34,6 +33,12 @@
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;
@@ -43,9 +48,24 @@
public static final int SORT_ORDER_DEFAULT = 4;
private final Context mDefaultContext;
+ private final LeakDetector mLeakDetector;
+ private final PluginManager mPluginManager;
+ private final TunerService mTunerService;
+ private final ConfigurationController mConfigurationController;
- public ExtensionControllerImpl(Context context) {
+ /**
+ */
+ @Inject
+ public ExtensionControllerImpl(Context context,
+ LeakDetector leakDetector,
+ PluginManager pluginManager,
+ TunerService tunerService,
+ ConfigurationController configurationController) {
mDefaultContext = context;
+ mLeakDetector = leakDetector;
+ mPluginManager = pluginManager;
+ mTunerService = tunerService;
+ mConfigurationController = configurationController;
}
@Override
@@ -159,14 +179,14 @@
@Override
public void clearItem(boolean isDestroyed) {
if (isDestroyed && mItem != null) {
- Dependency.get(LeakDetector.class).trackGarbage(mItem);
+ mLeakDetector.trackGarbage(mItem);
}
mItem = null;
}
private void notifyChanged() {
if (mItem != null) {
- Dependency.get(LeakDetector.class).trackGarbage(mItem);
+ mLeakDetector.trackGarbage(mItem);
}
mItem = null;
for (int i = 0; i < mProducers.size(); i++) {
@@ -207,7 +227,7 @@
public PluginItem(String action, Class<P> cls, PluginConverter<T, P> converter) {
mConverter = converter;
- Dependency.get(PluginManager.class).addPluginListener(action, this, cls);
+ mPluginManager.addPluginListener(action, this, cls);
}
@Override
@@ -235,7 +255,7 @@
@Override
public void destroy() {
- Dependency.get(PluginManager.class).removePluginListener(this);
+ mPluginManager.removePluginListener(this);
}
@Override
@@ -251,7 +271,7 @@
public TunerItem(TunerFactory<T> factory, String... setting) {
mFactory = factory;
- Dependency.get(TunerService.class).addTunable(this, setting);
+ mTunerService.addTunable(this, setting);
}
@Override
@@ -261,7 +281,7 @@
@Override
public void destroy() {
- Dependency.get(TunerService.class).removeTunable(this);
+ mTunerService.removeTunable(this);
}
@Override
@@ -289,7 +309,7 @@
mSupplier = supplier;
mUiMode = mDefaultContext.getResources().getConfiguration().uiMode
& Configuration.UI_MODE_TYPE_MASK;
- Dependency.get(ConfigurationController.class).addCallback(this);
+ mConfigurationController.addCallback(this);
}
@Override
@@ -309,7 +329,7 @@
@Override
public void destroy() {
- Dependency.get(ConfigurationController.class).removeCallback(this);
+ mConfigurationController.removeCallback(this);
}
@Override
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 e23ce2d..41ff9d10 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.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 @@
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 0a72c3f..420abe8 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.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 @@
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 aee021c..ba6369e 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_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 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 @@
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 5eb0fb7..3c8ed6e 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 java.util.ArrayList;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback
implements KeyguardMonitor {
@@ -47,6 +53,9 @@
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 840e77e..683cdbb 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 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 @@
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 bc43120..9422101 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_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 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.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 @@
/**
* 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 dac878c..288b3af 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.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 @@
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 5418dc1..1f368e1 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 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 @@
}
};
+ @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 29a6b95..e0259c9 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.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 @@
// 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 fed8032..0ca6ff6 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 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 @@
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 15b2f2b..e412e09 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.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 @@
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 89ccff0..7198165 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.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 @@
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 e85dee8..81d77a6 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 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 @@
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 196d9bc..0a47f19 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.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 @@
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 88cbbb5..31f4991 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 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 @@
* 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 @@
@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/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
new file mode 100644
index 0000000..e458e63
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util;
+
+import android.content.Context;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+import android.view.InflateException;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.systemui.SystemUIFactory;
+import com.android.systemui.qs.QuickStatusBarHeader;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+
+/**
+ * Manages inflation that requires dagger injection.
+ * See docs/dagger.md for details.
+ */
+@Singleton
+public class InjectionInflationController {
+
+ public static final String VIEW_CONTEXT = "view_context";
+ private final ViewCreator mViewCreator;
+ private final ArrayMap<String, Method> mInjectionMap = new ArrayMap<>();
+ private final LayoutInflater.Factory2 mFactory = new InjectionFactory();
+
+ @Inject
+ public InjectionInflationController(SystemUIFactory.SystemUIRootComponent rootComponent) {
+ mViewCreator = rootComponent.createViewCreator();
+ initInjectionMap();
+ }
+
+ ArrayMap<String, Method> getInjectionMap() {
+ return mInjectionMap;
+ }
+
+ ViewCreator getFragmentCreator() {
+ return mViewCreator;
+ }
+
+ /**
+ * Wraps a {@link LayoutInflater} to support creating dagger injected views.
+ * See docs/dagger.md for details.
+ */
+ public LayoutInflater injectable(LayoutInflater inflater) {
+ LayoutInflater ret = inflater.cloneInContext(inflater.getContext());
+ ret.setPrivateFactory(mFactory);
+ return ret;
+ }
+
+ private void initInjectionMap() {
+ for (Method method : ViewInstanceCreator.class.getDeclaredMethods()) {
+ if (View.class.isAssignableFrom(method.getReturnType())
+ && (method.getModifiers() & Modifier.PUBLIC) != 0) {
+ mInjectionMap.put(method.getReturnType().getName(), method);
+ }
+ }
+ }
+
+ /**
+ * The subcomponent of dagger that holds all views that need injection.
+ */
+ @Subcomponent
+ public interface ViewCreator {
+ /**
+ * Creates another subcomponent to actually generate the view.
+ */
+ ViewInstanceCreator createInstanceCreator(ViewAttributeProvider attributeProvider);
+ }
+
+ /**
+ * Secondary sub-component that actually creates the views.
+ *
+ * Having two subcomponents lets us hide the complexity of providing the named context
+ * and AttributeSet from the SystemUIRootComponent, instead we have one subcomponent that
+ * creates a new ViewInstanceCreator any time we need to inflate a view.
+ */
+ @Subcomponent(modules = ViewAttributeProvider.class)
+ public interface ViewInstanceCreator {
+ /**
+ * Creates the QuickStatusBarHeader.
+ */
+ QuickStatusBarHeader createQsHeader();
+ }
+
+ /**
+ * Module for providing view-specific constructor objects.
+ */
+ @Module
+ public class ViewAttributeProvider {
+ private final Context mContext;
+ private final AttributeSet mAttrs;
+
+ private ViewAttributeProvider(Context context, AttributeSet attrs) {
+ mContext = context;
+ mAttrs = attrs;
+ }
+
+ /**
+ * Provides the view-themed context (as opposed to the global sysui application context).
+ */
+ @Provides
+ @Named(VIEW_CONTEXT)
+ public Context provideContext() {
+ return mContext;
+ }
+
+ /**
+ * Provides the AttributeSet for the current view being inflated.
+ */
+ @Provides
+ public AttributeSet provideAttributeSet() {
+ return mAttrs;
+ }
+ }
+
+ private class InjectionFactory implements LayoutInflater.Factory2 {
+
+ @Override
+ public View onCreateView(String name, Context context, AttributeSet attrs) {
+ Method creationMethod = mInjectionMap.get(name);
+ if (creationMethod != null) {
+ ViewAttributeProvider provider = new ViewAttributeProvider(context, attrs);
+ try {
+ return (View) creationMethod.invoke(
+ mViewCreator.createInstanceCreator(provider));
+ } catch (IllegalAccessException e) {
+ throw new InflateException("Could not inflate " + name, e);
+ } catch (InvocationTargetException e) {
+ throw new InflateException("Could not inflate " + name, e);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+ return onCreateView(name, context, attrs);
+ }
+ }
+}
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 b2cc269..7bc9626 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 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 @@
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 a47e99d..b25df5f 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.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 @@
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 e20e267..a86970b 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.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 @@
*
* 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 @@
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 fb2ceac..4150602 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 @@
}
@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 @@
}
@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 bb44548..2582946 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 @@
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 @@
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 @@
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 @@
@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 c1c80ce..7d94635 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 @@
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.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 @@
@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 @@
@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 @@
}
@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 b23f667..d3b3dae 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 @@
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 563599b..e6d7ee7 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.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 @@
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 @@
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 @@
@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 @@
}
@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/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 39afbac..45e49df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -34,11 +34,13 @@
import com.android.keyguard.CarrierText;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.util.InjectionInflationController;
import org.junit.Before;
import org.junit.Ignore;
@@ -122,6 +124,8 @@
@Override
protected Fragment instantiate(Context context, String className, Bundle arguments) {
- return new QSFragment(new RemoteInputQuickSettingsDisabler(context));
+ return new QSFragment(new RemoteInputQuickSettingsDisabler(context),
+ new InjectionInflationController(SystemUIFactory.getInstance().getRootComponent()),
+ context);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 26fa20d..c3a3e63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -218,4 +218,12 @@
}
assertFalse(specs.contains("other"));
}
+
+ @Test
+ public void testQueryTiles_nullSetting() {
+ Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES, null);
+ mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
+ STOCK_TILES);
+ mTileQueryHelper.queryTiles(mQSTileHost);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
index f8ff583..894ef3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -26,6 +26,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -55,7 +56,8 @@
public class NonPhoneDependencyTest extends SysuiTestCase {
@Mock private NotificationPresenter mPresenter;
@Mock private NotificationListContainer mListContainer;
- @Mock private NotificationEntryManager.Callback mEntryManagerCallback;
+ @Mock
+ private NotificationEntryListener mEntryManagerCallback;
@Mock private HeadsUpManager mHeadsUpManager;
@Mock private RemoteInputController.Delegate mDelegate;
@Mock private NotificationRemoteInputManager.Callback mRemoteInputManagerCallback;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 520a927..8cf4b05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -35,6 +36,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationData.Entry;
@@ -90,7 +92,10 @@
when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
- mViewHierarchyManager = new NotificationViewHierarchyManager(mContext);
+ mViewHierarchyManager = new NotificationViewHierarchyManager(mContext,
+ mLockscreenUserManager, mGroupManager, mVisualStabilityManager,
+ mock(StatusBarStateController.class), mEntryManager, mock(BubbleController.class),
+ () -> mShadeController);
Dependency.get(InitController.class).executePostInitTasks();
mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
index def7513..871ff89 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
@@ -29,8 +29,6 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -50,7 +48,6 @@
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
-import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -105,6 +102,7 @@
com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
MockitoAnnotations.initMocks(this);
when(mMockStatusBarNotification.getUid()).thenReturn(UID_NORMAL);
+ when(mMockStatusBarNotification.cloneLight()).thenReturn(mMockStatusBarNotification);
when(mMockPackageManager.checkUidPermission(
eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
@@ -129,41 +127,6 @@
}
@Test
- @UiThreadTest
- public void testShowNotificationEvenIfUnprovisioned_FalseIfNoExtra() {
- initStatusBarNotification(false);
- when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
-
- assertFalse(
- NotificationData.showNotificationEvenIfUnprovisioned(
- mMockPackageManager,
- mMockStatusBarNotification));
- }
-
- @Test
- @UiThreadTest
- public void testShowNotificationEvenIfUnprovisioned_FalseIfNoPermission() {
- initStatusBarNotification(true);
-
- assertFalse(
- NotificationData.showNotificationEvenIfUnprovisioned(
- mMockPackageManager,
- mMockStatusBarNotification));
- }
-
- @Test
- @UiThreadTest
- public void testShowNotificationEvenIfUnprovisioned_TrueIfHasPermissionAndExtra() {
- initStatusBarNotification(true);
- when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
-
- assertTrue(
- NotificationData.showNotificationEvenIfUnprovisioned(
- mMockPackageManager,
- mMockStatusBarNotification));
- }
-
- @Test
public void testChannelSetWhenAdded() {
mNotificationData.add(mRow.getEntry());
assertEquals(NOTIFICATION_CHANNEL, mRow.getEntry().channel);
@@ -230,76 +193,6 @@
}
@Test
- public void testSuppressSystemAlertNotification() {
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
- when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
- StatusBarNotification sbn = mRow.getEntry().notification;
- Bundle bundle = new Bundle();
- bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {"something"});
- sbn.getNotification().extras = bundle;
-
- assertTrue(mNotificationData.shouldFilterOut(mRow.getEntry()));
- }
-
- @Test
- public void testDoNotSuppressSystemAlertNotification() {
- StatusBarNotification sbn = mRow.getEntry().notification;
- Bundle bundle = new Bundle();
- bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {"something"});
- sbn.getNotification().extras = bundle;
-
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
- when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
-
- assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
-
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
- when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
-
- assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
-
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
- when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
-
- assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
- }
-
- @Test
- public void testDoNotSuppressMalformedSystemAlertNotification() {
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
-
- // missing extra
- assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
-
- StatusBarNotification sbn = mRow.getEntry().notification;
- Bundle bundle = new Bundle();
- bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {});
- sbn.getNotification().extras = bundle;
-
- // extra missing values
- assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
- }
-
- @Test
- public void testShouldFilterHiddenNotifications() {
- initStatusBarNotification(false);
- // setup
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
- when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
-
- // test should filter out hidden notifications:
- // hidden
- when(mMockStatusBarNotification.getKey()).thenReturn(TEST_HIDDEN_NOTIFICATION_KEY);
- NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
- assertTrue(mNotificationData.shouldFilterOut(entry));
-
- // not hidden
- when(mMockStatusBarNotification.getKey()).thenReturn("not hidden");
- entry = new NotificationData.Entry(mMockStatusBarNotification);
- assertFalse(mNotificationData.shouldFilterOut(entry));
- }
-
- @Test
public void testGetNotificationsForCurrentUser_shouldFilterNonCurrentUserNotifications()
throws Exception {
mNotificationData.add(mRow.getEntry());
@@ -325,9 +218,10 @@
Notification n = mMockStatusBarNotification.getNotification();
n.flags = Notification.FLAG_FOREGROUND_SERVICE;
NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+ mNotificationData.add(entry);
- assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
- assertFalse(mNotificationData.shouldSuppressAmbient(entry));
+ assertTrue(entry.isExemptFromDndVisualSuppression());
+ assertFalse(entry.shouldSuppressAmbient());
}
@Test
@@ -341,9 +235,10 @@
n = nb.build();
when(mMockStatusBarNotification.getNotification()).thenReturn(n);
NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+ mNotificationData.add(entry);
- assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
- assertFalse(mNotificationData.shouldSuppressAmbient(entry));
+ assertTrue(entry.isExemptFromDndVisualSuppression());
+ assertFalse(entry.shouldSuppressAmbient());
}
@Test
@@ -353,9 +248,10 @@
TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
entry.mIsSystemNotification = true;
+ mNotificationData.add(entry);
- assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
- assertFalse(mNotificationData.shouldSuppressAmbient(entry));
+ assertTrue(entry.isExemptFromDndVisualSuppression());
+ assertFalse(entry.shouldSuppressAmbient());
}
@Test
@@ -365,31 +261,33 @@
TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
entry.mIsSystemNotification = true;
+ mNotificationData.add(entry);
+
when(mMockStatusBarNotification.getNotification()).thenReturn(
new Notification.Builder(mContext, "").setCategory(CATEGORY_CALL).build());
- assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
- assertTrue(mNotificationData.shouldSuppressAmbient(entry));
+ assertFalse(entry.isExemptFromDndVisualSuppression());
+ assertTrue(entry.shouldSuppressAmbient());
when(mMockStatusBarNotification.getNotification()).thenReturn(
new Notification.Builder(mContext, "").setCategory(CATEGORY_REMINDER).build());
- assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+ assertFalse(entry.isExemptFromDndVisualSuppression());
when(mMockStatusBarNotification.getNotification()).thenReturn(
new Notification.Builder(mContext, "").setCategory(CATEGORY_ALARM).build());
- assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+ assertFalse(entry.isExemptFromDndVisualSuppression());
when(mMockStatusBarNotification.getNotification()).thenReturn(
new Notification.Builder(mContext, "").setCategory(CATEGORY_EVENT).build());
- assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+ assertFalse(entry.isExemptFromDndVisualSuppression());
when(mMockStatusBarNotification.getNotification()).thenReturn(
new Notification.Builder(mContext, "").setCategory(CATEGORY_MESSAGE).build());
- assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+ assertFalse(entry.isExemptFromDndVisualSuppression());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 8fe91cd..701ea7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -103,7 +103,8 @@
@Mock private KeyguardEnvironment mEnvironment;
@Mock private ExpandableNotificationRow mRow;
@Mock private NotificationListContainer mListContainer;
- @Mock private NotificationEntryManager.Callback mCallback;
+ @Mock
+ private NotificationEntryListener mCallback;
@Mock
private NotificationRowBinder.BindRowCallback mBindCallback;
@Mock private HeadsUpManager mHeadsUpManager;
@@ -137,7 +138,6 @@
super(context);
mBarService = barService;
mCountDownLatch = new CountDownLatch(1);
- mUseHeadsUp = true;
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
new file mode 100644
index 0000000..da8bc01d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.app.Notification;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.service.notification.StatusBarNotification;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.ShadeController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class NotificationFilterTest extends SysuiTestCase {
+
+ private static final int UID_NORMAL = 123;
+ private static final int UID_ALLOW_DURING_SETUP = 456;
+ private static final String TEST_HIDDEN_NOTIFICATION_KEY = "testHiddenNotificationKey";
+
+ private final StatusBarNotification mMockStatusBarNotification =
+ mock(StatusBarNotification.class);
+
+ @Mock
+ ForegroundServiceController mFsc;
+ @Mock
+ NotificationData.KeyguardEnvironment mEnvironment;
+ private final IPackageManager mMockPackageManager = mock(IPackageManager.class);
+
+ private NotificationFilter mNotificationFilter;
+ private ExpandableNotificationRow mRow;
+
+ @Before
+ public void setUp() throws Exception {
+ com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ MockitoAnnotations.initMocks(this);
+ when(mMockStatusBarNotification.getUid()).thenReturn(UID_NORMAL);
+
+ when(mMockPackageManager.checkUidPermission(
+ eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
+ eq(UID_NORMAL)))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mMockPackageManager.checkUidPermission(
+ eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
+ eq(UID_ALLOW_DURING_SETUP)))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+ mDependency.injectTestDependency(ForegroundServiceController.class, mFsc);
+ mDependency.injectTestDependency(NotificationGroupManager.class,
+ new NotificationGroupManager());
+ mDependency.injectMockDependency(ShadeController.class);
+ mDependency.injectTestDependency(NotificationData.KeyguardEnvironment.class, mEnvironment);
+ when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
+ when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
+ mRow = new NotificationTestHelper(getContext()).createRow();
+ mNotificationFilter = new NotificationFilter();
+ }
+
+ @Test
+ @UiThreadTest
+ public void testShowNotificationEvenIfUnprovisioned_FalseIfNoExtra() {
+ initStatusBarNotification(false);
+ when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
+
+ assertFalse(
+ NotificationFilter.showNotificationEvenIfUnprovisioned(
+ mMockPackageManager,
+ mMockStatusBarNotification));
+ }
+
+ @Test
+ @UiThreadTest
+ public void testShowNotificationEvenIfUnprovisioned_FalseIfNoPermission() {
+ initStatusBarNotification(true);
+
+ assertFalse(
+ NotificationFilter.showNotificationEvenIfUnprovisioned(
+ mMockPackageManager,
+ mMockStatusBarNotification));
+ }
+
+ @Test
+ @UiThreadTest
+ public void testShowNotificationEvenIfUnprovisioned_TrueIfHasPermissionAndExtra() {
+ initStatusBarNotification(true);
+ when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
+
+ assertTrue(
+ NotificationFilter.showNotificationEvenIfUnprovisioned(
+ mMockPackageManager,
+ mMockStatusBarNotification));
+ }
+
+ @Test
+ public void testSuppressSystemAlertNotification() {
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
+ when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
+ StatusBarNotification sbn = mRow.getEntry().notification;
+ Bundle bundle = new Bundle();
+ bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{"something"});
+ sbn.getNotification().extras = bundle;
+
+ assertTrue(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+ }
+
+ @Test
+ public void testDoNotSuppressSystemAlertNotification() {
+ StatusBarNotification sbn = mRow.getEntry().notification;
+ Bundle bundle = new Bundle();
+ bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{"something"});
+ sbn.getNotification().extras = bundle;
+
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
+ when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
+
+ assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
+ when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
+
+ assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
+ when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
+
+ assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+ }
+
+ @Test
+ public void testDoNotSuppressMalformedSystemAlertNotification() {
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
+
+ // missing extra
+ assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+
+ StatusBarNotification sbn = mRow.getEntry().notification;
+ Bundle bundle = new Bundle();
+ bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{});
+ sbn.getNotification().extras = bundle;
+
+ // extra missing values
+ assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+ }
+
+ @Test
+ public void testShouldFilterHiddenNotifications() {
+ initStatusBarNotification(false);
+ // setup
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
+ when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
+
+ // test should filter out hidden notifications:
+ // hidden
+ NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+ entry.suspended = true;
+ assertTrue(mNotificationFilter.shouldFilterOut(entry));
+
+ // not hidden
+ entry = new NotificationData.Entry(mMockStatusBarNotification);
+ entry.suspended = false;
+ assertFalse(mNotificationFilter.shouldFilterOut(entry));
+ }
+
+ private void initStatusBarNotification(boolean allowDuringSetup) {
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
+ Notification notification = new Notification.Builder(mContext, "test")
+ .addExtras(bundle)
+ .build();
+ when(mMockStatusBarNotification.getNotification()).thenReturn(notification);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index e65e806..b4f99c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -126,7 +126,7 @@
mPowerManager = new PowerManager(mContext, powerManagerService,
Handler.createAsync(Looper.myLooper()));
- mEntryManager = new TestableNotificationEntryManager(mDreamManager, mPowerManager,
+ mEntryManager = new TestableNotificationEntryManager(mPowerManager,
mContext);
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
Dependency.get(InitController.class).executePostInitTasks();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index ee39e10..490288e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -32,9 +32,9 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.AmbientPulseManager;
-import com.android.systemui.statusbar.notification.AlertTransferListener;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -61,8 +61,9 @@
private AmbientPulseManager mAmbientPulseManager;
private HeadsUpManager mHeadsUpManager;
@Mock private NotificationEntryManager mNotificationEntryManager;
- @Captor private ArgumentCaptor<AlertTransferListener> mListenerCaptor;
- private AlertTransferListener mAlertTransferListener;
+ @Captor
+ private ArgumentCaptor<NotificationEntryListener> mListenerCaptor;
+ private NotificationEntryListener mNotificationEntryListener;
private final HashMap<String, Entry> mPendingEntries = new HashMap<>();
private final NotificationGroupTestHelper mGroupTestHelper =
new NotificationGroupTestHelper(mContext);
@@ -85,8 +86,8 @@
mGroupAlertTransferHelper.setHeadsUpManager(mHeadsUpManager);
mGroupAlertTransferHelper.bind(mNotificationEntryManager, mGroupManager);
- verify(mNotificationEntryManager).setAlertTransferListener(mListenerCaptor.capture());
- mAlertTransferListener = mListenerCaptor.getValue();
+ verify(mNotificationEntryManager).addNotificationEntryListener(mListenerCaptor.capture());
+ mNotificationEntryListener = mListenerCaptor.getValue();
mHeadsUpManager.addListener(mGroupAlertTransferHelper);
mAmbientPulseManager.addListener(mGroupAlertTransferHelper);
}
@@ -121,7 +122,7 @@
// Add second child notification so that summary is no longer suppressed.
mPendingEntries.put(childEntry2.key, childEntry2);
- mAlertTransferListener.onPendingEntryAdded(childEntry2);
+ mNotificationEntryListener.onPendingEntryAdded(childEntry2);
mGroupManager.onEntryAdded(childEntry2);
// The alert state should transfer back to the summary as there is now more than one
@@ -148,7 +149,7 @@
// Add second child notification so that summary is no longer suppressed.
mPendingEntries.put(childEntry2.key, childEntry2);
- mAlertTransferListener.onPendingEntryAdded(childEntry2);
+ mNotificationEntryListener.onPendingEntryAdded(childEntry2);
mGroupManager.onEntryAdded(childEntry2);
// Dozing changed so no reason to re-alert summary.
@@ -186,7 +187,7 @@
when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
.thenReturn(true);
- mAlertTransferListener.onEntryReinflated(childEntry);
+ mNotificationEntryListener.onEntryReinflated(childEntry);
// Alert is immediately removed from summary, and we show child as its content is inflated.
assertFalse(mHeadsUpManager.isAlerting(summaryEntry.key));
@@ -210,13 +211,13 @@
// Add second child notification so that summary is no longer suppressed.
mPendingEntries.put(childEntry2.key, childEntry2);
- mAlertTransferListener.onPendingEntryAdded(childEntry2);
+ mNotificationEntryListener.onPendingEntryAdded(childEntry2);
mGroupManager.onEntryAdded(childEntry2);
// Child entry finishes its inflation.
when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
.thenReturn(true);
- mAlertTransferListener.onEntryReinflated(childEntry);
+ mNotificationEntryListener.onEntryReinflated(childEntry);
verify(childEntry.getRow(), times(1)).freeContentViewWhenSafe(mHeadsUpManager
.getContentFlag());
@@ -236,7 +237,7 @@
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
- mAlertTransferListener.onEntryRemoved(childEntry);
+ mNotificationEntryListener.onEntryRemoved(childEntry);
assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index e5620a5..c584d02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -91,7 +92,10 @@
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationFilter;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -129,6 +133,8 @@
@Mock private ArrayList<Entry> mNotificationList;
@Mock private BiometricUnlockController mBiometricUnlockController;
@Mock private NotificationData mNotificationData;
+ @Mock
+ private NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor;
// Mock dependencies:
@Mock private NotificationViewHierarchyManager mViewHierarchyManager;
@@ -141,13 +147,17 @@
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
@Mock private NotificationPresenter mNotificationPresenter;
- @Mock private NotificationEntryManager.Callback mCallback;
+ @Mock
+ private NotificationEntryListener mCallback;
@Mock private BubbleController mBubbleController;
+ @Mock
+ private NotificationFilter mNotificationFilter;
private TestableStatusBar mStatusBar;
private FakeMetricsLogger mMetricsLogger;
private PowerManager mPowerManager;
private TestableNotificationEntryManager mEntryManager;
+ private TestableNotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
private NotificationLogger mNotificationLogger;
private CommandQueue mCommandQueue;
@@ -168,6 +178,17 @@
mDependency.injectTestDependency(DeviceProvisionedController.class,
mDeviceProvisionedController);
mDependency.injectMockDependency(BubbleController.class);
+ mDependency.injectTestDependency(NotificationFilter.class, mNotificationFilter);
+
+ IPowerManager powerManagerService = mock(IPowerManager.class);
+ mPowerManager = new PowerManager(mContext, powerManagerService,
+ Handler.createAsync(Looper.myLooper()));
+
+ mNotificationInterruptionStateProvider =
+ new TestableNotificationInterruptionStateProvider(mContext, mPowerManager,
+ mDreamManager);
+ mDependency.injectTestDependency(NotificationInterruptionStateProvider.class,
+ mNotificationInterruptionStateProvider);
mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
@@ -178,10 +199,6 @@
mNotificationLogger = new NotificationLogger();
DozeLog.traceDozing(mContext, false /* dozing */);
- IPowerManager powerManagerService = mock(IPowerManager.class);
- mPowerManager = new PowerManager(mContext, powerManagerService,
- Handler.createAsync(Looper.myLooper()));
-
mCommandQueue = mock(CommandQueue.class);
when(mCommandQueue.asBinder()).thenReturn(new Binder());
mContext.putComponent(CommandQueue.class, mCommandQueue);
@@ -205,7 +222,10 @@
return null;
}).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any());
- mEntryManager = new TestableNotificationEntryManager(mDreamManager, mPowerManager, mContext);
+ mNotificationInterruptionStateProvider.setUpWithPresenter(mNotificationPresenter,
+ mHeadsUpManager, mHeadsUpSuppressor);
+
+ mEntryManager = new TestableNotificationEntryManager(mPowerManager, mContext);
when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache,
mKeyguardIndicationController, mStackScroller, mHeadsUpManager,
@@ -362,11 +382,9 @@
public void testShouldHeadsUp_nonSuppressedGroupSummary() throws Exception {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
- when(mNotificationData.shouldSuppressStatusBar(any())).thenReturn(false);
- when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+ when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(false);
- when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
- when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
+ when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
Notification n = new Notification.Builder(getContext(), "a")
.setGroup("a")
@@ -376,19 +394,18 @@
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ entry.importance = IMPORTANCE_HIGH;
- assertTrue(mEntryManager.shouldHeadsUp(entry));
+ assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
}
@Test
public void testShouldHeadsUp_suppressedGroupSummary() throws Exception {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
- when(mNotificationData.shouldSuppressStatusBar(any())).thenReturn(false);
- when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+ when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(false);
- when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
- when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
+ when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
Notification n = new Notification.Builder(getContext(), "a")
.setGroup("a")
@@ -398,46 +415,44 @@
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ entry.importance = IMPORTANCE_HIGH;
- assertFalse(mEntryManager.shouldHeadsUp(entry));
+ assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
}
@Test
public void testShouldHeadsUp_suppressedHeadsUp() throws Exception {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
- when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+ when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(false);
- when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
- when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
-
- when(mNotificationData.shouldSuppressPeek(any())).thenReturn(true);
+ when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
Notification n = new Notification.Builder(getContext(), "a").build();
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ entry.suppressedVisualEffects = SUPPRESSED_EFFECT_PEEK;
+ entry.importance = IMPORTANCE_HIGH;
- assertFalse(mEntryManager.shouldHeadsUp(entry));
+ assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
}
@Test
public void testShouldHeadsUp_noSuppressedHeadsUp() throws Exception {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
- when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+ when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(false);
- when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
- when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
-
- when(mNotificationData.shouldSuppressPeek(any())).thenReturn(false);
+ when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
Notification n = new Notification.Builder(getContext(), "a").build();
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ entry.importance = IMPORTANCE_HIGH;
- assertTrue(mEntryManager.shouldHeadsUp(entry));
+ assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
}
@Test
@@ -725,20 +740,29 @@
public static class TestableNotificationEntryManager extends NotificationEntryManager {
- public TestableNotificationEntryManager(IDreamManager dreamManager,
- PowerManager powerManager, Context context) {
+ public TestableNotificationEntryManager(PowerManager powerManager, Context context) {
super(context);
- mDreamManager = dreamManager;
mPowerManager = powerManager;
}
public void setUpForTest(NotificationPresenter presenter,
NotificationListContainer listContainer,
- Callback callback,
+ NotificationEntryListener callback,
HeadsUpManagerPhone headsUpManager,
NotificationData notificationData) {
super.setUpWithPresenter(presenter, listContainer, callback, headsUpManager);
mNotificationData = notificationData;
+ }
+ }
+
+ public static class TestableNotificationInterruptionStateProvider extends
+ NotificationInterruptionStateProvider {
+
+ public TestableNotificationInterruptionStateProvider(
+ Context context,
+ PowerManager powerManager,
+ IDreamManager dreamManager) {
+ super(context, powerManager, dreamManager);
mUseHeadsUp = true;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
index 1cceefa..2553ac1e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
@@ -28,7 +28,6 @@
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
-import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.OverlayPlugin;
import com.android.systemui.plugins.Plugin;
@@ -39,6 +38,7 @@
import com.android.systemui.statusbar.policy.ExtensionController.TunerFactory;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
+import com.android.systemui.util.leak.LeakDetector;
import org.junit.Before;
import org.junit.Test;
@@ -62,7 +62,8 @@
mPluginManager = mDependency.injectMockDependency(PluginManager.class);
mTunerService = mDependency.injectMockDependency(TunerService.class);
mConfigurationController = mDependency.injectMockDependency(ConfigurationController.class);
- mExtensionController = Dependency.get(ExtensionController.class);
+ mExtensionController = new ExtensionControllerImpl(mContext,
+ mock(LeakDetector.class), mPluginManager, mTunerService, mConfigurationController);
}
@Test
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index fd20437..7049744 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -117,26 +117,16 @@
* @param userId User id on which the backup operation is being requested.
* @param message A message to include in the exception if it is thrown.
*/
- private void enforceCallingPermissionOnUserId(int userId, String message) {
+ private void enforceCallingPermissionOnUserId(@UserIdInt int userId, String message) {
if (Binder.getCallingUserHandle().getIdentifier() != userId) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
}
}
- /**
- * 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 @@
/**
* 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() {
@@ -170,9 +175,14 @@
* @param userId The id of the user to retrieve its instance of {@link
* UserBackupManagerService}.
* @param caller A {@link String} identifying the caller for logging purposes.
+ * @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.
*/
@Nullable
- private UserBackupManagerService getServiceForUser(@UserIdInt int userId, String caller) {
+ @VisibleForTesting
+ UserBackupManagerService getServiceForUserIfCallerHasPermission(
+ @UserIdInt int userId, String caller) {
+ enforceCallingPermissionOnUserId(userId, caller);
UserBackupManagerService userBackupManagerService = mServiceUsers.get(userId);
if (userBackupManagerService == null) {
Slog.w(TAG, "Called " + caller + " for unknown user: " + userId);
@@ -196,9 +206,9 @@
* backup for their app {@code packageName}. Only used for apps participating in key-value
* backup.
*/
- public void dataChanged(String packageName) {
+ public void dataChanged(@UserIdInt int userId, String packageName) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "dataChanged()");
+ getServiceForUserIfCallerHasPermission(userId, "dataChanged()");
if (userBackupManagerService != null) {
userBackupManagerService.dataChanged(packageName);
@@ -209,9 +219,9 @@
* Callback: a requested backup agent has been instantiated. This should only be called from the
* {@link ActivityManager}.
*/
- public void agentConnected(String packageName, IBinder agentBinder) {
+ public void agentConnected(@UserIdInt int userId, String packageName, IBinder agentBinder) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "agentConnected()");
+ getServiceForUserIfCallerHasPermission(userId, "agentConnected()");
if (userBackupManagerService != null) {
userBackupManagerService.agentConnected(packageName, agentBinder);
@@ -222,9 +232,9 @@
* Callback: a backup agent has failed to come up, or has unexpectedly quit. This should only be
* called from the {@link ActivityManager}.
*/
- public void agentDisconnected(String packageName) {
+ public void agentDisconnected(@UserIdInt int userId, String packageName) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "agentDisconnected()");
+ getServiceForUserIfCallerHasPermission(userId, "agentDisconnected()");
if (userBackupManagerService != null) {
userBackupManagerService.agentDisconnected(packageName);
@@ -235,9 +245,9 @@
* Used by a currently-active backup agent to notify the service that it has completed its given
* outstanding asynchronous backup/restore operation.
*/
- public void opComplete(int token, long result) {
+ public void opComplete(@UserIdInt int userId, int token, long result) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "opComplete()");
+ getServiceForUserIfCallerHasPermission(userId, "opComplete()");
if (userBackupManagerService != null) {
userBackupManagerService.opComplete(token, result);
@@ -249,9 +259,10 @@
// ---------------------------------------------
/** Run an initialize operation for the given transports {@code transportNames}. */
- public void initializeTransports(String[] transportNames, IBackupObserver observer) {
+ public void initializeTransports(
+ @UserIdInt int userId, String[] transportNames, IBackupObserver observer) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "initializeTransports()");
+ getServiceForUserIfCallerHasPermission(userId, "initializeTransports()");
if (userBackupManagerService != null) {
userBackupManagerService.initializeTransports(transportNames, observer);
@@ -262,9 +273,9 @@
* Clear the given package {@code packageName}'s backup data from the transport {@code
* transportName}.
*/
- public void clearBackupData(String transportName, String packageName) {
+ public void clearBackupData(@UserIdInt int userId, String transportName, String packageName) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "clearBackupData()");
+ getServiceForUserIfCallerHasPermission(userId, "clearBackupData()");
if (userBackupManagerService != null) {
userBackupManagerService.clearBackupData(transportName, packageName);
@@ -273,9 +284,9 @@
/** Return the name of the currently active transport. */
@Nullable
- public String getCurrentTransport() {
+ public String getCurrentTransport(@UserIdInt int userId) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "getCurrentTransport()");
+ getServiceForUserIfCallerHasPermission(userId, "getCurrentTransport()");
return userBackupManagerService == null
? null
@@ -287,9 +298,9 @@
* null} if no transport selected or if the transport selected is not registered.
*/
@Nullable
- public ComponentName getCurrentTransportComponent() {
+ public ComponentName getCurrentTransportComponent(@UserIdInt int userId) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "getCurrentTransportComponent()");
+ getServiceForUserIfCallerHasPermission(userId, "getCurrentTransportComponent()");
return userBackupManagerService == null
? null
@@ -298,9 +309,9 @@
/** Report all known, available backup transports by name. */
@Nullable
- public String[] listAllTransports() {
+ public String[] listAllTransports(@UserIdInt int userId) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "listAllTransports()");
+ getServiceForUserIfCallerHasPermission(userId, "listAllTransports()");
return userBackupManagerService == null
? null
@@ -309,9 +320,9 @@
/** Report all known, available backup transports by {@link ComponentName}. */
@Nullable
- public ComponentName[] listAllTransportComponents() {
+ public ComponentName[] listAllTransportComponents(@UserIdInt int userId) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "listAllTransportComponents()");
+ getServiceForUserIfCallerHasPermission(userId, "listAllTransportComponents()");
return userBackupManagerService == null
? null
@@ -321,12 +332,14 @@
/** Report all system whitelisted transports. */
@Nullable
public String[] getTransportWhitelist() {
- UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "getTransportWhitelist()");
-
- return userBackupManagerService == null
- ? null
- : userBackupManagerService.getTransportWhitelist();
+ // No permission check, intentionally.
+ String[] whitelistedTransports = new String[mTransportWhitelist.size()];
+ int i = 0;
+ for (ComponentName component : mTransportWhitelist) {
+ whitelistedTransports[i] = component.flattenToShortString();
+ i++;
+ }
+ return whitelistedTransports;
}
/**
@@ -353,6 +366,7 @@
* {@code transportComponent} or if the caller does NOT have BACKUP permission.
*/
public void updateTransportAttributes(
+ @UserIdInt int userId,
ComponentName transportComponent,
String name,
@Nullable Intent configurationIntent,
@@ -360,7 +374,7 @@
@Nullable Intent dataManagementIntent,
String dataManagementLabel) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "updateTransportAttributes()");
+ getServiceForUserIfCallerHasPermission(userId, "updateTransportAttributes()");
if (userBackupManagerService != null) {
userBackupManagerService.updateTransportAttributes(
@@ -381,9 +395,9 @@
*/
@Deprecated
@Nullable
- public String selectBackupTransport(String transportName) {
+ public String selectBackupTransport(@UserIdInt int userId, String transportName) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "selectBackupTransport()");
+ getServiceForUserIfCallerHasPermission(userId, "selectBackupTransport()");
return userBackupManagerService == null
? null
@@ -395,9 +409,11 @@
* with the result upon completion.
*/
public void selectBackupTransportAsync(
- ComponentName transportComponent, ISelectBackupTransportCallback listener) {
+ @UserIdInt int userId,
+ ComponentName transportComponent,
+ ISelectBackupTransportCallback listener) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "selectBackupTransportAsync()");
+ getServiceForUserIfCallerHasPermission(userId, "selectBackupTransportAsync()");
if (userBackupManagerService != null) {
userBackupManagerService.selectBackupTransportAsync(transportComponent, listener);
@@ -410,9 +426,9 @@
* returns {@code null}.
*/
@Nullable
- public Intent getConfigurationIntent(String transportName) {
+ public Intent getConfigurationIntent(@UserIdInt int userId, String transportName) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "getConfigurationIntent()");
+ getServiceForUserIfCallerHasPermission(userId, "getConfigurationIntent()");
return userBackupManagerService == null
? null
@@ -429,9 +445,9 @@
* @return The current destination string or null if the transport is not registered.
*/
@Nullable
- public String getDestinationString(String transportName) {
+ public String getDestinationString(@UserIdInt int userId, String transportName) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "getDestinationString()");
+ getServiceForUserIfCallerHasPermission(userId, "getDestinationString()");
return userBackupManagerService == null
? null
@@ -440,9 +456,9 @@
/** Supply the manage-data intent for the given transport. */
@Nullable
- public Intent getDataManagementIntent(String transportName) {
+ public Intent getDataManagementIntent(@UserIdInt int userId, String transportName) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "getDataManagementIntent()");
+ getServiceForUserIfCallerHasPermission(userId, "getDataManagementIntent()");
return userBackupManagerService == null
? null
@@ -454,9 +470,9 @@
* transport.
*/
@Nullable
- public String getDataManagementLabel(String transportName) {
+ public String getDataManagementLabel(@UserIdInt int userId, String transportName) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "getDataManagementLabel()");
+ getServiceForUserIfCallerHasPermission(userId, "getDataManagementLabel()");
return userBackupManagerService == null
? null
@@ -469,9 +485,8 @@
/** Enable/disable the backup service. This is user-configurable via backup settings. */
public void setBackupEnabled(@UserIdInt int userId, boolean enable) {
- enforceCallingPermissionOnUserId(userId, "setBackupEnabled");
UserBackupManagerService userBackupManagerService =
- getServiceForUser(userId, "setBackupEnabled()");
+ getServiceForUserIfCallerHasPermission(userId, "setBackupEnabled()");
if (userBackupManagerService != null) {
userBackupManagerService.setBackupEnabled(enable);
@@ -479,32 +494,21 @@
}
/** Enable/disable automatic restore of app data at install time. */
- public void setAutoRestore(boolean autoRestore) {
+ public void setAutoRestore(@UserIdInt int userId, boolean autoRestore) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "setAutoRestore()");
+ getServiceForUserIfCallerHasPermission(userId, "setAutoRestore()");
if (userBackupManagerService != null) {
userBackupManagerService.setAutoRestore(autoRestore);
}
}
- /** Mark the backup service as having been provisioned (device has gone through SUW). */
- public void setBackupProvisioned(boolean provisioned) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "setBackupProvisioned()");
-
- if (userBackupManagerService != null) {
- userBackupManagerService.setBackupProvisioned(provisioned);
- }
- }
-
/**
* Return {@code true} if the backup mechanism is currently enabled, else returns {@code false}.
*/
public boolean isBackupEnabled(@UserIdInt int userId) {
- enforceCallingPermissionOnUserId(userId, "isBackupEnabled");
UserBackupManagerService userBackupManagerService =
- getServiceForUser(userId, "isBackupEnabled()");
+ getServiceForUserIfCallerHasPermission(userId, "isBackupEnabled()");
return userBackupManagerService != null && userBackupManagerService.isBackupEnabled();
}
@@ -514,9 +518,9 @@
// ---------------------------------------------
/** Checks if the given package {@code packageName} is eligible for backup. */
- public boolean isAppEligibleForBackup(String packageName) {
+ public boolean isAppEligibleForBackup(@UserIdInt int userId, String packageName) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "isAppEligibleForBackup()");
+ getServiceForUserIfCallerHasPermission(userId, "isAppEligibleForBackup()");
return userBackupManagerService != null
&& userBackupManagerService.isAppEligibleForBackup(packageName);
@@ -526,9 +530,9 @@
* Returns from the inputted packages {@code packages}, the ones that are eligible for backup.
*/
@Nullable
- public String[] filterAppsEligibleForBackup(String[] packages) {
+ public String[] filterAppsEligibleForBackup(@UserIdInt int userId, String[] packages) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "filterAppsEligibleForBackup()");
+ getServiceForUserIfCallerHasPermission(userId, "filterAppsEligibleForBackup()");
return userBackupManagerService == null
? null
@@ -540,9 +544,8 @@
* they have pending updates.
*/
public void backupNow(@UserIdInt int userId) {
- enforceCallingPermissionOnUserId(userId, "backupNow");
UserBackupManagerService userBackupManagerService =
- getServiceForUser(userId, "backupNow()");
+ getServiceForUserIfCallerHasPermission(userId, "backupNow()");
if (userBackupManagerService != null) {
userBackupManagerService.backupNow();
@@ -559,9 +562,8 @@
IBackupObserver observer,
IBackupManagerMonitor monitor,
int flags) {
- enforceCallingPermissionOnUserId(userId, "requestBackup");
UserBackupManagerService userBackupManagerService =
- getServiceForUser(userId, "requestBackup()");
+ getServiceForUserIfCallerHasPermission(userId, "requestBackup()");
return userBackupManagerService == null
? BackupManager.ERROR_BACKUP_NOT_ALLOWED
@@ -570,9 +572,8 @@
/** Cancel all running backup operations. */
public void cancelBackups(@UserIdInt int userId) {
- enforceCallingPermissionOnUserId(userId, "cancelBackups");
UserBackupManagerService userBackupManagerService =
- getServiceForUser(userId, "cancelBackups()");
+ getServiceForUserIfCallerHasPermission(userId, "cancelBackups()");
if (userBackupManagerService != null) {
userBackupManagerService.cancelBackups();
@@ -589,7 +590,7 @@
*/
public boolean beginFullBackup(FullBackupJob scheduledJob) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "beginFullBackup()");
+ getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "beginFullBackup()");
return userBackupManagerService != null
&& userBackupManagerService.beginFullBackup(scheduledJob);
@@ -601,7 +602,7 @@
*/
public void endFullBackup() {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "endFullBackup()");
+ getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "endFullBackup()");
if (userBackupManagerService != null) {
userBackupManagerService.endFullBackup();
@@ -611,9 +612,9 @@
/**
* Run a full backup pass for the given packages {@code packageNames}. Used by 'adb shell bmgr'.
*/
- public void fullTransportBackup(String[] packageNames) {
+ public void fullTransportBackup(@UserIdInt int userId, String[] packageNames) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "fullTransportBackup()");
+ getServiceForUserIfCallerHasPermission(userId, "fullTransportBackup()");
if (userBackupManagerService != null) {
userBackupManagerService.fullTransportBackup(packageNames);
@@ -628,9 +629,9 @@
* Used to run a restore pass for an application that is being installed. This should only be
* called from the {@link PackageManager}.
*/
- public void restoreAtInstall(String packageName, int token) {
+ public void restoreAtInstall(@UserIdInt int userId, String packageName, int token) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "restoreAtInstall()");
+ getServiceForUserIfCallerHasPermission(userId, "restoreAtInstall()");
if (userBackupManagerService != null) {
userBackupManagerService.restoreAtInstall(packageName, token);
@@ -642,9 +643,10 @@
* {@code transportName}.
*/
@Nullable
- public IRestoreSession beginRestoreSession(String packageName, String transportName) {
+ public IRestoreSession beginRestoreSession(
+ @UserIdInt int userId, String packageName, String transportName) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "beginRestoreSession()");
+ getServiceForUserIfCallerHasPermission(userId, "beginRestoreSession()");
return userBackupManagerService == null
? null
@@ -655,9 +657,9 @@
* Get the restore-set token for the best-available restore set for this {@code packageName}:
* the active set if possible, else the ancestral one. Returns zero if none available.
*/
- public long getAvailableRestoreToken(String packageName) {
+ public long getAvailableRestoreToken(@UserIdInt int userId, String packageName) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "getAvailableRestoreToken()");
+ getServiceForUserIfCallerHasPermission(userId, "getAvailableRestoreToken()");
return userBackupManagerService == null
? 0
@@ -671,7 +673,8 @@
/** Sets the backup password used when running adb backup. */
public boolean setBackupPassword(String currentPassword, String newPassword) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "setBackupPassword()");
+ getServiceForUserIfCallerHasPermission(
+ UserHandle.USER_SYSTEM, "setBackupPassword()");
return userBackupManagerService != null
&& userBackupManagerService.setBackupPassword(currentPassword, newPassword);
@@ -680,7 +683,8 @@
/** Returns {@code true} if adb backup was run with a password, else returns {@code false}. */
public boolean hasBackupPassword() {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "hasBackupPassword()");
+ getServiceForUserIfCallerHasPermission(
+ UserHandle.USER_SYSTEM, "hasBackupPassword()");
return userBackupManagerService != null && userBackupManagerService.hasBackupPassword();
}
@@ -703,9 +707,8 @@
boolean doCompress,
boolean doKeyValue,
String[] packageNames) {
- enforceCallingPermissionOnUserId(userId, "adbBackup");
UserBackupManagerService userBackupManagerService =
- getServiceForUser(userId, "adbBackup()");
+ getServiceForUserIfCallerHasPermission(userId, "adbBackup()");
if (userBackupManagerService != null) {
userBackupManagerService.adbBackup(
@@ -728,9 +731,8 @@
* requires on-screen confirmation by the user.
*/
public void adbRestore(@UserIdInt int userId, ParcelFileDescriptor fd) {
- enforceCallingPermissionOnUserId(userId, "setBackupEnabled");
UserBackupManagerService userBackupManagerService =
- getServiceForUser(userId, "adbRestore()");
+ getServiceForUserIfCallerHasPermission(userId, "adbRestore()");
if (userBackupManagerService != null) {
userBackupManagerService.adbRestore(fd);
@@ -742,13 +744,14 @@
* to require a user-facing disclosure about the operation.
*/
public void acknowledgeAdbBackupOrRestore(
+ @UserIdInt int userId,
int token,
boolean allow,
String currentPassword,
String encryptionPassword,
IFullBackupRestoreObserver observer) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "acknowledgeAdbBackupOrRestore()");
+ getServiceForUserIfCallerHasPermission(userId, "acknowledgeAdbBackupOrRestore()");
if (userBackupManagerService != null) {
userBackupManagerService.acknowledgeAdbBackupOrRestore(
@@ -763,7 +766,7 @@
/** Prints service state for 'dumpsys backup'. */
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "dump()");
+ getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "dump()");
if (userBackupManagerService != null) {
userBackupManagerService.dump(fd, pw, args);
@@ -823,10 +826,14 @@
@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 eb10a04..8eb5207 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 @@
}
/**
- * 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);
-
- // 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();
+ if (mGlobalDisable) {
+ Slog.i(TAG, "Backup service not supported");
+ return;
}
+
+ 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 @@
}
/**
+ * 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.
@@ -287,7 +284,7 @@
public void dataChangedForUser(int userId, String packageName) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.dataChanged(packageName);
+ svc.dataChanged(userId, packageName);
}
}
@@ -301,7 +298,7 @@
int userId, String[] transportNames, IBackupObserver observer) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.initializeTransports(transportNames, observer);
+ svc.initializeTransports(userId, transportNames, observer);
}
}
@@ -310,7 +307,7 @@
throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.clearBackupData(transportName, packageName);
+ svc.clearBackupData(userId, transportName, packageName);
}
}
@@ -325,7 +322,7 @@
throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.agentConnected(packageName, agent);
+ svc.agentConnected(userId, packageName, agent);
}
}
@@ -338,7 +335,7 @@
public void agentDisconnectedForUser(int userId, String packageName) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.agentDisconnected(packageName);
+ svc.agentDisconnected(userId, packageName);
}
}
@@ -352,7 +349,7 @@
throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.restoreAtInstall(packageName, token);
+ svc.restoreAtInstall(userId, packageName, token);
}
}
@@ -379,7 +376,7 @@
public void setAutoRestoreForUser(int userId, boolean doAutoRestore) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.setAutoRestore(doAutoRestore);
+ svc.setAutoRestore(userId, doAutoRestore);
}
}
@@ -390,10 +387,9 @@
@Override
public void setBackupProvisioned(boolean isProvisioned) throws RemoteException {
- BackupManagerService svc = mService;
- if (svc != null) {
- svc.setBackupProvisioned(isProvisioned);
- }
+ /*
+ * This is now a no-op; provisioning is simply the device's own setup state.
+ */
}
@Override
@@ -448,7 +444,7 @@
throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.fullTransportBackup(packageNames);
+ svc.fullTransportBackup(userId, packageNames);
}
}
@@ -471,7 +467,7 @@
throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.acknowledgeAdbBackupOrRestore(token, allow,
+ svc.acknowledgeAdbBackupOrRestore(userId, token, allow,
curPassword, encryptionPassword, observer);
}
}
@@ -489,7 +485,7 @@
@Override
public String getCurrentTransportForUser(int userId) throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.getCurrentTransport() : null;
+ return (svc != null) ? svc.getCurrentTransport(userId) : null;
}
@Override
@@ -505,13 +501,13 @@
@Nullable
public ComponentName getCurrentTransportComponentForUser(int userId) {
BackupManagerService svc = mService;
- return (svc != null) ? svc.getCurrentTransportComponent() : null;
+ return (svc != null) ? svc.getCurrentTransportComponent(userId) : null;
}
@Override
public String[] listAllTransportsForUser(int userId) throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.listAllTransports() : null;
+ return (svc != null) ? svc.listAllTransports(userId) : null;
}
@Override
@@ -522,7 +518,7 @@
@Override
public ComponentName[] listAllTransportComponentsForUser(int userId) throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.listAllTransportComponents() : null;
+ return (svc != null) ? svc.listAllTransportComponents(userId) : null;
}
@Override
@@ -543,6 +539,7 @@
BackupManagerService svc = mService;
if (svc != null) {
svc.updateTransportAttributes(
+ userId,
transportComponent,
name,
configurationIntent,
@@ -556,7 +553,7 @@
public String selectBackupTransportForUser(int userId, String transport)
throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.selectBackupTransport(transport) : null;
+ return (svc != null) ? svc.selectBackupTransport(userId, transport) : null;
}
@Override
@@ -569,7 +566,7 @@
ISelectBackupTransportCallback listener) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.selectBackupTransportAsync(transport, listener);
+ svc.selectBackupTransportAsync(userId, transport, listener);
} else {
if (listener != null) {
try {
@@ -585,7 +582,7 @@
public Intent getConfigurationIntentForUser(int userId, String transport)
throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.getConfigurationIntent(transport) : null;
+ return (svc != null) ? svc.getConfigurationIntent(userId, transport) : null;
}
@Override
@@ -597,7 +594,7 @@
@Override
public String getDestinationStringForUser(int userId, String transport) throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.getDestinationString(transport) : null;
+ return (svc != null) ? svc.getDestinationString(userId, transport) : null;
}
@Override
@@ -609,7 +606,7 @@
public Intent getDataManagementIntentForUser(int userId, String transport)
throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.getDataManagementIntent(transport) : null;
+ return (svc != null) ? svc.getDataManagementIntent(userId, transport) : null;
}
@Override
@@ -622,7 +619,7 @@
public String getDataManagementLabelForUser(int userId, String transport)
throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.getDataManagementLabel(transport) : null;
+ return (svc != null) ? svc.getDataManagementLabel(userId, transport) : null;
}
@Override
@@ -635,33 +632,33 @@
public IRestoreSession beginRestoreSessionForUser(
int userId, String packageName, String transportID) throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.beginRestoreSession(packageName, transportID) : null;
+ return (svc != null) ? svc.beginRestoreSession(userId, packageName, transportID) : null;
}
@Override
public void opComplete(int token, long result) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.opComplete(token, result);
+ svc.opComplete(binderGetCallingUserId(), token, result);
}
}
@Override
public long getAvailableRestoreTokenForUser(int userId, String packageName) {
BackupManagerService svc = mService;
- return (svc != null) ? svc.getAvailableRestoreToken(packageName) : 0;
+ return (svc != null) ? svc.getAvailableRestoreToken(userId, packageName) : 0;
}
@Override
public boolean isAppEligibleForBackupForUser(int userId, String packageName) {
BackupManagerService svc = mService;
- return (svc != null) ? svc.isAppEligibleForBackup(packageName) : false;
+ return (svc != null) ? svc.isAppEligibleForBackup(userId, packageName) : false;
}
@Override
public String[] filterAppsEligibleForBackupForUser(int userId, String[] packages) {
BackupManagerService svc = mService;
- return (svc != null) ? svc.filterAppsEligibleForBackup(packages) : null;
+ return (svc != null) ? svc.filterAppsEligibleForBackup(userId, packages) : null;
}
@Override
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index d357404..2e41443 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -2811,15 +2811,6 @@
}
}
- /** Mark the backup service as having been provisioned. */
- public void setBackupProvisioned(boolean available) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "setBackupProvisioned");
- /*
- * This is now a no-op; provisioning is simply the device's own setup state.
- */
- }
-
/** Report whether the backup mechanism is currently enabled. */
public boolean isBackupEnabled() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
@@ -2869,19 +2860,6 @@
return mTransportManager.getRegisteredTransportComponents();
}
- /** Report all system whitelisted transports. */
- public String[] getTransportWhitelist() {
- // No permission check, intentionally.
- Set<ComponentName> whitelistedComponents = mTransportManager.getTransportWhitelist();
- String[] whitelistedTransports = new String[whitelistedComponents.size()];
- int i = 0;
- for (ComponentName component : whitelistedComponents) {
- whitelistedTransports[i] = component.flattenToShortString();
- i++;
- }
- return whitelistedTransports;
- }
-
/**
* Update the attributes of the transport identified by {@code transportComponent}. If the
* specified transport has not been bound at least once (for registration), this call will be
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index e8820ae..0b3fa02 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.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.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 @@
@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 @@
// 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 f21b0d8..03257e3 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 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.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 @@
@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 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 ba98b95..f59636b 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 @@
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 @@
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 f0ec69f..3cdf09e 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -65,7 +65,6 @@
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
@@ -1646,29 +1645,40 @@
}
@Override
+ public int checkOperationRaw(int code, int uid, String packageName) {
+ return checkOperationInternal(code, uid, packageName, true /*raw*/);
+ }
+
+ @Override
public int checkOperation(int code, int uid, String packageName) {
+ return checkOperationInternal(code, uid, packageName, false /*raw*/);
+ }
+
+ private int checkOperationInternal(int code, int uid, String packageName, boolean raw) {
final CheckOpsDelegate delegate;
synchronized (this) {
delegate = mCheckOpsDelegate;
}
if (delegate == null) {
- return checkOperationImpl(code, uid, packageName);
+ return checkOperationImpl(code, uid, packageName, raw);
}
- return delegate.checkOperation(code, uid, packageName,
+ return delegate.checkOperation(code, uid, packageName, raw,
AppOpsService.this::checkOperationImpl);
}
- private int checkOperationImpl(int code, int uid, String packageName) {
+ private int checkOperationImpl(int code, int uid, String packageName,
+ boolean raw) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
return AppOpsManager.MODE_IGNORED;
}
- return checkOperationUnchecked(code, uid, resolvedPackageName);
+ return checkOperationUnchecked(code, uid, resolvedPackageName, raw);
}
- private int checkOperationUnchecked(int code, int uid, String packageName) {
+ private int checkOperationUnchecked(int code, int uid, String packageName,
+ boolean raw) {
synchronized (this) {
if (isOpRestrictedLocked(uid, code, packageName)) {
return AppOpsManager.MODE_IGNORED;
@@ -1677,7 +1687,8 @@
UidState uidState = getUidStateLocked(uid, false);
if (uidState != null && uidState.opModes != null
&& uidState.opModes.indexOfKey(code) >= 0) {
- return uidState.evalMode(uidState.opModes.get(code));
+ final int rawMode = uidState.opModes.get(code);
+ return raw ? rawMode : uidState.evalMode(rawMode);
}
Op op = getOpLocked(code, uid, packageName, false, true, false);
if (op == null) {
@@ -4085,10 +4096,15 @@
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;
@@ -4258,9 +4274,8 @@
}
@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 08cb7a2..121a830 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.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.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 @@
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 @@
* 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 @@
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 @@
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 @@
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 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 @@
static class Injector {
private final Context mContext;
private ConnectivityService mConnectivityService;
+ private Constants mConstants;
private LocationManager mLocationManager;
Injector(Context ctx) {
@@ -1591,7 +1636,10 @@
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 @@
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 @@
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 @@
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 @@
.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 @@
}
}
+ @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 @@
cancelLocatingLocked();
stopMonitoringMotionLocked();
mAnyMotionDetector.stop();
+ updateActiveConstraintsLocked();
}
private void resetLightIdleManagementLocked() {
@@ -2583,40 +2746,50 @@
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 @@
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 @@
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 @@
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 @@
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 d33b617..2346cfc 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.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 @@
}
@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 7adcaba..2a80644 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.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 @@
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 @@
}
}
+ 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/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index fe632e5..a94fa12 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2023,7 +2023,7 @@
if (!mAm.validateAssociationAllowedLocked(callingPackage, callingUid,
name.getPackageName(), sInfo.applicationInfo.uid)) {
String msg = "association not allowed between packages "
- + callingPackage + " and " + r.packageName;
+ + callingPackage + " and " + name.getPackageName();
Slog.w(TAG, "Service lookup failed: " + msg);
return new ServiceLookupResult(null, msg);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3bfd363..1a5dd90 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 @@
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 @@
*/
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 @@
final Handler mUiHandler;
final ServiceThread mProcStartHandlerThread;
final Handler mProcStartHandler;
- final ServiceThread mCompactionThread;
final ActivityManagerConstants mConstants;
@@ -1796,11 +1795,6 @@
}
};
- 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 @@
? 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 @@
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 @@
DisplayThread.get().getLooper());
mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
+ mAppCompact = new AppCompactor(this);
+
mProcessCpuThread = new Thread("CpuTracker") {
@Override
public void run() {
@@ -2455,7 +2361,7 @@
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 @@
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);
@@ -20117,18 +20015,18 @@
}
@Override
- public int checkOperation(int code, int uid, String packageName,
- TriFunction<Integer, Integer, String, Integer> superImpl) {
+ public int checkOperation(int code, int uid, String packageName, boolean raw,
+ QuadFunction<Integer, Integer, String, Boolean, Integer> superImpl) {
if (uid == mTargetUid && isTargetOp(code)) {
final long identity = Binder.clearCallingIdentity();
try {
return superImpl.apply(code, Process.SHELL_UID,
- "com.android.shell");
+ "com.android.shell", raw);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- return superImpl.apply(code, uid, packageName);
+ return superImpl.apply(code, uid, packageName, raw);
}
@Override
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 0000000..aee16c3
--- /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/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 5208ca5..e483b26 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -15,7 +15,7 @@
jjaggi@google.com
racarr@google.com
chaviw@google.com
-brycelee@google.com
+vishnun@google.com
akulian@google.com
roosa@google.com
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 2feea41..905f826 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -20,11 +20,11 @@
import android.content.pm.PackageManager;
import android.media.AudioFormat;
import android.media.AudioManager;
-import android.media.AudioPlaybackConfiguration;
import android.media.AudioRecordingConfiguration;
import android.media.AudioSystem;
import android.media.IRecordingConfigDispatcher;
import android.media.MediaRecorder;
+import android.media.audiofx.AudioEffect;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
@@ -64,12 +64,19 @@
* Implementation of android.media.AudioSystem.AudioRecordingCallback
*/
public void onRecordingConfigurationChanged(int event, int uid, int session, int source,
- int[] recordingInfo, String packName) {
+ int portId, boolean silenced, int[] recordingInfo,
+ AudioEffect.Descriptor[] clientEffects,
+ AudioEffect.Descriptor[] effects,
+ int activeSource, String packName) {
if (MediaRecorder.isSystemOnlyAudioSource(source)) {
return;
}
+ String clientEffectName = clientEffects.length == 0 ? "None" : clientEffects[0].name;
+ String effectName = effects.length == 0 ? "None" : effects[0].name;
+
final List<AudioRecordingConfiguration> configsSystem =
- updateSnapshot(event, uid, session, source, recordingInfo);
+ updateSnapshot(event, uid, session, source, recordingInfo,
+ portId, silenced, activeSource, clientEffects, effects);
if (configsSystem != null){
synchronized (mClients) {
// list of recording configurations for "public consumption". It is only computed if
@@ -179,13 +186,20 @@
* @param session
* @param source
* @param recordingFormat see
- * {@link AudioSystem.AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int, int[])}
+ * {@link AudioSystem.AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int,\
+ int, int, boolean, int[], AudioEffect.Descriptor[], AudioEffect.Descriptor[], int, String)}
* for the definition of the contents of the array
+ * @param portId
+ * @param silenced
+ * @param activeSource
+ * @param clientEffects
+ * @param effects
* @return null if the list of active recording sessions has not been modified, a list
* with the current active configurations otherwise.
*/
private List<AudioRecordingConfiguration> updateSnapshot(int event, int uid, int session,
- int source, int[] recordingInfo) {
+ int source, int[] recordingInfo, int portId, boolean silenced, int activeSource,
+ AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] effects) {
final boolean configChanged;
final ArrayList<AudioRecordingConfiguration> configs;
synchronized(mRecordConfigs) {
@@ -211,7 +225,7 @@
.setSampleRate(recordingInfo[5])
.build();
final int patchHandle = recordingInfo[6];
- final Integer sessionKey = new Integer(session);
+ final Integer portIdKey = new Integer(portId);
final String[] packages = mPackMan.getPackagesForUid(uid);
final String packageName;
@@ -222,19 +236,20 @@
}
final AudioRecordingConfiguration updatedConfig =
new AudioRecordingConfiguration(uid, session, source,
- clientFormat, deviceFormat, patchHandle, packageName);
+ clientFormat, deviceFormat, patchHandle, packageName,
+ portId, silenced, activeSource, clientEffects, effects);
- if (mRecordConfigs.containsKey(sessionKey)) {
- if (updatedConfig.equals(mRecordConfigs.get(sessionKey))) {
+ if (mRecordConfigs.containsKey(portIdKey)) {
+ if (updatedConfig.equals(mRecordConfigs.get(portIdKey))) {
configChanged = false;
} else {
// config exists but has been modified
- mRecordConfigs.remove(sessionKey);
- mRecordConfigs.put(sessionKey, updatedConfig);
+ mRecordConfigs.remove(portIdKey);
+ mRecordConfigs.put(portIdKey, updatedConfig);
configChanged = true;
}
} else {
- mRecordConfigs.put(sessionKey, updatedConfig);
+ mRecordConfigs.put(portIdKey, updatedConfig);
configChanged = true;
}
if (configChanged) {
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 0000000..cc319bf
--- /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 0000000..6d52f71
--- /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 0000000..4d5760e
--- /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 0000000..f1f95730
--- /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 0000000..2d472de6
--- /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/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index 521fa23..b6c82d3 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -39,26 +39,31 @@
import android.os.Looper;
import android.os.UserHandle;
import android.provider.Settings.Secure;
+import android.provider.Settings.System;
import android.util.MathUtils;
import android.util.Slog;
import android.view.animation.AnimationUtils;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ColorDisplayController;
+import com.android.server.DisplayThread;
import com.android.server.SystemService;
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
+import java.time.DateTimeException;
+import java.time.Instant;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
+import java.time.format.DateTimeParseException;
/**
* Controls the display's color transforms.
*/
-public final class ColorDisplayService extends SystemService
- implements ColorDisplayController.Callback {
+public final class ColorDisplayService extends SystemService {
private static final String TAG = "ColorDisplayService";
@@ -71,6 +76,7 @@
* The identity matrix, used if one of the given matrices is {@code null}.
*/
private static final float[] MATRIX_IDENTITY = new float[16];
+
static {
Matrix.setIdentityM(MATRIX_IDENTITY, 0);
}
@@ -90,10 +96,12 @@
private ContentObserver mUserSetupObserver;
private boolean mBootCompleted;
- private ColorDisplayController mController;
+ private ColorDisplayController mNightDisplayController;
+ private ContentObserver mContentObserver;
private ValueAnimator mColorMatrixAnimator;
- private Boolean mIsActivated;
- private AutoMode mAutoMode;
+
+ private Boolean mIsNightDisplayActivated;
+ private NightDisplayAutoMode mNightDisplayAutoMode;
public ColorDisplayService(Context context) {
super(context);
@@ -186,42 +194,102 @@
private void setUp() {
Slog.d(TAG, "setUp: currentUser=" + mCurrentUser);
- // Create a new controller for the current user and start listening for changes.
- mController = new ColorDisplayController(getContext(), mCurrentUser);
- mController.setListener(this);
+ mNightDisplayController = new ColorDisplayController(getContext(), mCurrentUser);
+
+ // Listen for external changes to any of the settings.
+ if (mContentObserver == null) {
+ mContentObserver = new ContentObserver(new Handler(DisplayThread.get().getLooper())) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+
+ final String setting = uri == null ? null : uri.getLastPathSegment();
+ if (setting != null) {
+ switch (setting) {
+ case Secure.NIGHT_DISPLAY_ACTIVATED:
+ onNightDisplayActivated(mNightDisplayController.isActivated());
+ break;
+ case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE:
+ onNightDisplayColorTemperatureChanged(
+ mNightDisplayController.getColorTemperature());
+ break;
+ case Secure.NIGHT_DISPLAY_AUTO_MODE:
+ onNightDisplayAutoModeChanged(
+ mNightDisplayController.getAutoMode());
+ break;
+ case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME:
+ onNightDisplayCustomStartTimeChanged(
+ mNightDisplayController.getCustomStartTime());
+ break;
+ case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME:
+ onNightDisplayCustomEndTimeChanged(
+ mNightDisplayController.getCustomEndTime());
+ break;
+ case System.DISPLAY_COLOR_MODE:
+ onDisplayColorModeChanged(mNightDisplayController.getColorMode());
+ break;
+ case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:
+ case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED:
+ onAccessibilityTransformChanged();
+ break;
+ }
+ }
+ }
+ };
+ }
+ final ContentResolver cr = getContext().getContentResolver();
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ cr.registerContentObserver(System.getUriFor(System.DISPLAY_COLOR_MODE),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ cr.registerContentObserver(
+ Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ cr.registerContentObserver(
+ Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
// Set the color mode, if valid, and immediately apply the updated tint matrix based on the
// existing activated state. This ensures consistency of tint across the color mode change.
- onDisplayColorModeChanged(mController.getColorMode());
+ onDisplayColorModeChanged(mNightDisplayController.getColorMode());
// Reset the activated state.
- mIsActivated = null;
+ mIsNightDisplayActivated = null;
setCoefficientMatrix(getContext(), DisplayTransformManager.needsLinearColorMatrix());
// Prepare color transformation matrix.
- setMatrix(mController.getColorTemperature(), mMatrixNight);
+ setMatrix(mNightDisplayController.getColorTemperature(), mMatrixNight);
// Initialize the current auto mode.
- onAutoModeChanged(mController.getAutoMode());
+ onNightDisplayAutoModeChanged(mNightDisplayController.getAutoMode());
// Force the initialization current activated state.
- if (mIsActivated == null) {
- onActivated(mController.isActivated());
+ if (mIsNightDisplayActivated == null) {
+ onNightDisplayActivated(mNightDisplayController.isActivated());
}
}
private void tearDown() {
Slog.d(TAG, "tearDown: currentUser=" + mCurrentUser);
- if (mController != null) {
- mController.setListener(null);
- mController = null;
+ getContext().getContentResolver().unregisterContentObserver(mContentObserver);
+
+ if (mNightDisplayController != null) {
+ mNightDisplayController = null;
}
- if (mAutoMode != null) {
- mAutoMode.onStop();
- mAutoMode = null;
+ if (mNightDisplayAutoMode != null) {
+ mNightDisplayAutoMode.onStop();
+ mNightDisplayAutoMode = null;
}
if (mColorMatrixAnimator != null) {
@@ -230,67 +298,61 @@
}
}
- @Override
- public void onActivated(boolean activated) {
- if (mIsActivated == null || mIsActivated != activated) {
+ private void onNightDisplayActivated(boolean activated) {
+ if (mIsNightDisplayActivated == null || mIsNightDisplayActivated != activated) {
Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display");
- mIsActivated = activated;
+ mIsNightDisplayActivated = activated;
- if (mAutoMode != null) {
- mAutoMode.onActivated(activated);
+ if (mNightDisplayAutoMode != null) {
+ mNightDisplayAutoMode.onActivated(activated);
}
applyTint(false);
}
}
- @Override
- public void onAutoModeChanged(int autoMode) {
- Slog.d(TAG, "onAutoModeChanged: autoMode=" + autoMode);
+ private void onNightDisplayAutoModeChanged(int autoMode) {
+ Slog.d(TAG, "onNightDisplayAutoModeChanged: autoMode=" + autoMode);
- if (mAutoMode != null) {
- mAutoMode.onStop();
- mAutoMode = null;
+ if (mNightDisplayAutoMode != null) {
+ mNightDisplayAutoMode.onStop();
+ mNightDisplayAutoMode = null;
}
if (autoMode == ColorDisplayController.AUTO_MODE_CUSTOM) {
- mAutoMode = new CustomAutoMode();
+ mNightDisplayAutoMode = new CustomNightDisplayAutoMode();
} else if (autoMode == ColorDisplayController.AUTO_MODE_TWILIGHT) {
- mAutoMode = new TwilightAutoMode();
+ mNightDisplayAutoMode = new TwilightNightDisplayAutoMode();
}
- if (mAutoMode != null) {
- mAutoMode.onStart();
+ if (mNightDisplayAutoMode != null) {
+ mNightDisplayAutoMode.onStart();
}
}
- @Override
- public void onCustomStartTimeChanged(LocalTime startTime) {
- Slog.d(TAG, "onCustomStartTimeChanged: startTime=" + startTime);
+ private void onNightDisplayCustomStartTimeChanged(LocalTime startTime) {
+ Slog.d(TAG, "onNightDisplayCustomStartTimeChanged: startTime=" + startTime);
- if (mAutoMode != null) {
- mAutoMode.onCustomStartTimeChanged(startTime);
+ if (mNightDisplayAutoMode != null) {
+ mNightDisplayAutoMode.onCustomStartTimeChanged(startTime);
}
}
- @Override
- public void onCustomEndTimeChanged(LocalTime endTime) {
- Slog.d(TAG, "onCustomEndTimeChanged: endTime=" + endTime);
+ private void onNightDisplayCustomEndTimeChanged(LocalTime endTime) {
+ Slog.d(TAG, "onNightDisplayCustomEndTimeChanged: endTime=" + endTime);
- if (mAutoMode != null) {
- mAutoMode.onCustomEndTimeChanged(endTime);
+ if (mNightDisplayAutoMode != null) {
+ mNightDisplayAutoMode.onCustomEndTimeChanged(endTime);
}
}
- @Override
- public void onColorTemperatureChanged(int colorTemperature) {
+ private void onNightDisplayColorTemperatureChanged(int colorTemperature) {
setMatrix(colorTemperature, mMatrixNight);
applyTint(true);
}
- @Override
- public void onDisplayColorModeChanged(int mode) {
+ private void onDisplayColorModeChanged(int mode) {
if (mode == -1) {
return;
}
@@ -301,16 +363,15 @@
}
setCoefficientMatrix(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
- setMatrix(mController.getColorTemperature(), mMatrixNight);
+ setMatrix(mNightDisplayController.getColorTemperature(), mMatrixNight);
final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
- dtm.setColorMode(mode, (mIsActivated != null && mIsActivated) ? mMatrixNight
- : MATRIX_IDENTITY);
+ dtm.setColorMode(mode, (mIsNightDisplayActivated != null && mIsNightDisplayActivated)
+ ? mMatrixNight : MATRIX_IDENTITY);
}
- @Override
- public void onAccessibilityTransformChanged(boolean state) {
- onDisplayColorModeChanged(mController.getColorMode());
+ private void onAccessibilityTransformChanged() {
+ onDisplayColorModeChanged(mNightDisplayController.getColorMode());
}
/**
@@ -338,7 +399,7 @@
final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
final float[] from = dtm.getColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY);
- final float[] to = mIsActivated ? mMatrixNight : MATRIX_IDENTITY;
+ final float[] to = mIsNightDisplayActivated ? mMatrixNight : MATRIX_IDENTITY;
if (immediate) {
dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, to);
@@ -383,7 +444,7 @@
* Set the color transformation {@code MATRIX_NIGHT} to the given color temperature.
*
* @param colorTemperature color temperature in Kelvin
- * @param outTemp the 4x4 display transformation matrix for that color temperature
+ * @param outTemp the 4x4 display transformation matrix for that color temperature
*/
private void setMatrix(int colorTemperature, float[] outTemp) {
if (outTemp.length != 16) {
@@ -412,7 +473,8 @@
* @param compareTime the LocalDateTime to compare against
* @return the prior LocalDateTime corresponding to this local time
*/
- public static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
+ @VisibleForTesting
+ static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
@@ -427,7 +489,8 @@
* @param compareTime the LocalDateTime to compare against
* @return the next LocalDateTime corresponding to this local time
*/
- public static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
+ @VisibleForTesting
+ static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
@@ -440,13 +503,47 @@
return dtm.isDeviceColorManaged();
}
- private abstract class AutoMode implements ColorDisplayController.Callback {
+ /**
+ * Returns the last time the night display transform activation state was changed, or {@link
+ * LocalDateTime#MIN} if night display has never been activated.
+ */
+ private @NonNull LocalDateTime getNightDisplayLastActivatedTimeSetting() {
+ final ContentResolver cr = getContext().getContentResolver();
+ final String lastActivatedTime = Secure.getStringForUser(
+ cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, getContext().getUserId());
+ if (lastActivatedTime != null) {
+ try {
+ return LocalDateTime.parse(lastActivatedTime);
+ } catch (DateTimeParseException ignored) {
+ }
+ // Uses the old epoch time.
+ try {
+ return LocalDateTime.ofInstant(
+ Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
+ ZoneId.systemDefault());
+ } catch (DateTimeException | NumberFormatException ignored) {
+ }
+ }
+ return LocalDateTime.MIN;
+ }
+
+ private abstract class NightDisplayAutoMode {
+
+ public abstract void onActivated(boolean activated);
+
public abstract void onStart();
public abstract void onStop();
+
+ public void onCustomStartTimeChanged(LocalTime startTime) {
+ }
+
+ public void onCustomEndTimeChanged(LocalTime endTime) {
+ }
}
- private class CustomAutoMode extends AutoMode implements AlarmManager.OnAlarmListener {
+ private final class CustomNightDisplayAutoMode extends NightDisplayAutoMode implements
+ AlarmManager.OnAlarmListener {
private final AlarmManager mAlarmManager;
private final BroadcastReceiver mTimeChangedReceiver;
@@ -456,7 +553,7 @@
private LocalDateTime mLastActivatedTime;
- CustomAutoMode() {
+ CustomNightDisplayAutoMode() {
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
mTimeChangedReceiver = new BroadcastReceiver() {
@Override
@@ -476,15 +573,15 @@
// Maintain the existing activated state if within the current period.
if (mLastActivatedTime.isBefore(now) && mLastActivatedTime.isAfter(start)
&& (mLastActivatedTime.isAfter(end) || now.isBefore(end))) {
- activate = mController.isActivated();
+ activate = mNightDisplayController.isActivated();
}
}
- if (mIsActivated == null || mIsActivated != activate) {
- mController.setActivated(activate);
+ if (mIsNightDisplayActivated == null || mIsNightDisplayActivated != activate) {
+ mNightDisplayController.setActivated(activate);
}
- updateNextAlarm(mIsActivated, now);
+ updateNextAlarm(mIsNightDisplayActivated, now);
}
private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) {
@@ -502,10 +599,10 @@
intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
getContext().registerReceiver(mTimeChangedReceiver, intentFilter);
- mStartTime = mController.getCustomStartTime();
- mEndTime = mController.getCustomEndTime();
+ mStartTime = mNightDisplayController.getCustomStartTime();
+ mEndTime = mNightDisplayController.getCustomEndTime();
- mLastActivatedTime = mController.getLastActivatedTime();
+ mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
// Force an update to initialize state.
updateActivated();
@@ -521,7 +618,7 @@
@Override
public void onActivated(boolean activated) {
- mLastActivatedTime = mController.getLastActivatedTime();
+ mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
updateNextAlarm(activated, LocalDateTime.now());
}
@@ -546,11 +643,13 @@
}
}
- private class TwilightAutoMode extends AutoMode implements TwilightListener {
+ private final class TwilightNightDisplayAutoMode extends NightDisplayAutoMode implements
+ TwilightListener {
private final TwilightManager mTwilightManager;
+ private LocalDateTime mLastActivatedTime;
- TwilightAutoMode() {
+ TwilightNightDisplayAutoMode() {
mTwilightManager = getLocalService(TwilightManager.class);
}
@@ -562,26 +661,31 @@
}
boolean activate = state.isNight();
- final LocalDateTime lastActivatedTime = mController.getLastActivatedTime();
- if (lastActivatedTime != null) {
+ if (mLastActivatedTime != null) {
final LocalDateTime now = LocalDateTime.now();
final LocalDateTime sunrise = state.sunrise();
final LocalDateTime sunset = state.sunset();
// Maintain the existing activated state if within the current period.
- if (lastActivatedTime.isBefore(now) && (lastActivatedTime.isBefore(sunrise)
- ^ lastActivatedTime.isBefore(sunset))) {
- activate = mController.isActivated();
+ if (mLastActivatedTime.isBefore(now) && (mLastActivatedTime.isBefore(sunrise)
+ ^ mLastActivatedTime.isBefore(sunset))) {
+ activate = mNightDisplayController.isActivated();
}
}
- if (mIsActivated == null || mIsActivated != activate) {
- mController.setActivated(activate);
+ if (mIsNightDisplayActivated == null || mIsNightDisplayActivated != activate) {
+ mNightDisplayController.setActivated(activate);
}
}
@Override
+ public void onActivated(boolean activated) {
+ mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
+ }
+
+ @Override
public void onStart() {
mTwilightManager.registerListener(this, mHandler);
+ mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
// Force an update to initialize state.
updateActivated(mTwilightManager.getLastTwilightState());
@@ -590,10 +694,7 @@
@Override
public void onStop() {
mTwilightManager.unregisterListener(this);
- }
-
- @Override
- public void onActivated(boolean activated) {
+ mLastActivatedTime = null;
}
@Override
@@ -624,6 +725,7 @@
}
private final class BinderService extends IColorDisplayManager.Stub {
+
@Override
public boolean isDeviceColorManaged() {
final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 3552e66..fb6eaa0 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -73,6 +73,7 @@
import android.os.Bundle;
import android.os.Debug;
import android.os.Environment;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.IInterface;
@@ -3275,7 +3276,11 @@
final int packageNum = packageInfos.length;
for (int i = 0; i < packageNum; ++i) {
if (packageInfos[i].equals(imi.getPackageName())) {
- mFileManager.addInputMethodSubtypes(imi, subtypes);
+ if (subtypes.length > 0) {
+ mFileManager.addInputMethodSubtypes(imi, subtypes);
+ } else {
+ mFileManager.deleteAllInputMethodSubtypes(imi.getId());
+ }
final long ident = Binder.clearCallingIdentity();
try {
buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
@@ -4296,19 +4301,10 @@
? new File(Environment.getDataDirectory(), SYSTEM_PATH)
: Environment.getUserSystemDirectory(userId);
final File inputMethodDir = new File(systemDir, INPUT_METHOD_PATH);
- if (!inputMethodDir.exists() && !inputMethodDir.mkdirs()) {
- Slog.w(TAG, "Couldn't create dir.: " + inputMethodDir.getAbsolutePath());
- }
final File subtypeFile = new File(inputMethodDir, ADDITIONAL_SUBTYPES_FILE_NAME);
mAdditionalInputMethodSubtypeFile = new AtomicFile(subtypeFile, "input-subtypes");
- if (!subtypeFile.exists()) {
- // If "subtypes.xml" doesn't exist, create a blank file.
- writeAdditionalInputMethodSubtypes(
- mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, methodMap);
- } else {
- readAdditionalInputMethodSubtypes(
- mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile);
- }
+ readAdditionalInputMethodSubtypes(mAdditionalSubtypesMap,
+ mAdditionalInputMethodSubtypeFile);
}
private void deleteAllInputMethodSubtypes(String imiId) {
@@ -4348,6 +4344,25 @@
private static void writeAdditionalInputMethodSubtypes(
ArrayMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile,
ArrayMap<String, InputMethodInfo> methodMap) {
+ if (allSubtypes.isEmpty()) {
+ if (subtypesFile.exists()) {
+ subtypesFile.delete();
+ }
+ final File parentDir = subtypesFile.getBaseFile().getParentFile();
+ if (parentDir != null && FileUtils.listFilesOrEmpty(parentDir).length == 0) {
+ if (!parentDir.delete()) {
+ Slog.e(TAG, "Failed to delete the empty parent directory " + parentDir);
+ }
+ }
+ return;
+ }
+
+ final File parentDir = subtypesFile.getBaseFile().getParentFile();
+ if (!parentDir.exists() && !parentDir.mkdirs()) {
+ Slog.e(TAG, "Failed to create a parent directory " + parentDir);
+ return;
+ }
+
// Safety net for the case that this function is called before methodMap is set.
final boolean isSetMethodMap = methodMap != null && methodMap.size() > 0;
FileOutputStream fos = null;
@@ -4404,6 +4419,10 @@
ArrayMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile) {
if (allSubtypes == null || subtypesFile == null) return;
allSubtypes.clear();
+ if (!subtypesFile.exists()) {
+ // Not having the file means there is no additional subtype.
+ return;
+ }
try (final FileInputStream fis = subtypesFile.openRead()) {
final XmlPullParser parser = Xml.newPullParser();
parser.setInput(fis, StandardCharsets.UTF_8.name());
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 8e3f351..918dc07 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.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 @@
}
// 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 29e1878..9c6cb20 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 @@
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
@@ -642,6 +642,17 @@
Log.e(TAG, "unable to parse SUPL_ES: " + suplESProperty);
}
}
+
+ String emergencyExtensionSecondsString
+ = properties.getProperty("ES_EXTENSION_SEC", "0");
+ try {
+ int emergencyExtensionSeconds =
+ Integer.parseInt(emergencyExtensionSecondsString);
+ mNIHandler.setEmergencyExtensionSeconds(emergencyExtensionSeconds);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "unable to parse ES_EXTENSION_SEC: "
+ + emergencyExtensionSecondsString);
+ }
}
private void loadPropertiesFromResource(Context context,
@@ -1596,20 +1607,20 @@
@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 3e2ba87..77dee82 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 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 @@
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 @@
}
}
+ /**
+ * 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 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 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 @@
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 37d43fc..e69b2ec 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 @@
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/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index b7bb2c6..93b6620 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -27,10 +27,8 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.media.AudioManager;
@@ -636,16 +634,11 @@
* <p>The contents of this object is guarded by {@link #mLock}.
*/
final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener {
- public static final int COMPONENT_TYPE_BROADCAST = 0;
- public static final int COMPONENT_TYPE_ACTIVITY = 1;
- public static final int COMPONENT_TYPE_SERVICE = 2;
private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
-
private final int mFullUserId;
private final MediaSessionStack mPriorityStack;
private PendingIntent mLastMediaButtonReceiver;
private ComponentName mRestoredMediaButtonReceiver;
- private int mRestoredMediaButtonReceiverComponentType;
private int mRestoredMediaButtonReceiverUserId;
private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener;
@@ -662,23 +655,17 @@
mFullUserId = fullUserId;
mPriorityStack = new MediaSessionStack(mAudioPlayerStateMonitor, this);
// Restore the remembered media button receiver before the boot.
- String mediaButtonReceiverInfo = Settings.Secure.getStringForUser(mContentResolver,
+ String mediaButtonReceiver = Settings.Secure.getStringForUser(mContentResolver,
Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId);
- if (mediaButtonReceiverInfo == null) {
+ if (mediaButtonReceiver == null) {
return;
}
- String[] tokens = mediaButtonReceiverInfo.split(COMPONENT_NAME_USER_ID_DELIM);
- if (tokens == null || (tokens.length != 2 && tokens.length != 3)) {
+ String[] tokens = mediaButtonReceiver.split(COMPONENT_NAME_USER_ID_DELIM);
+ if (tokens == null || tokens.length != 2) {
return;
}
mRestoredMediaButtonReceiver = ComponentName.unflattenFromString(tokens[0]);
mRestoredMediaButtonReceiverUserId = Integer.parseInt(tokens[1]);
- if (tokens.length == 3) {
- mRestoredMediaButtonReceiverComponentType = Integer.parseInt(tokens[2]);
- } else {
- mRestoredMediaButtonReceiverComponentType =
- getComponentType(mRestoredMediaButtonReceiver);
- }
}
public void destroySessionsForUserLocked(int userId) {
@@ -709,8 +696,6 @@
pw.println(indent + "Callback: " + mCallback);
pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver);
pw.println(indent + "Restored MediaButtonReceiver: " + mRestoredMediaButtonReceiver);
- pw.println(indent + "Restored MediaButtonReceiverComponentType: "
- + mRestoredMediaButtonReceiverComponentType);
mPriorityStack.dump(pw, indent);
}
@@ -737,21 +722,17 @@
PendingIntent receiver = record.getMediaButtonReceiver();
mLastMediaButtonReceiver = receiver;
mRestoredMediaButtonReceiver = null;
-
- String mediaButtonReceiverInfo = "";
+ String componentName = "";
if (receiver != null) {
ComponentName component = receiver.getIntent().getComponent();
if (component != null
&& record.getPackageName().equals(component.getPackageName())) {
- String componentName = component.flattenToString();
- int componentType = getComponentType(component);
- mediaButtonReceiverInfo = String.join(COMPONENT_NAME_USER_ID_DELIM,
- componentName, String.valueOf(record.getUserId()),
- String.valueOf(componentType));
+ componentName = component.flattenToString();
}
}
Settings.Secure.putStringForUser(mContentResolver,
- Settings.System.MEDIA_BUTTON_RECEIVER, mediaButtonReceiverInfo,
+ Settings.System.MEDIA_BUTTON_RECEIVER,
+ componentName + COMPONENT_NAME_USER_ID_DELIM + record.getUserId(),
mFullUserId);
}
@@ -781,32 +762,6 @@
return isGlobalPriorityActiveLocked()
? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession();
}
-
- private int getComponentType(ComponentName componentName) {
- PackageManager pm = getContext().getPackageManager();
- try {
- ActivityInfo activityInfo = pm.getActivityInfo(componentName,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.GET_ACTIVITIES);
- if (activityInfo != null) {
- return COMPONENT_TYPE_ACTIVITY;
- }
- } catch (NameNotFoundException e) {
- }
- try {
- ServiceInfo serviceInfo = pm.getServiceInfo(componentName,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.GET_SERVICES);
- if (serviceInfo != null) {
- return COMPONENT_TYPE_SERVICE;
- }
- } catch (NameNotFoundException e) {
- }
- // Pick legacy behavior for BroadcastReceiver or unknown.
- return COMPONENT_TYPE_BROADCAST;
- }
}
final class SessionsListenerRecord implements IBinder.DeathRecipient {
@@ -1625,32 +1580,14 @@
} else {
ComponentName receiver =
mCurrentFullUserRecord.mRestoredMediaButtonReceiver;
- int componentType = mCurrentFullUserRecord
- .mRestoredMediaButtonReceiverComponentType;
- UserHandle userHandle = UserHandle.of(mCurrentFullUserRecord
- .mRestoredMediaButtonReceiverUserId);
if (DEBUG_KEY_EVENT) {
Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
- + receiver + ", type=" + componentType);
+ + receiver);
}
mediaButtonIntent.setComponent(receiver);
- try {
- switch (componentType) {
- case FullUserRecord.COMPONENT_TYPE_ACTIVITY:
- getContext().startActivityAsUser(mediaButtonIntent, userHandle);
- break;
- case FullUserRecord.COMPONENT_TYPE_SERVICE:
- getContext().startForegroundServiceAsUser(mediaButtonIntent,
- userHandle);
- break;
- default:
- // Legacy behavior for other cases.
- getContext().sendBroadcastAsUser(mediaButtonIntent, userHandle);
- }
- } catch (Exception e) {
- Log.w(TAG, "Error sending media button to the restored intent "
- + receiver + ", type=" + componentType, e);
- }
+ getContext().sendBroadcastAsUser(mediaButtonIntent,
+ UserHandle.of(mCurrentFullUserRecord
+ .mRestoredMediaButtonReceiverUserId));
if (mCurrentFullUserRecord.mCallback != null) {
mCurrentFullUserRecord.mCallback
.onMediaKeyEventDispatchedToMediaButtonReceiver(
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 53ee16b..d961bad1 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1952,9 +1952,11 @@
*/
@GuardedBy("mNotificationLock")
protected void reportSeen(NotificationRecord r) {
- mAppUsageStats.reportEvent(r.sbn.getPackageName(),
- getRealUserId(r.sbn.getUserId()),
- UsageEvents.Event.NOTIFICATION_SEEN);
+ if (!r.isProxied()) {
+ mAppUsageStats.reportEvent(r.sbn.getPackageName(),
+ getRealUserId(r.sbn.getUserId()),
+ UsageEvents.Event.NOTIFICATION_SEEN);
+ }
}
protected int calculateSuppressedVisualEffects(Policy incomingPolicy, Policy currPolicy,
@@ -4056,7 +4058,7 @@
final ArraySet<ComponentName> listeners =
mListenersDisablingEffects.valueAt(i);
for (int j = 0; j < listeners.size(); j++) {
- final ComponentName componentName = listeners.valueAt(i);
+ final ComponentName componentName = listeners.valueAt(j);
componentName.writeToProto(proto,
ListenersDisablingEffectsProto.LISTENER_COMPONENTS);
}
@@ -4201,8 +4203,8 @@
final int listenerSize = listeners.size();
for (int j = 0; j < listenerSize; j++) {
- if (i > 0) pw.print(',');
- final ComponentName listener = listeners.valueAt(i);
+ if (j > 0) pw.print(',');
+ final ComponentName listener = listeners.valueAt(j);
if (listener != null) {
pw.print(listener);
}
@@ -4937,7 +4939,7 @@
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/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 1f8893c..e2c64ca 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -151,7 +151,6 @@
private int mSuppressedVisualEffects = 0;
private String mUserExplanation;
- private String mPeopleExplanation;
private boolean mPreChannelsNotification = true;
private Uri mSound;
private long[] mVibration;
@@ -1191,6 +1190,13 @@
}
/**
+ * Returns whether this notification was posted by a secondary app
+ */
+ public boolean isProxied() {
+ return !Objects.equals(sbn.getPackageName(), sbn.getOpPkg());
+ }
+
+ /**
* @return all {@link Uri} that should have permission granted to whoever
* will be rendering it. This list has already been vetted to only
* include {@link Uri} that the enqueuing app can grant.
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 7ae2271..3a7919a 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -120,7 +120,7 @@
android.Manifest.permission.INTERACT_ACROSS_PROFILES, callingUid,
-1, true);
if (permissionFlag != PackageManager.PERMISSION_GRANTED
- || !mInjector.getUserManager().isSameProfileGroup(callerUserId, userId)) {
+ || !isSameProfileGroup(callerUserId, userId)) {
throw new SecurityException("Attempt to launch activity without required "
+ android.Manifest.permission.INTERACT_ACROSS_PROFILES + " permission"
+ " or target user is not in the same profile group.");
@@ -209,6 +209,15 @@
}
}
+ private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) {
+ final long ident = mInjector.clearCallingIdentity();
+ try {
+ return mInjector.getUserManager().isSameProfileGroup(callerUserId, userId);
+ } finally {
+ mInjector.restoreCallingIdentity(ident);
+ }
+ }
+
/**
* Verify that the given calling package is belong to the calling UID.
*/
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 093b85e..f9e31ae 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -402,12 +402,17 @@
+ " dexoptFlags=" + printDexoptFlags(dexoptFlags)
+ " target-filter=" + compilerFilter);
- // TODO(calin): b/64530081 b/66984396. Use SKIP_SHARED_LIBRARY_CHECK for the context
- // (instead of dexUseInfo.getClassLoaderContext()) in order to compile secondary dex files
- // in isolation (and avoid to extract/verify the main apk if it's in the class path).
- // Note this trades correctness for performance since the resulting slow down is
- // unacceptable in some cases until b/64530081 is fixed.
- String classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
+ String classLoaderContext;
+ if (dexUseInfo.isUnknownClassLoaderContext() || dexUseInfo.isVariableClassLoaderContext()) {
+ // If we have an unknown (not yet set), or a variable class loader chain, compile
+ // without a context and mark the oat file with SKIP_SHARED_LIBRARY_CHECK. Note that
+ // this might lead to a incorrect compilation.
+ // TODO(calin): We should just extract in this case.
+ classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
+ } else {
+ classLoaderContext = dexUseInfo.getClassLoaderContext();
+ }
+
int reason = options.getCompilationReason();
try {
for (String isa : dexUseInfo.getLoaderIsas()) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 206a88b..35ffe8d 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -250,6 +250,15 @@
@GuardedBy("mLock")
private int mParentSessionId;
+ @GuardedBy("mLock")
+ private boolean mStagedSessionApplied;
+ @GuardedBy("mLock")
+ private boolean mStagedSessionReady;
+ @GuardedBy("mLock")
+ private boolean mStagedSessionFailed;
+ @GuardedBy("mLock")
+ private int mStagedSessionErrorCode = SessionInfo.NO_ERROR;
+
/**
* Path to the validated base APK for this session, which may point at an
* APK inside the session (when the session defines the base), or it may
@@ -470,6 +479,10 @@
if (info.childSessionIds == null) {
info.childSessionIds = EMPTY_CHILD_SESSION_ARRAY;
}
+ info.isSessionApplied = mStagedSessionApplied;
+ info.isSessionReady = mStagedSessionReady;
+ info.isSessionFailed = mStagedSessionFailed;
+ info.setStagedSessionErrorCode(mStagedSessionErrorCode);
}
return info;
}
@@ -1051,6 +1064,8 @@
}
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 28fb01d..af12633 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 libcore.io.IoUtils;
import libcore.util.EmptyArray;
-import libcore.util.HexEncoding;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -888,7 +887,7 @@
volatile boolean mSystemReady;
volatile boolean mSafeMode;
volatile boolean mHasSystemUidErrors;
- private volatile boolean mWebInstantAppsDisabled;
+ private volatile SparseBooleanArray mWebInstantAppsDisabled = new SparseBooleanArray();
ApplicationInfo mAndroidApplication;
final ActivityInfo mResolveActivity = new ActivityInfo();
@@ -2225,9 +2224,7 @@
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
@@ -5904,8 +5901,8 @@
/**
* Returns whether or not instant apps have been disabled remotely.
*/
- private boolean areWebInstantAppsDisabled() {
- return mWebInstantAppsDisabled;
+ private boolean areWebInstantAppsDisabled(int userId) {
+ return mWebInstantAppsDisabled.get(userId);
}
private boolean isInstantAppResolutionAllowed(
@@ -5936,7 +5933,7 @@
} else {
if (intent.getData() == null || TextUtils.isEmpty(intent.getData().getHost())) {
return false;
- } else if (areWebInstantAppsDisabled()) {
+ } else if (areWebInstantAppsDisabled(userId)) {
return false;
}
}
@@ -6816,7 +6813,7 @@
private List<ResolveInfo> applyPostResolutionFilter(List<ResolveInfo> resolveInfos,
String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
boolean resolveForStart, int userId, Intent intent) {
- final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled();
+ final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId);
for (int i = resolveInfos.size() - 1; i >= 0; i--) {
final ResolveInfo info = resolveInfos.get(i);
// remove locally resolved instant app web results when disabled
@@ -8744,8 +8741,15 @@
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 @@
}
}
- 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 @@
@GuardedBy("mPackages")
private void updateSharedLibrariesLPr(PackageParser.Package pkg,
PackageParser.Package changingLib) throws PackageManagerException {
- if (pkg == null) {
- return;
- }
+ final ArrayList<SharedLibraryInfo> sharedLibraryInfos =
+ collectSharedLibraryInfos(pkg, Collections.unmodifiableMap(mPackages),
+ mSharedLibraries, null);
+ executeSharedLibrariesUpdateLPr(pkg, changingLib, sharedLibraryInfos);
+ }
- // If the package provides libraries, clear their old dependencies.
- // This method will set them up again.
- applyDefiningSharedLibraryUpdateLocked(pkg, null, (definingLibrary, dependency) -> {
- definingLibrary.clearDependencies();
- });
+ 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 null;
+ }
// 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 @@
}
@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 @@
+ 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 @@
@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 @@
}
- 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 @@
* 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 @@
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 @@
} 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 @@
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 @@
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 @@
}
}
- 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 @@
* 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,90 +11451,23 @@
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;
- // 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");
+ if (!ArrayUtils.isEmpty(reconciledPkg.allowedSharedLibraryInfos)) {
+ for (SharedLibraryInfo info : reconciledPkg.allowedSharedLibraryInfos) {
+ commitSharedLibraryInfoLocked(info);
}
- // 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.
+ try {
+ // Shared libraries for the package need to be updated.
+ updateSharedLibrariesLPr(pkg, null);
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e);
+ }
}
- 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);
- }
- }
+ 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);
}
}
@@ -15235,8 +15223,8 @@
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 @@
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 @@
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 @@
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 @@
} 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 @@
try {
prepareScanResultLocked(scanResult);
- commitScanResultLocked(scanResult);
+ commitReconciledScanResultLocked(reconciledPkg);
} catch (PackageManagerException e) {
unprepareScanResultLocked(scanResult);
res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR);
@@ -15482,7 +15626,7 @@
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 @@
}
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 @@
*/
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 @@
// 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 @@
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 @@
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 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 @@
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 @@
}
}
}
- return new DeletePackageAction(ps, disabledPs, outInfo, flags, user,
- mayDeleteUnupdatedSystemApp);
+ return new DeletePackageAction(ps, disabledPs, outInfo, flags, user);
}
/*
@@ -17993,8 +18144,8 @@
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 @@
// 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 @@
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,16 +20280,21 @@
ContentObserver co = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
- mWebInstantAppsDisabled =
- (Global.getInt(resolver, Global.ENABLE_EPHEMERAL_FEATURE, 1) == 0) ||
- (Secure.getInt(resolver, Secure.INSTANT_APPS_ENABLED, 1) == 0);
+ final boolean ephemeralFeatureDisabled =
+ Global.getInt(resolver, Global.ENABLE_EPHEMERAL_FEATURE, 1) == 0;
+ for (int userId : UserManagerService.getInstance().getUserIds()) {
+ final boolean instantAppsDisabledForUser =
+ ephemeralFeatureDisabled || Secure.getIntForUser(resolver,
+ Secure.INSTANT_APPS_ENABLED, 1, userId) == 0;
+ mWebInstantAppsDisabled.put(userId, instantAppsDisabledForUser);
+ }
}
};
mContext.getContentResolver().registerContentObserver(android.provider.Settings.Global
.getUriFor(Global.ENABLE_EPHEMERAL_FEATURE),
- false, co, UserHandle.USER_SYSTEM);
+ false, co, UserHandle.USER_ALL);
mContext.getContentResolver().registerContentObserver(android.provider.Settings.Secure
- .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_SYSTEM);
+ .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_ALL);
co.onChange(true);
// Disable any carrier apps. We do this very early in boot to prevent the apps from being
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 37a35a2..357872e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -630,9 +630,13 @@
pw.print("=");
}
pw.print(info.packageName);
- if (showVersionCode && !isApex) {
+ if (showVersionCode) {
pw.print(" versionCode:");
- pw.print(info.applicationInfo.versionCode);
+ if (info.applicationInfo != null) {
+ pw.print(info.applicationInfo.versionCode);
+ } else {
+ pw.print(info.versionCode);
+ }
}
if (listInstaller && !isApex) {
pw.print(" installer=");
@@ -2779,7 +2783,7 @@
pw.println(" Prints all system libraries.");
pw.println("");
pw.println(" list packages [-f] [-d] [-e] [-s] [-3] [-i] [-l] [-u] [-U] ");
- pw.println(" [--apex-only] [--uid UID] [--user USER_ID] [FILTER]");
+ pw.println(" [--show-versioncode] [--apex-only] [--uid UID] [--user USER_ID] [FILTER]");
pw.println(" Prints all packages; optionally only those whose name contains");
pw.println(" the text in FILTER. Options are:");
pw.println(" -f: see their associated file");
@@ -2792,6 +2796,7 @@
pw.println(" -l: ignored (used for compatibility with older releases)");
pw.println(" -U: also show the package UID");
pw.println(" -u: also include uninstalled packages");
+ pw.println(" --show-versioncode: also show the version code");
pw.println(" --apex-only: only show APEX packages");
pw.println(" --uid UID: filter to only show packages with the given UID");
pw.println(" --user USER_ID: only list packages belonging to the given user");
diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
index 33a9650..e68c238 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -875,15 +875,13 @@
public String getClassLoaderContext() { return mClassLoaderContext; }
- @VisibleForTesting
- /* package */ boolean isUnknownClassLoaderContext() {
+ public boolean isUnknownClassLoaderContext() {
// The class loader context may be unknown if we loaded the data from a previous version
// which didn't save the context.
return UNKNOWN_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
}
- @VisibleForTesting
- /* package */ boolean isVariableClassLoaderContext() {
+ public boolean isVariableClassLoaderContext() {
return VARIABLE_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
}
}
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 164af38..789664d 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 @@
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 @@
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 @@
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 b58c8116..93964cb 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.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 @@
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 @@
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/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 02d8c0b..fc21adb 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -718,12 +718,12 @@
disabledData += " }";
final UiState state = getUiState(displayId);
- Log.d(TAG, "disabledlocked (b/113914868): displayId=" + displayId + "net1=" + net1
+ Log.d(TAG, "disabledlocked (b/113914868): displayId=" + displayId + ", net1=" + net1
+ ", mDisabled1=" + state.mDisabled1 + ", token=" + token
+ ", mDisableRecords=" + mDisableRecords.size() + " => " + disabledData);
}
final UiState state = getUiState(displayId);
- if (state.disableEquals(net1, net2)) {
+ if (!state.disableEquals(net1, net2)) {
state.setDisabled(net1, net2);
mHandler.post(() -> mNotificationDelegate.onSetDisabled(net1));
if (mBar != null) {
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 0000000..56bcdd9
--- /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 23c29f8..65d5b10 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 @@
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.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 @@
@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 @@
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/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 7c61e37..f008770 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -905,16 +905,14 @@
return;
}
if (supportsMultiDisplay(systemConnection)
- && fallbackConnection.getConnectedEngineSize() != 0) {
- fallbackConnection.forEachDisplayConnector(
- WallpaperConnection.DisplayConnector::disconnectLocked);
+ && fallbackConnection.mDisplayConnector.size() != 0) {
+ fallbackConnection.forEachDisplayConnector(connector -> {
+ if (connector.mEngine != null) {
+ connector.disconnectLocked();
+ }
+ });
fallbackConnection.mDisplayConnector.clear();
} else {
- // TODO(b/121181553) Handle wallpaper service disconnect case.
- if (fallbackConnection.mService == null) {
- Slog.w(TAG, "There is no fallback wallpaper service");
- return;
- }
fallbackConnection.appendConnectorWithCondition(display ->
fallbackConnection.isUsableDisplay(display)
&& display.getDisplayId() != DEFAULT_DISPLAY
@@ -965,6 +963,10 @@
}
void connectLocked(WallpaperConnection connection, WallpaperData wallpaper) {
+ if (connection.mService == null) {
+ Slog.w(TAG, "WallpaperService is not connected yet");
+ return;
+ }
if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken);
try {
mIWindowManager.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId);
@@ -980,7 +982,7 @@
wpdData.mPadding, mDisplayId);
} catch (RemoteException e) {
Slog.w(TAG, "Failed attaching wallpaper on display", e);
- if (mLastWallpaper != null && !mLastWallpaper.wallpaperUpdating
+ if (wallpaper != null && !wallpaper.wallpaperUpdating
&& connection.getConnectedEngineSize() == 0) {
bindWallpaperComponentLocked(null /* componentName */, false /* force */,
false /* fromUser */, wallpaper, null /* reply */);
@@ -1067,8 +1069,11 @@
for (Display display : displays) {
if (tester.test(display)) {
final int displayId = display.getDisplayId();
- mDisplayConnector.append(displayId,
- new DisplayConnector(displayId));
+ final DisplayConnector connector = mDisplayConnector.get(displayId);
+ if (connector == null) {
+ mDisplayConnector.append(displayId,
+ new DisplayConnector(displayId));
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4d8440a8..2cd0168 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 @@
}
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 1943efc..a5ceee2 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 @@
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 @@
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 @@
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 7aabc15..bcc7be4 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 @@
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 c1e9a73..fc1c65c 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 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 @@
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 @@
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 @@
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/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index fff42c5..8dda485 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -2,6 +2,6 @@
jjaggi@google.com
racarr@google.com
chaviw@google.com
-brycelee@google.com
+vishnun@google.com
akulian@google.com
roosa@google.com
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index 8ec97c5..6f92e64 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 @@
// 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 @@
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 b10fd31..d334bd2 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.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 @@
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 11d9ebb..ee74bdf 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 @@
* 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 c006a7b..b219419 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 @@
// 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 651089d..32c5a3b 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 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 @@
}
}
+ /**
+ * 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 e3ced83..b6a4a51 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 @@
}
}
- @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 58fd30e..729aed1 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_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::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 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_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 @@
// 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();
}
+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 @@
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 @@
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 @@
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 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 bd3dfe9..409e7f4 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 @@
return Settings.Global.getInt(mContext.getContentResolver(), name, def);
}
+ @Nullable
String settingsGlobalGetString(String name) {
return Settings.Global.getString(mContext.getContentResolver(), name);
}
@@ -13901,7 +13902,11 @@
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/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d1dfd7d..046c991 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2125,11 +2125,11 @@
return;
}
switch (settings) {
- case Settings.Global.CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED_ALWAYS:
+ case "always":
// Should be used only during development
Slog.d(TAG, "ContentCaptureService explicitly enabled by Settings");
break;
- case Settings.Global.CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED_DEFAULT:
+ case "default":
// Default case: check if OEM overlaid the resource that defines the service.
final String serviceName = context.getString(
com.android.internal.R.string.config_defaultContentCaptureService);
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 83f66c5..b8723c5 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -23,6 +23,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -86,9 +87,7 @@
mContext = application;
mShadowContext = shadowOf(application);
- // TODO(b/120212806): Hardcoding system user for now since most methods in BMS don't yet
- // take an user parameter (and instead hardcode the system user).
- mUserOneId = UserHandle.USER_SYSTEM;
+ mUserOneId = UserHandle.USER_SYSTEM + 1;
mUserTwoId = mUserOneId + 1;
}
@@ -176,9 +175,78 @@
assertThat(serviceUsers.get(mUserOneId)).isEqualTo(mUserOneService);
}
- // TODO(b/120212806): When BMS methods take in a user parameter, modify unknown user tests to
- // check that that we don't call the method on another registered user. Currently these tests
- // have no registered users since we hardcode the system user in BMS.
+ /** 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.
+ */
+ @Test
+ public void testGetServiceForUser_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.getServiceForUserIfCallerHasPermission(
+ mUserOneId, "test"));
+ }
+
+ /**
+ * Test that the backup services does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testGetServiceForUserIfCallerHasPermission_withPermission_worksForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ true);
+
+ assertEquals(
+ mUserOneService,
+ backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test"));
+ }
+
+ /**
+ * Test that the backup services does not throw a {@link SecurityException} if the caller does
+ * not have INTERACT_ACROSS_USERS_FULL permission and passes in the calling user id.
+ */
+ @Test
+ public void testGetServiceForUserIfCallerHasPermission_withoutPermission_worksForCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ assertEquals(
+ mUserOneService,
+ backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test"));
+ }
// ---------------------------------------------
// Backup agent tests
@@ -189,8 +257,9 @@
public void testDataChanged_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.dataChanged(TEST_PACKAGE);
+ backupManagerService.dataChanged(mUserOneId, TEST_PACKAGE);
verify(mUserOneService).dataChanged(TEST_PACKAGE);
}
@@ -198,9 +267,11 @@
/** Test that the backup service does not route methods for non-registered users. */
@Test
public void testDataChanged_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.dataChanged(TEST_PACKAGE);
+ backupManagerService.dataChanged(mUserTwoId, TEST_PACKAGE);
verify(mUserOneService, never()).dataChanged(TEST_PACKAGE);
}
@@ -210,9 +281,10 @@
public void testAgentConnected_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
IBinder agentBinder = mock(IBinder.class);
- backupManagerService.agentConnected(TEST_PACKAGE, agentBinder);
+ backupManagerService.agentConnected(mUserOneId, TEST_PACKAGE, agentBinder);
verify(mUserOneService).agentConnected(TEST_PACKAGE, agentBinder);
}
@@ -220,10 +292,12 @@
/** Test that the backup service does not route methods for non-registered users. */
@Test
public void testAgentConnected_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
IBinder agentBinder = mock(IBinder.class);
- backupManagerService.agentConnected(TEST_PACKAGE, agentBinder);
+ backupManagerService.agentConnected(mUserTwoId, TEST_PACKAGE, agentBinder);
verify(mUserOneService, never()).agentConnected(TEST_PACKAGE, agentBinder);
}
@@ -233,8 +307,9 @@
public void testAgentDisconnected_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.agentDisconnected(TEST_PACKAGE);
+ backupManagerService.agentDisconnected(mUserOneId, TEST_PACKAGE);
verify(mUserOneService).agentDisconnected(TEST_PACKAGE);
}
@@ -242,9 +317,11 @@
/** Test that the backup service does not route methods for non-registered users. */
@Test
public void testAgentDisconnected_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.agentDisconnected(TEST_PACKAGE);
+ backupManagerService.agentDisconnected(mUserTwoId, TEST_PACKAGE);
verify(mUserOneService, never()).agentDisconnected(TEST_PACKAGE);
}
@@ -254,8 +331,9 @@
public void testOpComplete_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.opComplete(/* token */ 0, /* result */ 0L);
+ backupManagerService.opComplete(mUserOneId, /* token */ 0, /* result */ 0L);
verify(mUserOneService).opComplete(/* token */ 0, /* result */ 0L);
}
@@ -263,9 +341,11 @@
/** Test that the backup service does not route methods for non-registered users. */
@Test
public void testOpComplete_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.opComplete(/* token */ 0, /* result */ 0L);
+ backupManagerService.opComplete(mUserTwoId, /* token */ 0, /* result */ 0L);
verify(mUserOneService, never()).opComplete(/* token */ 0, /* result */ 0L);
}
@@ -279,9 +359,10 @@
public void testInitializeTransports_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
String[] transports = {TEST_TRANSPORT};
- backupManagerService.initializeTransports(transports, /* observer */ null);
+ backupManagerService.initializeTransports(mUserOneId, transports, /* observer */ null);
verify(mUserOneService).initializeTransports(transports, /* observer */ null);
}
@@ -289,10 +370,12 @@
/** Test that the backup service does not route methods for non-registered users. */
@Test
public void testInitializeTransports_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
String[] transports = {TEST_TRANSPORT};
- backupManagerService.initializeTransports(transports, /* observer */ null);
+ backupManagerService.initializeTransports(mUserTwoId, transports, /* observer */ null);
verify(mUserOneService, never()).initializeTransports(transports, /* observer */ null);
}
@@ -302,8 +385,9 @@
public void testClearBackupData_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+ backupManagerService.clearBackupData(mUserOneId, TEST_TRANSPORT, TEST_PACKAGE);
verify(mUserOneService).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
}
@@ -311,9 +395,11 @@
/** Test that the backup service does not route methods for non-registered users. */
@Test
public void testClearBackupData_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+ backupManagerService.clearBackupData(mUserTwoId, TEST_TRANSPORT, TEST_PACKAGE);
verify(mUserOneService, never()).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
}
@@ -323,8 +409,9 @@
public void testGetCurrentTransport_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.getCurrentTransport();
+ backupManagerService.getCurrentTransport(mUserOneId);
verify(mUserOneService).getCurrentTransport();
}
@@ -332,9 +419,11 @@
/** Test that the backup service does not route methods for non-registered users. */
@Test
public void testGetCurrentTransport_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.getCurrentTransport();
+ backupManagerService.getCurrentTransport(mUserTwoId);
verify(mUserOneService, never()).getCurrentTransport();
}
@@ -345,8 +434,9 @@
throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.getCurrentTransportComponent();
+ backupManagerService.getCurrentTransportComponent(mUserOneId);
verify(mUserOneService).getCurrentTransportComponent();
}
@@ -355,9 +445,11 @@
@Test
public void testGetCurrentTransportComponent_onUnknownUser_doesNotPropagateCall()
throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.getCurrentTransportComponent();
+ backupManagerService.getCurrentTransportComponent(mUserTwoId);
verify(mUserOneService, never()).getCurrentTransportComponent();
}
@@ -367,8 +459,9 @@
public void testListAllTransports_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.listAllTransports();
+ backupManagerService.listAllTransports(mUserOneId);
verify(mUserOneService).listAllTransports();
}
@@ -376,9 +469,11 @@
/** Test that the backup service does not route methods for non-registered users. */
@Test
public void testListAllTransports_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.listAllTransports();
+ backupManagerService.listAllTransports(mUserTwoId);
verify(mUserOneService, never()).listAllTransports();
}
@@ -389,8 +484,9 @@
throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.listAllTransportComponents();
+ backupManagerService.listAllTransportComponents(mUserOneId);
verify(mUserOneService).listAllTransportComponents();
}
@@ -399,32 +495,13 @@
@Test
public void testListAllTransportComponents_onUnknownUser_doesNotPropagateCall()
throws Exception {
- BackupManagerService backupManagerService = createService();
-
- backupManagerService.listAllTransportComponents();
-
- verify(mUserOneService, never()).listAllTransportComponents();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetTransportWhitelist_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.getTransportWhitelist();
+ backupManagerService.listAllTransportComponents(mUserTwoId);
- verify(mUserOneService).getTransportWhitelist();
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testGetTransportWhitelist_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
-
- backupManagerService.getTransportWhitelist();
-
- verify(mUserOneService, never()).getTransportWhitelist();
+ verify(mUserOneService, never()).listAllTransportComponents();
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@@ -433,11 +510,13 @@
throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
TransportData transport = backupTransport();
Intent configurationIntent = new Intent();
Intent dataManagementIntent = new Intent();
backupManagerService.updateTransportAttributes(
+ mUserOneId,
transport.getTransportComponent(),
transport.transportName,
configurationIntent,
@@ -459,12 +538,15 @@
@Test
public void testUpdateTransportAttributes_onUnknownUser_doesNotPropagateCall()
throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
TransportData transport = backupTransport();
Intent configurationIntent = new Intent();
Intent dataManagementIntent = new Intent();
backupManagerService.updateTransportAttributes(
+ mUserTwoId,
transport.getTransportComponent(),
transport.transportName,
configurationIntent,
@@ -487,8 +569,9 @@
public void testSelectBackupTransport_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.selectBackupTransport(TEST_TRANSPORT);
+ backupManagerService.selectBackupTransport(mUserOneId, TEST_TRANSPORT);
verify(mUserOneService).selectBackupTransport(TEST_TRANSPORT);
}
@@ -496,9 +579,11 @@
/** Test that the backup service does not route methods for non-registered users. */
@Test
public void testSelectBackupTransport_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.selectBackupTransport(TEST_TRANSPORT);
+ backupManagerService.selectBackupTransport(mUserTwoId, TEST_TRANSPORT);
verify(mUserOneService, never()).selectBackupTransport(TEST_TRANSPORT);
}
@@ -508,11 +593,12 @@
public void testSelectTransportAsync_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
TransportData transport = backupTransport();
ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
backupManagerService.selectBackupTransportAsync(
- transport.getTransportComponent(), callback);
+ mUserOneId, transport.getTransportComponent(), callback);
verify(mUserOneService)
.selectBackupTransportAsync(transport.getTransportComponent(), callback);
@@ -522,12 +608,14 @@
@Test
public void testSelectBackupTransportAsync_onUnknownUser_doesNotPropagateCall()
throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
TransportData transport = backupTransport();
ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
backupManagerService.selectBackupTransportAsync(
- transport.getTransportComponent(), callback);
+ mUserTwoId, transport.getTransportComponent(), callback);
verify(mUserOneService, never())
.selectBackupTransportAsync(transport.getTransportComponent(), callback);
@@ -538,8 +626,9 @@
public void testGetConfigurationIntent_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.getConfigurationIntent(TEST_TRANSPORT);
+ backupManagerService.getConfigurationIntent(mUserOneId, TEST_TRANSPORT);
verify(mUserOneService).getConfigurationIntent(TEST_TRANSPORT);
}
@@ -547,9 +636,11 @@
/** Test that the backup service does not route methods for non-registered users. */
@Test
public void testGetConfigurationIntent_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.getConfigurationIntent(TEST_TRANSPORT);
+ backupManagerService.getConfigurationIntent(mUserTwoId, TEST_TRANSPORT);
verify(mUserOneService, never()).getConfigurationIntent(TEST_TRANSPORT);
}
@@ -559,8 +650,9 @@
public void testGetDestinationString_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.getDestinationString(TEST_TRANSPORT);
+ backupManagerService.getDestinationString(mUserOneId, TEST_TRANSPORT);
verify(mUserOneService).getDestinationString(TEST_TRANSPORT);
}
@@ -568,9 +660,11 @@
/** Test that the backup service does not route methods for non-registered users. */
@Test
public void testGetDestinationString_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.getDestinationString(TEST_TRANSPORT);
+ backupManagerService.getDestinationString(mUserTwoId, TEST_TRANSPORT);
verify(mUserOneService, never()).getDestinationString(TEST_TRANSPORT);
}
@@ -580,8 +674,9 @@
public void testGetDataManagementIntent_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.getDataManagementIntent(TEST_TRANSPORT);
+ backupManagerService.getDataManagementIntent(mUserOneId, TEST_TRANSPORT);
verify(mUserOneService).getDataManagementIntent(TEST_TRANSPORT);
}
@@ -589,9 +684,11 @@
/** Test that the backup service does not route methods for non-registered users. */
@Test
public void testGetDataManagementIntent_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.getDataManagementIntent(TEST_TRANSPORT);
+ backupManagerService.getDataManagementIntent(mUserTwoId, TEST_TRANSPORT);
verify(mUserOneService, never()).getDataManagementIntent(TEST_TRANSPORT);
}
@@ -601,8 +698,9 @@
public void testGetDataManagementLabel_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.getDataManagementLabel(TEST_TRANSPORT);
+ backupManagerService.getDataManagementLabel(mUserOneId, TEST_TRANSPORT);
verify(mUserOneService).getDataManagementLabel(TEST_TRANSPORT);
}
@@ -610,9 +708,11 @@
/** Test that the backup service does not route methods for non-registered users. */
@Test
public void testGetDataManagementLabel_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.getDataManagementLabel(TEST_TRANSPORT);
+ backupManagerService.getDataManagementLabel(mUserTwoId, TEST_TRANSPORT);
verify(mUserOneService, never()).getDataManagementLabel(TEST_TRANSPORT);
}
@@ -620,7 +720,6 @@
// ---------------------------------------------
// Settings tests
// ---------------------------------------------
-
/**
* Test that the backup services throws a {@link SecurityException} if the caller does not have
* INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
@@ -681,8 +780,9 @@
public void testSetAutoRestore_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.setAutoRestore(true);
+ backupManagerService.setAutoRestore(mUserOneId, true);
verify(mUserOneService).setAutoRestore(true);
}
@@ -690,66 +790,17 @@
/** Test that the backup service does not route methods for non-registered users. */
@Test
public void testSetAutoRestore_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.setAutoRestore(true);
+ backupManagerService.setAutoRestore(mUserTwoId, true);
verify(mUserOneService, never()).setAutoRestore(true);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testSetBackupProvisioned_onRegisteredUser_callsMethodForUser() throws Exception {
- BackupManagerService backupManagerService =
- createServiceAndRegisterUser(mUserOneId, mUserOneService);
-
- backupManagerService.setBackupProvisioned(true);
-
- verify(mUserOneService).setBackupProvisioned(true);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testSetBackupProvisioned_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
-
- backupManagerService.setBackupProvisioned(true);
-
- verify(mUserOneService, never()).setBackupProvisioned(true);
- }
-
- /**
- * Test that the backup services throws a {@link SecurityException} if the caller does not have
- * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
- */
- @Test
- public void testIsBackupEnabled_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
- BackupManagerService backupManagerService =
- createServiceAndRegisterUser(mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- expectThrows(
- SecurityException.class, () -> backupManagerService.isBackupEnabled(mUserTwoId));
- }
-
- /**
- * Test that the backup service does not throw a {@link SecurityException} if the caller has
- * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
- */
- @Test
- public void testIsBackupEnabled_withPermission_propagatesForNonCallingUser() {
- BackupManagerService backupManagerService =
- createServiceAndRegisterUser(mUserOneId, mUserOneService);
- backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
-
- backupManagerService.isBackupEnabled(mUserTwoId);
-
- verify(mUserTwoService).isBackupEnabled();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
public void testIsBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
@@ -781,8 +832,9 @@
public void testIsAppEligibleForBackup_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.isAppEligibleForBackup(TEST_PACKAGE);
+ backupManagerService.isAppEligibleForBackup(mUserOneId, TEST_PACKAGE);
verify(mUserOneService).isAppEligibleForBackup(TEST_PACKAGE);
}
@@ -790,9 +842,11 @@
/** Test that the backup service does not route methods for non-registered users. */
@Test
public void testIsAppEligibleForBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.isAppEligibleForBackup(TEST_PACKAGE);
+ backupManagerService.isAppEligibleForBackup(mUserTwoId, TEST_PACKAGE);
verify(mUserOneService, never()).isAppEligibleForBackup(TEST_PACKAGE);
}
@@ -803,9 +857,10 @@
throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
String[] packages = {TEST_PACKAGE};
- backupManagerService.filterAppsEligibleForBackup(packages);
+ backupManagerService.filterAppsEligibleForBackup(mUserOneId, packages);
verify(mUserOneService).filterAppsEligibleForBackup(packages);
}
@@ -814,10 +869,12 @@
@Test
public void testFilterAppsEligibleForBackup_onUnknownUser_doesNotPropagateCall()
throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
String[] packages = {TEST_PACKAGE};
- backupManagerService.filterAppsEligibleForBackup(packages);
+ backupManagerService.filterAppsEligibleForBackup(mUserTwoId, packages);
verify(mUserOneService, never()).filterAppsEligibleForBackup(packages);
}
@@ -1001,7 +1058,7 @@
@Test
public void testBeginFullBackup_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
- createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
FullBackupJob job = new FullBackupJob();
backupManagerService.beginFullBackup(job);
@@ -1024,7 +1081,7 @@
@Test
public void testEndFullBackup_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
- createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
backupManagerService.endFullBackup();
@@ -1046,9 +1103,10 @@
public void testFullTransportBackup_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
String[] packages = {TEST_PACKAGE};
- backupManagerService.fullTransportBackup(packages);
+ backupManagerService.fullTransportBackup(mUserOneId, packages);
verify(mUserOneService).fullTransportBackup(packages);
}
@@ -1056,10 +1114,12 @@
/** Test that the backup service does not route methods for non-registered users. */
@Test
public void testFullTransportBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
String[] packages = {TEST_PACKAGE};
- backupManagerService.fullTransportBackup(packages);
+ backupManagerService.fullTransportBackup(mUserTwoId, packages);
verify(mUserOneService, never()).fullTransportBackup(packages);
}
@@ -1073,8 +1133,9 @@
public void testRestoreAtInstall_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+ backupManagerService.restoreAtInstall(mUserOneId, TEST_PACKAGE, /* token */ 0);
verify(mUserOneService).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
}
@@ -1082,9 +1143,11 @@
/** Test that the backup service does not route methods for non-registered users. */
@Test
public void testRestoreAtInstall_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+ backupManagerService.restoreAtInstall(mUserTwoId, TEST_PACKAGE, /* token */ 0);
verify(mUserOneService, never()).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
}
@@ -1094,8 +1157,9 @@
public void testBeginRestoreSession_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+ backupManagerService.beginRestoreSession(mUserOneId, TEST_PACKAGE, TEST_TRANSPORT);
verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
}
@@ -1103,9 +1167,11 @@
/** Test that the backup service does not route methods for non-registered users. */
@Test
public void testBeginRestoreSession_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+ backupManagerService.beginRestoreSession(mUserTwoId, TEST_PACKAGE, TEST_TRANSPORT);
verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
}
@@ -1116,8 +1182,9 @@
throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.getAvailableRestoreToken(TEST_PACKAGE);
+ backupManagerService.getAvailableRestoreToken(mUserOneId, TEST_PACKAGE);
verify(mUserOneService).getAvailableRestoreToken(TEST_PACKAGE);
}
@@ -1125,9 +1192,11 @@
/** Test that the backup service does not route methods for non-registered users. */
@Test
public void testGetAvailableRestoreToken_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.getAvailableRestoreToken(TEST_PACKAGE);
+ backupManagerService.getAvailableRestoreToken(mUserTwoId, TEST_PACKAGE);
verify(mUserOneService, never()).getAvailableRestoreToken(TEST_PACKAGE);
}
@@ -1140,7 +1209,7 @@
@Test
public void testSetBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
- createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
backupManagerService.setBackupPassword("currentPassword", "newPassword");
@@ -1161,7 +1230,7 @@
@Test
public void testHasBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
- createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
backupManagerService.hasBackupPassword();
@@ -1377,10 +1446,16 @@
throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
backupManagerService.acknowledgeAdbBackupOrRestore(
- /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer);
+ mUserOneId,
+ /* token */ 0,
+ /* allow */ true,
+ "currentPassword",
+ "encryptionPassword",
+ observer);
verify(mUserOneService)
.acknowledgeAdbBackupOrRestore(
@@ -1395,11 +1470,18 @@
@Test
public void testAcknowledgeAdbBackupOrRestore_onUnknownUser_doesNotPropagateCall()
throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
backupManagerService.acknowledgeAdbBackupOrRestore(
- /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer);
+ mUserTwoId,
+ /* token */ 0,
+ /* allow */ true,
+ "currentPassword",
+ "encryptionPassword",
+ observer);
verify(mUserOneService, never())
.acknowledgeAdbBackupOrRestore(
@@ -1418,7 +1500,7 @@
@Test
public void testDump_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
- createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
File testFile = new File(mContext.getFilesDir(), "test");
testFile.createNewFile();
FileDescriptor fileDescriptor = new FileDescriptor();
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index cff0521..1a16e56 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.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.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 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.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 @@
private DeviceIdleController mDeviceIdleController;
private AnyMotionDetectorForTest mAnyMotionDetector;
private AppStateTrackerForTest mAppStateTracker;
+ private DeviceIdleController.Constants mConstants;
private InjectorForTest mInjector;
private MockitoSession mMockingSession;
@@ -97,7 +103,7 @@
@Mock
private ConnectivityService mConnectivityService;
@Mock
- private DeviceIdleController.Constants mConstants;
+ private ContentResolver mContentResolver;
@Mock
private IActivityManager mIActivityManager;
@Mock
@@ -108,10 +114,13 @@
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 @@
}
@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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 0000000..f74ac1f
--- /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 52f434d..edd89f9 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 @@
// 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 @@
// 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 5615dff..e02a30d 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.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 @@
}
@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 @@
@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 @@
}
@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 @@
@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 @@
@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 @@
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,18 +342,21 @@
@Test
public void dataChangedForUser_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.dataChangedForUser(mUserId, PACKAGE_NAME);
- verify(mBackupManagerServiceMock).dataChanged(PACKAGE_NAME);
+ verify(mBackupManagerServiceMock).dataChanged(mUserId, PACKAGE_NAME);
}
@Test
public void dataChanged_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService();
+
mTrampoline.dataChanged(PACKAGE_NAME);
- verify(mBackupManagerServiceMock).dataChanged(PACKAGE_NAME);
+
+ verify(mBackupManagerServiceMock).dataChanged(mUserId, PACKAGE_NAME);
}
@Test
@@ -325,18 +367,21 @@
@Test
public void clearBackupDataForUser_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.clearBackupDataForUser(mUserId, TRANSPORT_NAME, PACKAGE_NAME);
- verify(mBackupManagerServiceMock).clearBackupData(TRANSPORT_NAME, PACKAGE_NAME);
+ verify(mBackupManagerServiceMock).clearBackupData(mUserId, TRANSPORT_NAME, PACKAGE_NAME);
}
@Test
public void clearBackupData_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService();
+
mTrampoline.clearBackupData(TRANSPORT_NAME, PACKAGE_NAME);
- verify(mBackupManagerServiceMock).clearBackupData(TRANSPORT_NAME, PACKAGE_NAME);
+
+ verify(mBackupManagerServiceMock).clearBackupData(mUserId, TRANSPORT_NAME, PACKAGE_NAME);
}
@Test
@@ -347,18 +392,21 @@
@Test
public void agentConnectedForUser_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.agentConnectedForUser(mUserId, PACKAGE_NAME, mAgentMock);
- verify(mBackupManagerServiceMock).agentConnected(PACKAGE_NAME, mAgentMock);
+ verify(mBackupManagerServiceMock).agentConnected(mUserId, PACKAGE_NAME, mAgentMock);
}
@Test
public void agentConnected_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService();
+
mTrampoline.agentConnected(PACKAGE_NAME, mAgentMock);
- verify(mBackupManagerServiceMock).agentConnected(PACKAGE_NAME, mAgentMock);
+
+ verify(mBackupManagerServiceMock).agentConnected(mUserId, PACKAGE_NAME, mAgentMock);
}
@Test
@@ -369,18 +417,21 @@
@Test
public void agentDisconnectedForUser_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.agentDisconnectedForUser(mUserId, PACKAGE_NAME);
- verify(mBackupManagerServiceMock).agentDisconnected(PACKAGE_NAME);
+ verify(mBackupManagerServiceMock).agentDisconnected(mUserId, PACKAGE_NAME);
}
@Test
public void agentDisconnected_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService();
+
mTrampoline.agentDisconnected(PACKAGE_NAME);
- verify(mBackupManagerServiceMock).agentDisconnected(PACKAGE_NAME);
+
+ verify(mBackupManagerServiceMock).agentDisconnected(mUserId, PACKAGE_NAME);
}
@Test
@@ -391,18 +442,21 @@
@Test
public void restoreAtInstallForUser_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.restoreAtInstallForUser(mUserId, PACKAGE_NAME, 123);
- verify(mBackupManagerServiceMock).restoreAtInstall(PACKAGE_NAME, 123);
+ verify(mBackupManagerServiceMock).restoreAtInstall(mUserId, PACKAGE_NAME, 123);
}
@Test
public void restoreAtInstall_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService();
+
mTrampoline.restoreAtInstall(PACKAGE_NAME, 123);
- verify(mBackupManagerServiceMock).restoreAtInstall(PACKAGE_NAME, 123);
+
+ verify(mBackupManagerServiceMock).restoreAtInstall(mUserId, PACKAGE_NAME, 123);
}
@Test
@@ -413,7 +467,7 @@
@Test
public void setBackupEnabledForUser_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.setBackupEnabledForUser(mUserId, true);
@@ -423,7 +477,7 @@
@Test
public void setBackupEnabled_forwardedToCallingUserId() throws RemoteException {
TrampolineTestable.sCallingUserId = mUserId;
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.setBackupEnabled(true);
@@ -438,18 +492,21 @@
@Test
public void setAutoRestoreForUser_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.setAutoRestoreForUser(mUserId, true);
- verify(mBackupManagerServiceMock).setAutoRestore(true);
+ verify(mBackupManagerServiceMock).setAutoRestore(mUserId, true);
}
@Test
public void setAutoRestore_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService();
+
mTrampoline.setAutoRestore(true);
- verify(mBackupManagerServiceMock).setAutoRestore(true);
+
+ verify(mBackupManagerServiceMock).setAutoRestore(mUserId, true);
}
@Test
@@ -460,9 +517,9 @@
@Test
public void setBackupProvisioned_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.setBackupProvisioned(true);
- verify(mBackupManagerServiceMock).setBackupProvisioned(true);
+ verifyNoMoreInteractions(mBackupManagerServiceMock);
}
@Test
@@ -473,7 +530,7 @@
@Test
public void isBackupEnabledForUser_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.isBackupEnabledForUser(mUserId);
@@ -483,7 +540,7 @@
@Test
public void isBackupEnabled_forwardedToCallingUserId() throws RemoteException {
TrampolineTestable.sCallingUserId = mUserId;
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.isBackupEnabled();
@@ -498,7 +555,7 @@
@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);
}
@@ -511,7 +568,7 @@
@Test
public void hasBackupPassword_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.hasBackupPassword();
verify(mBackupManagerServiceMock).hasBackupPassword();
}
@@ -524,7 +581,7 @@
@Test
public void backupNowForUser_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.backupNowForUser(mUserId);
@@ -534,7 +591,7 @@
@Test
public void backupNow_forwardedToCallingUserId() throws RemoteException {
TrampolineTestable.sCallingUserId = mUserId;
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.backupNow();
@@ -551,7 +608,7 @@
@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);
@@ -567,9 +624,11 @@
@Test
public void fullTransportBackupForUser_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
+
mTrampoline.fullTransportBackupForUser(mUserId, PACKAGE_NAMES);
- verify(mBackupManagerServiceMock).fullTransportBackup(PACKAGE_NAMES);
+
+ verify(mBackupManagerServiceMock).fullTransportBackup(mUserId, PACKAGE_NAMES);
}
@Test
@@ -580,7 +639,7 @@
@Test
public void adbRestore_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.adbRestore(mUserId, mParcelFileDescriptorMock);
verify(mBackupManagerServiceMock).adbRestore(mUserId, mParcelFileDescriptorMock);
}
@@ -595,7 +654,7 @@
@Test
public void acknowledgeFullBackupOrRestoreForUser_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.acknowledgeFullBackupOrRestoreForUser(
mUserId,
@@ -605,17 +664,32 @@
ENCRYPTION_PASSWORD,
mFullBackupRestoreObserverMock);
- verify(mBackupManagerServiceMock).acknowledgeAdbBackupOrRestore(123, true, CURRENT_PASSWORD,
- ENCRYPTION_PASSWORD, mFullBackupRestoreObserverMock);
+ verify(mBackupManagerServiceMock)
+ .acknowledgeAdbBackupOrRestore(
+ mUserId,
+ 123,
+ true,
+ CURRENT_PASSWORD,
+ ENCRYPTION_PASSWORD,
+ mFullBackupRestoreObserverMock);
}
@Test
public void acknowledgeFullBackupOrRestore_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService();
+
mTrampoline.acknowledgeFullBackupOrRestore(123, true, CURRENT_PASSWORD, ENCRYPTION_PASSWORD,
mFullBackupRestoreObserverMock);
- verify(mBackupManagerServiceMock).acknowledgeAdbBackupOrRestore(123, true, CURRENT_PASSWORD,
- ENCRYPTION_PASSWORD, mFullBackupRestoreObserverMock);
+
+ verify(mBackupManagerServiceMock)
+ .acknowledgeAdbBackupOrRestore(
+ mUserId,
+ 123,
+ true,
+ CURRENT_PASSWORD,
+ ENCRYPTION_PASSWORD,
+ mFullBackupRestoreObserverMock);
}
@Test
@@ -626,22 +700,21 @@
@Test
public void getCurrentTransportForUser_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.getCurrentTransport()).thenReturn(TRANSPORT_NAME);
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ when(mBackupManagerServiceMock.getCurrentTransport(mUserId)).thenReturn(TRANSPORT_NAME);
+ mTrampoline.initializeService();
assertEquals(TRANSPORT_NAME, mTrampoline.getCurrentTransportForUser(mUserId));
-
- verify(mBackupManagerServiceMock).getCurrentTransport();
+ verify(mBackupManagerServiceMock).getCurrentTransport(mUserId);
}
@Test
public void getCurrentTransport_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.getCurrentTransport()).thenReturn(TRANSPORT_NAME);
-
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ TrampolineTestable.sCallingUserId = mUserId;
+ when(mBackupManagerServiceMock.getCurrentTransport(mUserId)).thenReturn(TRANSPORT_NAME);
+ mTrampoline.initializeService();
assertEquals(TRANSPORT_NAME, mTrampoline.getCurrentTransport());
- verify(mBackupManagerServiceMock).getCurrentTransport();
+ verify(mBackupManagerServiceMock).getCurrentTransport(mUserId);
}
@Test
@@ -652,21 +725,22 @@
@Test
public void listAllTransportsForUser_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.listAllTransports()).thenReturn(TRANSPORTS);
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ when(mBackupManagerServiceMock.listAllTransports(mUserId)).thenReturn(TRANSPORTS);
+ mTrampoline.initializeService();
assertEquals(TRANSPORTS, mTrampoline.listAllTransportsForUser(mUserId));
- verify(mBackupManagerServiceMock).listAllTransports();
+ verify(mBackupManagerServiceMock).listAllTransports(mUserId);
}
@Test
public void listAllTransports_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.listAllTransports()).thenReturn(TRANSPORTS);
+ TrampolineTestable.sCallingUserId = mUserId;
+ when(mBackupManagerServiceMock.listAllTransports(mUserId)).thenReturn(TRANSPORTS);
+ mTrampoline.initializeService();
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
assertEquals(TRANSPORTS, mTrampoline.listAllTransports());
- verify(mBackupManagerServiceMock).listAllTransports();
+ verify(mBackupManagerServiceMock).listAllTransports(mUserId);
}
@Test
@@ -678,12 +752,12 @@
@Test
public void listAllTransportComponentsForUser_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.listAllTransportComponents()).thenReturn(
+ when(mBackupManagerServiceMock.listAllTransportComponents(mUserId)).thenReturn(
TRANSPORT_COMPONENTS);
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
assertEquals(TRANSPORT_COMPONENTS, mTrampoline.listAllTransportComponentsForUser(mUserId));
- verify(mBackupManagerServiceMock).listAllTransportComponents();
+ verify(mBackupManagerServiceMock).listAllTransportComponents(mUserId);
}
@Test
@@ -695,8 +769,8 @@
@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();
}
@@ -719,7 +793,7 @@
@Test
public void updateTransportAttributesForUser_forwarded() throws RemoteException {
when(mBackupManagerServiceMock.getTransportWhitelist()).thenReturn(TRANSPORTS);
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.updateTransportAttributesForUser(
mUserId,
@@ -730,8 +804,15 @@
null,
"Data Management");
- verify(mBackupManagerServiceMock).updateTransportAttributes(TRANSPORT_COMPONENT_NAME,
- TRANSPORT_NAME, null, "Transport Destination", null, "Data Management");
+ verify(mBackupManagerServiceMock)
+ .updateTransportAttributes(
+ mUserId,
+ TRANSPORT_COMPONENT_NAME,
+ TRANSPORT_NAME,
+ null,
+ "Transport Destination",
+ null,
+ "Data Management");
}
@Test
@@ -742,18 +823,21 @@
@Test
public void selectBackupTransportForUser_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.selectBackupTransportForUser(mUserId, TRANSPORT_NAME);
- verify(mBackupManagerServiceMock).selectBackupTransport(TRANSPORT_NAME);
+ verify(mBackupManagerServiceMock).selectBackupTransport(mUserId, TRANSPORT_NAME);
}
@Test
public void selectBackupTransport_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService();
+
mTrampoline.selectBackupTransport(TRANSPORT_NAME);
- verify(mBackupManagerServiceMock).selectBackupTransport(TRANSPORT_NAME);
+
+ verify(mBackupManagerServiceMock).selectBackupTransport(mUserId, TRANSPORT_NAME);
}
@Test
@@ -825,12 +909,12 @@
@Test
public void selectBackupTransportAsyncForUser_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, null);
- verify(mBackupManagerServiceMock).selectBackupTransportAsync(TRANSPORT_COMPONENT_NAME,
- null);
+ verify(mBackupManagerServiceMock)
+ .selectBackupTransportAsync(mUserId, TRANSPORT_COMPONENT_NAME, null);
}
@Test
@@ -842,25 +926,26 @@
@Test
public void getConfigurationIntentForUser_forwarded() throws RemoteException {
Intent configurationIntentStub = new Intent();
- when(mBackupManagerServiceMock.getConfigurationIntent(TRANSPORT_NAME)).thenReturn(
+ when(mBackupManagerServiceMock.getConfigurationIntent(mUserId, TRANSPORT_NAME)).thenReturn(
configurationIntentStub);
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
assertEquals(
configurationIntentStub,
mTrampoline.getConfigurationIntentForUser(mUserId, TRANSPORT_NAME));
- verify(mBackupManagerServiceMock).getConfigurationIntent(TRANSPORT_NAME);
+ verify(mBackupManagerServiceMock).getConfigurationIntent(mUserId, TRANSPORT_NAME);
}
@Test
public void getConfigurationIntent_forwarded() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
Intent configurationIntentStub = new Intent();
- when(mBackupManagerServiceMock.getConfigurationIntent(TRANSPORT_NAME)).thenReturn(
+ when(mBackupManagerServiceMock.getConfigurationIntent(mUserId, TRANSPORT_NAME)).thenReturn(
configurationIntentStub);
+ mTrampoline.initializeService();
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
assertEquals(configurationIntentStub, mTrampoline.getConfigurationIntent(TRANSPORT_NAME));
- verify(mBackupManagerServiceMock).getConfigurationIntent(TRANSPORT_NAME);
+ verify(mBackupManagerServiceMock).getConfigurationIntent(mUserId, TRANSPORT_NAME);
}
@Test
@@ -871,24 +956,25 @@
@Test
public void getDestinationStringForUser_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.getDestinationString(TRANSPORT_NAME)).thenReturn(
+ when(mBackupManagerServiceMock.getDestinationString(mUserId, TRANSPORT_NAME)).thenReturn(
DESTINATION_STRING);
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
assertEquals(
DESTINATION_STRING,
mTrampoline.getDestinationStringForUser(mUserId, TRANSPORT_NAME));
- verify(mBackupManagerServiceMock).getDestinationString(TRANSPORT_NAME);
+ verify(mBackupManagerServiceMock).getDestinationString(mUserId, TRANSPORT_NAME);
}
@Test
public void getDestinationString_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.getDestinationString(TRANSPORT_NAME)).thenReturn(
+ TrampolineTestable.sCallingUserId = mUserId;
+ 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(TRANSPORT_NAME);
+ verify(mBackupManagerServiceMock).getDestinationString(mUserId, TRANSPORT_NAME);
}
@Test
@@ -900,25 +986,26 @@
@Test
public void getDataManagementIntentForUser_forwarded() throws RemoteException {
Intent dataManagementIntent = new Intent();
- when(mBackupManagerServiceMock.getDataManagementIntent(TRANSPORT_NAME)).thenReturn(
+ when(mBackupManagerServiceMock.getDataManagementIntent(mUserId, TRANSPORT_NAME)).thenReturn(
dataManagementIntent);
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
assertEquals(
dataManagementIntent,
mTrampoline.getDataManagementIntentForUser(mUserId, TRANSPORT_NAME));
- verify(mBackupManagerServiceMock).getDataManagementIntent(TRANSPORT_NAME);
+ verify(mBackupManagerServiceMock).getDataManagementIntent(mUserId, TRANSPORT_NAME);
}
@Test
public void getDataManagementIntent_forwarded() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
Intent dataManagementIntent = new Intent();
- when(mBackupManagerServiceMock.getDataManagementIntent(TRANSPORT_NAME)).thenReturn(
+ when(mBackupManagerServiceMock.getDataManagementIntent(mUserId, TRANSPORT_NAME)).thenReturn(
dataManagementIntent);
+ mTrampoline.initializeService();
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
assertEquals(dataManagementIntent, mTrampoline.getDataManagementIntent(TRANSPORT_NAME));
- verify(mBackupManagerServiceMock).getDataManagementIntent(TRANSPORT_NAME);
+ verify(mBackupManagerServiceMock).getDataManagementIntent(mUserId, TRANSPORT_NAME);
}
@Test
@@ -929,24 +1016,25 @@
@Test
public void getDataManagementLabelForUser_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.getDataManagementLabel(TRANSPORT_NAME)).thenReturn(
+ when(mBackupManagerServiceMock.getDataManagementLabel(mUserId, TRANSPORT_NAME)).thenReturn(
DATA_MANAGEMENT_LABEL);
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
assertEquals(
DATA_MANAGEMENT_LABEL,
mTrampoline.getDataManagementLabelForUser(mUserId, TRANSPORT_NAME));
- verify(mBackupManagerServiceMock).getDataManagementLabel(TRANSPORT_NAME);
+ verify(mBackupManagerServiceMock).getDataManagementLabel(mUserId, TRANSPORT_NAME);
}
@Test
public void getDataManagementLabel_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.getDataManagementLabel(TRANSPORT_NAME)).thenReturn(
+ TrampolineTestable.sCallingUserId = mUserId;
+ when(mBackupManagerServiceMock.getDataManagementLabel(mUserId, TRANSPORT_NAME)).thenReturn(
DATA_MANAGEMENT_LABEL);
+ mTrampoline.initializeService();
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
assertEquals(DATA_MANAGEMENT_LABEL, mTrampoline.getDataManagementLabel(TRANSPORT_NAME));
- verify(mBackupManagerServiceMock).getDataManagementLabel(TRANSPORT_NAME);
+ verify(mBackupManagerServiceMock).getDataManagementLabel(mUserId, TRANSPORT_NAME);
}
@Test
@@ -957,11 +1045,12 @@
@Test
public void beginRestoreSessionForUser_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.beginRestoreSessionForUser(mUserId, PACKAGE_NAME, TRANSPORT_NAME);
- verify(mBackupManagerServiceMock).beginRestoreSession(PACKAGE_NAME, TRANSPORT_NAME);
+ verify(mBackupManagerServiceMock)
+ .beginRestoreSession(mUserId, PACKAGE_NAME, TRANSPORT_NAME);
}
@Test
@@ -972,9 +1061,12 @@
@Test
public void opComplete_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService();
+
mTrampoline.opComplete(1, 2);
- verify(mBackupManagerServiceMock).opComplete(1, 2);
+
+ verify(mBackupManagerServiceMock).opComplete(mUserId, 1, 2);
}
@Test
@@ -986,11 +1078,12 @@
@Test
public void getAvailableRestoreTokenForUser_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.getAvailableRestoreToken(PACKAGE_NAME)).thenReturn(123L);
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ when(mBackupManagerServiceMock.getAvailableRestoreToken(mUserId, PACKAGE_NAME))
+ .thenReturn(123L);
+ mTrampoline.initializeService();
assertEquals(123, mTrampoline.getAvailableRestoreTokenForUser(mUserId, PACKAGE_NAME));
- verify(mBackupManagerServiceMock).getAvailableRestoreToken(PACKAGE_NAME);
+ verify(mBackupManagerServiceMock).getAvailableRestoreToken(mUserId, PACKAGE_NAME);
}
@Test
@@ -1002,11 +1095,12 @@
@Test
public void isAppEligibleForBackupForUser_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.isAppEligibleForBackup(PACKAGE_NAME)).thenReturn(true);
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ when(mBackupManagerServiceMock.isAppEligibleForBackup(mUserId, PACKAGE_NAME))
+ .thenReturn(true);
+ mTrampoline.initializeService();
assertTrue(mTrampoline.isAppEligibleForBackupForUser(mUserId, PACKAGE_NAME));
- verify(mBackupManagerServiceMock).isAppEligibleForBackup(PACKAGE_NAME);
+ verify(mBackupManagerServiceMock).isAppEligibleForBackup(mUserId, PACKAGE_NAME);
}
@Test
@@ -1020,7 +1114,7 @@
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));
@@ -1033,7 +1127,7 @@
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));
@@ -1049,7 +1143,7 @@
@Test
public void cancelBackupsForUser_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.cancelBackupsForUser(mUserId);
@@ -1059,7 +1153,7 @@
@Test
public void cancelBackups_forwardedToCallingUserId() throws RemoteException {
TrampolineTestable.sCallingUserId = mUserId;
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.cancelBackups();
@@ -1077,7 +1171,7 @@
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);
}
@@ -1090,7 +1184,7 @@
@Test
public void endFullBackup_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.endFullBackup();
verify(mBackupManagerServiceMock).endFullBackup();
}
@@ -1100,8 +1194,7 @@
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]);
@@ -1124,8 +1217,7 @@
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 2aed35f..83c1c76 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -232,11 +232,6 @@
}
@Override
- protected void reportSeen(NotificationRecord r) {
- return;
- }
-
- @Override
protected void reportUserInteraction(NotificationRecord r) {
return;
}
@@ -3823,7 +3818,7 @@
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addNotification(r);
- NotificationVisibility[] notificationVisibility = new NotificationVisibility[] {
+ NotificationVisibility[] notificationVisibility = new NotificationVisibility[]{
NotificationVisibility.obtain(r.getKey(), 0, 0, true)
};
mService.mNotificationDelegate.onNotificationVisibilityChanged(notificationVisibility,
@@ -3831,4 +3826,49 @@
assertEquals(0, mService.countLogSmartSuggestionsVisible);
}
+
+ public void testReportSeen_delegated() {
+ Notification.Builder nb =
+ new Notification.Builder(mContext, mTestNotificationChannel.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+ StatusBarNotification sbn = new StatusBarNotification(PKG, "opPkg", 0, "tag", mUid, 0,
+ nb.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mService.reportSeen(r);
+ verify(mAppUsageStats, never()).reportEvent(anyString(), anyInt(), anyInt());
+
+ }
+
+ @Test
+ public void testReportSeen_notDelegated() {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+
+ 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 92b4dbb..bc62de1 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 @@
// 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 8430616..3826fac 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_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 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 @@
@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 @@
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 bf4b52e..6b31e6f 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.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 @@
}
@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 @@
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 83e7ee7..dfdbf32 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 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 @@
@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 60f957f..e156143 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.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.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 @@
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 @@
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/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
index 522ab9f..04e433e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
@@ -164,6 +164,7 @@
}
private void tearDown() {
+ cancelAllPendingAnimations();
waitUntilWindowManagerHandlersIdle();
destroyAllSurfaceTransactions();
destroyAllSurfaceControls();
@@ -178,6 +179,15 @@
return mService;
}
+ private void cancelAllPendingAnimations() {
+ for (final WeakReference<SurfaceControl> reference : mSurfaceControls) {
+ final SurfaceControl sc = reference.get();
+ if (sc != null) {
+ mService.mSurfaceAnimationRunner.onAnimationCancelled(sc);
+ }
+ }
+ }
+
void waitUntilWindowManagerHandlersIdle() {
final WindowManagerService wm = getWindowManagerService();
if (wm == null) {
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index d6b40ae..38efc74 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 @@
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 @@
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 @@
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 @@
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 06059c8..757d863 100644
--- a/startop/view_compiler/dex_builder.h
+++ b/startop/view_compiler/dex_builder.h
@@ -73,7 +73,7 @@
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 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 9351dc3..55bfdc7 100644
--- a/startop/view_compiler/main.cc
+++ b/startop/view_compiler/main.cc
@@ -42,7 +42,7 @@
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 1cbe5a2..c115a4b 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -2732,10 +2732,26 @@
/**
* 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 @@
/**
* 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 @@
"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 @@
/**
* 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 @@
* 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 @@
* <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 @@
})
@Retention(RetentionPolicy.SOURCE)
public @interface EditStatus {}
-
}
/**
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index c6f7d0e..c53b37d 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -15,151 +15,383 @@
*/
package android.telephony;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.content.Context;
import android.os.PersistableBundle;
+import com.android.internal.util.ArrayUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
/**
- * Returned as the reason for a connection failure as defined
- * by RIL_DataCallFailCause in ril.h and some local errors.
+ * Returned as the reason for a data connection failure as defined by modem and some local errors.
* @hide
*/
-public enum DataFailCause {
- NONE(0),
+public final class DataFailCause {
+ /** There is no failure */
+ public static final int NONE = 0;
// This series of errors as specified by the standards
// specified in ril.h
- OPERATOR_BARRED(0x08), /* no retry */
- NAS_SIGNALLING(0x0E),
- LLC_SNDCP(0x19),
- INSUFFICIENT_RESOURCES(0x1A),
- MISSING_UNKNOWN_APN(0x1B), /* no retry */
- UNKNOWN_PDP_ADDRESS_TYPE(0x1C), /* no retry */
- USER_AUTHENTICATION(0x1D), /* no retry */
- ACTIVATION_REJECT_GGSN(0x1E), /* no retry */
- ACTIVATION_REJECT_UNSPECIFIED(0x1F),
- SERVICE_OPTION_NOT_SUPPORTED(0x20), /* no retry */
- SERVICE_OPTION_NOT_SUBSCRIBED(0x21), /* no retry */
- SERVICE_OPTION_OUT_OF_ORDER(0x22),
- NSAPI_IN_USE(0x23), /* no retry */
- REGULAR_DEACTIVATION(0x24), /* possibly restart radio, based on config */
- QOS_NOT_ACCEPTED(0x25),
- NETWORK_FAILURE(0x26),
- UMTS_REACTIVATION_REQ(0x27),
- FEATURE_NOT_SUPP(0x28),
- TFT_SEMANTIC_ERROR(0x29),
- TFT_SYTAX_ERROR(0x2A),
- UNKNOWN_PDP_CONTEXT(0x2B),
- FILTER_SEMANTIC_ERROR(0x2C),
- FILTER_SYTAX_ERROR(0x2D),
- PDP_WITHOUT_ACTIVE_TFT(0x2E),
- ONLY_IPV4_ALLOWED(0x32), /* no retry */
- ONLY_IPV6_ALLOWED(0x33), /* no retry */
- ONLY_SINGLE_BEARER_ALLOWED(0x34),
- ESM_INFO_NOT_RECEIVED(0x35),
- PDN_CONN_DOES_NOT_EXIST(0x36),
- MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED(0x37),
- MAX_ACTIVE_PDP_CONTEXT_REACHED(0x41),
- UNSUPPORTED_APN_IN_CURRENT_PLMN(0x42),
- INVALID_TRANSACTION_ID(0x51),
- MESSAGE_INCORRECT_SEMANTIC(0x5F),
- INVALID_MANDATORY_INFO(0x60),
- MESSAGE_TYPE_UNSUPPORTED(0x61),
- MSG_TYPE_NONCOMPATIBLE_STATE(0x62),
- UNKNOWN_INFO_ELEMENT(0x63),
- CONDITIONAL_IE_ERROR(0x64),
- MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE(0x65),
- PROTOCOL_ERRORS(0x6F), /* no retry */
- APN_TYPE_CONFLICT(0x70),
- INVALID_PCSCF_ADDR(0x71),
- INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN(0x72),
- EMM_ACCESS_BARRED(0x73),
- EMERGENCY_IFACE_ONLY(0x74),
- IFACE_MISMATCH(0x75),
- COMPANION_IFACE_IN_USE(0x76),
- IP_ADDRESS_MISMATCH(0x77),
- IFACE_AND_POL_FAMILY_MISMATCH(0x78),
- EMM_ACCESS_BARRED_INFINITE_RETRY(0x79),
- AUTH_FAILURE_ON_EMERGENCY_CALL(0x7A),
+ /** Operator determined barring. */
+ public static final int OPERATOR_BARRED = 0x08;
+ /** NAS signalling. */
+ public static final int NAS_SIGNALLING = 0x0E;
+ /** Logical Link Control (LLC) Sub Network Dependent Convergence Protocol (SNDCP). */
+ public static final int LLC_SNDCP = 0x19;
+ /** Insufficient resources. */
+ public static final int INSUFFICIENT_RESOURCES = 0x1A;
+ /** Missing or unknown APN. */
+ public static final int MISSING_UNKNOWN_APN = 0x1B; /* no retry */
+ /** Unknown Packet Data Protocol (PDP) address type. */
+ public static final int UNKNOWN_PDP_ADDRESS_TYPE = 0x1C; /* no retry */
+ /** User authentication. */
+ public static final int USER_AUTHENTICATION = 0x1D; /* no retry */
+ /** Activation rejected by Gateway GPRS Support Node (GGSN), Serving Gateway or PDN Gateway. */
+ public static final int ACTIVATION_REJECT_GGSN = 0x1E; /* no retry */
+ /** Activation rejected, unspecified. */
+ public static final int ACTIVATION_REJECT_UNSPECIFIED = 0x1F;
+ /** Service option not supported. */
+ public static final int SERVICE_OPTION_NOT_SUPPORTED = 0x20; /* no retry */
+ /** Requested service option not subscribed. */
+ public static final int SERVICE_OPTION_NOT_SUBSCRIBED = 0x21; /* no retry */
+ /** Service option temporarily out of order. */
+ public static final int SERVICE_OPTION_OUT_OF_ORDER = 0x22;
+ /** The Network Service Access Point Identifier (NSAPI) is in use. */
+ public static final int NSAPI_IN_USE = 0x23; /* no retry */
+ /* possibly restart radio, based on config */
+ /** Regular deactivation. */
+ public static final int REGULAR_DEACTIVATION = 0x24;
+ /** Quality of service (QoS) is not accepted. */
+ public static final int QOS_NOT_ACCEPTED = 0x25;
+ /** Network Failure. */
+ public static final int NETWORK_FAILURE = 0x26;
+ /** Universal Mobile Telecommunications System (UMTS) reactivation request. */
+ public static final int UMTS_REACTIVATION_REQ = 0x27;
+ /** Feature not supported. */
+ public static final int FEATURE_NOT_SUPP = 0x28;
+ /** Semantic error in the Traffic flow templates (TFT) operation. */
+ public static final int TFT_SEMANTIC_ERROR = 0x29;
+ /** Syntactical error in the Traffic flow templates (TFT) operation. */
+ public static final int TFT_SYTAX_ERROR = 0x2A;
+ /** Unknown Packet Data Protocol (PDP) context. */
+ public static final int UNKNOWN_PDP_CONTEXT = 0x2B;
+ /** Semantic errors in packet filter. */
+ public static final int FILTER_SEMANTIC_ERROR = 0x2C;
+ /** Syntactical errors in packet filter(s). */
+ public static final int FILTER_SYTAX_ERROR = 0x2D;
+ /** Packet Data Protocol (PDP) without active traffic flow template (TFT). */
+ public static final int PDP_WITHOUT_ACTIVE_TFT = 0x2E;
+ /** Packet Data Protocol (PDP) type IPv4 only allowed. */
+ public static final int ONLY_IPV4_ALLOWED = 0x32; /* no retry */
+ /** Packet Data Protocol (PDP) type IPv6 only allowed. */
+ public static final int ONLY_IPV6_ALLOWED = 0x33; /* no retry */
+ /** Single address bearers only allowed. */
+ public static final int ONLY_SINGLE_BEARER_ALLOWED = 0x34;
+ /** EPS Session Management (ESM) information is not received. */
+ public static final int ESM_INFO_NOT_RECEIVED = 0x35;
+ /** PDN connection does not exist. */
+ public static final int PDN_CONN_DOES_NOT_EXIST = 0x36;
+ /** Multiple connections to a same PDN is not allowed. */
+ public static final int MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED = 0x37;
+ /** Packet Data Protocol (PDP) */
+ public static final int MAX_ACTIVE_PDP_CONTEXT_REACHED = 0x41;
+ /** Unsupported APN in current public land mobile network (PLMN). */
+ public static final int UNSUPPORTED_APN_IN_CURRENT_PLMN = 0x42;
+ /** Invalid transaction id. */
+ public static final int INVALID_TRANSACTION_ID = 0x51;
+ /** Incorrect message semantic. */
+ public static final int MESSAGE_INCORRECT_SEMANTIC = 0x5F;
+ /** Invalid mandatory information. */
+ public static final int INVALID_MANDATORY_INFO = 0x60;
+ /** Unsupported message type. */
+ public static final int MESSAGE_TYPE_UNSUPPORTED = 0x61;
+ /** Message type uncompatible. */
+ public static final int MSG_TYPE_NONCOMPATIBLE_STATE = 0x62;
+ /** Unknown info element. */
+ public static final int UNKNOWN_INFO_ELEMENT = 0x63;
+ /** Conditional Information Element (IE) error. */
+ public static final int CONDITIONAL_IE_ERROR = 0x64;
+ /** Message and protocol state uncompatible. */
+ public static final int MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE = 0x65;
+ /** Protocol errors. */
+ public static final int PROTOCOL_ERRORS = 0x6F; /* no retry */
+ /** APN type conflict. */
+ public static final int APN_TYPE_CONFLICT = 0x70;
+ /** Invalid Proxy-Call Session Control Function (P-CSCF) address. */
+ public static final int INVALID_PCSCF_ADDR = 0x71;
+ /** Internal data call preempt by high priority APN. */
+ public static final int INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN = 0x72;
+ /** EPS (Evolved Packet System) Mobility Management (EMM) access barred. */
+ public static final int EMM_ACCESS_BARRED = 0x73;
+ /** Emergency interface only. */
+ public static final int EMERGENCY_IFACE_ONLY = 0x74;
+ /** Interface mismatch. */
+ public static final int IFACE_MISMATCH = 0x75;
+ /** Companion interface in use. */
+ public static final int COMPANION_IFACE_IN_USE = 0x76;
+ /** IP address mismatch. */
+ public static final int IP_ADDRESS_MISMATCH = 0x77;
+ public static final int IFACE_AND_POL_FAMILY_MISMATCH = 0x78;
+ /** EPS (Evolved Packet System) Mobility Management (EMM) access barred infinity retry. **/
+ public static final int EMM_ACCESS_BARRED_INFINITE_RETRY = 0x79;
+ /** Authentication failure on emergency call. */
+ public static final int AUTH_FAILURE_ON_EMERGENCY_CALL = 0x7A;
// OEM sepecific error codes. To be used by OEMs when they don't
// want to reveal error code which would be replaced by ERROR_UNSPECIFIED
- OEM_DCFAILCAUSE_1(0x1001),
- OEM_DCFAILCAUSE_2(0x1002),
- OEM_DCFAILCAUSE_3(0x1003),
- OEM_DCFAILCAUSE_4(0x1004),
- OEM_DCFAILCAUSE_5(0x1005),
- OEM_DCFAILCAUSE_6(0x1006),
- OEM_DCFAILCAUSE_7(0x1007),
- OEM_DCFAILCAUSE_8(0x1008),
- OEM_DCFAILCAUSE_9(0x1009),
- OEM_DCFAILCAUSE_10(0x100A),
- OEM_DCFAILCAUSE_11(0x100B),
- OEM_DCFAILCAUSE_12(0x100C),
- OEM_DCFAILCAUSE_13(0x100D),
- OEM_DCFAILCAUSE_14(0x100E),
- OEM_DCFAILCAUSE_15(0x100F),
+ public static final int OEM_DCFAILCAUSE_1 = 0x1001;
+ public static final int OEM_DCFAILCAUSE_2 = 0x1002;
+ public static final int OEM_DCFAILCAUSE_3 = 0x1003;
+ public static final int OEM_DCFAILCAUSE_4 = 0x1004;
+ public static final int OEM_DCFAILCAUSE_5 = 0x1005;
+ public static final int OEM_DCFAILCAUSE_6 = 0x1006;
+ public static final int OEM_DCFAILCAUSE_7 = 0x1007;
+ public static final int OEM_DCFAILCAUSE_8 = 0x1008;
+ public static final int OEM_DCFAILCAUSE_9 = 0x1009;
+ public static final int OEM_DCFAILCAUSE_10 = 0x100A;
+ public static final int OEM_DCFAILCAUSE_11 = 0x100B;
+ public static final int OEM_DCFAILCAUSE_12 = 0x100C;
+ public static final int OEM_DCFAILCAUSE_13 = 0x100D;
+ public static final int OEM_DCFAILCAUSE_14 = 0x100E;
+ public static final int OEM_DCFAILCAUSE_15 = 0x100F;
// Local errors generated by Vendor RIL
// specified in ril.h
- REGISTRATION_FAIL(-1),
- GPRS_REGISTRATION_FAIL(-2),
- SIGNAL_LOST(-3), /* no retry */
- PREF_RADIO_TECH_CHANGED(-4),
- RADIO_POWER_OFF(-5), /* no retry */
- TETHERED_CALL_ACTIVE(-6), /* no retry */
- ERROR_UNSPECIFIED(0xFFFF),
+ public static final int REGISTRATION_FAIL = -1;
+ public static final int GPRS_REGISTRATION_FAIL = -2;
+ public static final int SIGNAL_LOST = -3; /* no retry */
+ public static final int PREF_RADIO_TECH_CHANGED = -4;
+ public static final int RADIO_POWER_OFF = -5; /* no retry */
+ public static final int TETHERED_CALL_ACTIVE = -6; /* no retry */
+ public static final int ERROR_UNSPECIFIED = 0xFFFF;
// Errors generated by the Framework
// specified here
- UNKNOWN(0x10000),
- RADIO_NOT_AVAILABLE(0x10001), /* no retry */
- UNACCEPTABLE_NETWORK_PARAMETER(0x10002), /* no retry */
- CONNECTION_TO_DATACONNECTIONAC_BROKEN(0x10003),
- LOST_CONNECTION(0x10004),
- RESET_BY_FRAMEWORK(0x10005);
+ public static final int UNKNOWN = 0x10000;
+ public static final int RADIO_NOT_AVAILABLE = 0x10001; /* no retry */
+ public static final int UNACCEPTABLE_NETWORK_PARAMETER = 0x10002; /* no retry */
+ public static final int CONNECTION_TO_DATACONNECTIONAC_BROKEN = 0x10003;
+ public static final int LOST_CONNECTION = 0x10004;
+ /** Data was reset by framework. */
+ public static final int RESET_BY_FRAMEWORK = 0x10005;
- private final int mErrorCode;
- private static final HashMap<Integer, DataFailCause> sErrorCodeToFailCauseMap;
+ /** @hide */
+ @IntDef(value = {
+ NONE,
+ OPERATOR_BARRED,
+ NAS_SIGNALLING,
+ LLC_SNDCP,
+ INSUFFICIENT_RESOURCES,
+ MISSING_UNKNOWN_APN,
+ UNKNOWN_PDP_ADDRESS_TYPE,
+ USER_AUTHENTICATION,
+ ACTIVATION_REJECT_GGSN,
+ ACTIVATION_REJECT_UNSPECIFIED,
+ SERVICE_OPTION_NOT_SUPPORTED,
+ SERVICE_OPTION_NOT_SUBSCRIBED,
+ SERVICE_OPTION_OUT_OF_ORDER,
+ NSAPI_IN_USE,
+ REGULAR_DEACTIVATION,
+ QOS_NOT_ACCEPTED,
+ NETWORK_FAILURE,
+ UMTS_REACTIVATION_REQ,
+ FEATURE_NOT_SUPP,
+ TFT_SEMANTIC_ERROR,
+ TFT_SYTAX_ERROR,
+ UNKNOWN_PDP_CONTEXT,
+ FILTER_SEMANTIC_ERROR,
+ FILTER_SYTAX_ERROR,
+ PDP_WITHOUT_ACTIVE_TFT,
+ ONLY_IPV4_ALLOWED,
+ ONLY_IPV6_ALLOWED,
+ ONLY_SINGLE_BEARER_ALLOWED,
+ ESM_INFO_NOT_RECEIVED,
+ PDN_CONN_DOES_NOT_EXIST,
+ MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED,
+ MAX_ACTIVE_PDP_CONTEXT_REACHED,
+ UNSUPPORTED_APN_IN_CURRENT_PLMN,
+ INVALID_TRANSACTION_ID,
+ MESSAGE_INCORRECT_SEMANTIC,
+ INVALID_MANDATORY_INFO,
+ MESSAGE_TYPE_UNSUPPORTED,
+ MSG_TYPE_NONCOMPATIBLE_STATE,
+ UNKNOWN_INFO_ELEMENT,
+ CONDITIONAL_IE_ERROR,
+ MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE,
+ PROTOCOL_ERRORS, /* no retry */
+ APN_TYPE_CONFLICT,
+ INVALID_PCSCF_ADDR,
+ INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN,
+ EMM_ACCESS_BARRED,
+ EMERGENCY_IFACE_ONLY,
+ IFACE_MISMATCH,
+ COMPANION_IFACE_IN_USE,
+ IP_ADDRESS_MISMATCH,
+ IFACE_AND_POL_FAMILY_MISMATCH,
+ EMM_ACCESS_BARRED_INFINITE_RETRY,
+ AUTH_FAILURE_ON_EMERGENCY_CALL,
+ OEM_DCFAILCAUSE_1,
+ OEM_DCFAILCAUSE_2,
+ OEM_DCFAILCAUSE_3,
+ OEM_DCFAILCAUSE_4,
+ OEM_DCFAILCAUSE_5,
+ OEM_DCFAILCAUSE_6,
+ OEM_DCFAILCAUSE_7,
+ OEM_DCFAILCAUSE_8,
+ OEM_DCFAILCAUSE_9,
+ OEM_DCFAILCAUSE_10,
+ OEM_DCFAILCAUSE_11,
+ OEM_DCFAILCAUSE_12,
+ OEM_DCFAILCAUSE_13,
+ OEM_DCFAILCAUSE_14,
+ OEM_DCFAILCAUSE_15,
+ REGISTRATION_FAIL,
+ GPRS_REGISTRATION_FAIL,
+ SIGNAL_LOST,
+ PREF_RADIO_TECH_CHANGED,
+ RADIO_POWER_OFF,
+ TETHERED_CALL_ACTIVE,
+ ERROR_UNSPECIFIED,
+ UNKNOWN,
+ RADIO_NOT_AVAILABLE,
+ UNACCEPTABLE_NETWORK_PARAMETER,
+ CONNECTION_TO_DATACONNECTIONAC_BROKEN,
+ LOST_CONNECTION,
+ RESET_BY_FRAMEWORK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FailCause{}
+
+ private static final Map<Integer, String> sFailCauseMap;
static {
- sErrorCodeToFailCauseMap = new HashMap<Integer, DataFailCause>();
- for (DataFailCause fc : values()) {
- sErrorCodeToFailCauseMap.put(fc.getErrorCode(), fc);
- }
+ sFailCauseMap = new HashMap<>();
+ sFailCauseMap.put(NONE, "NONE");
+ sFailCauseMap.put(OPERATOR_BARRED, "OPERATOR_BARRED");
+ sFailCauseMap.put(NAS_SIGNALLING, "NAS_SIGNALLING");
+ sFailCauseMap.put(LLC_SNDCP, "LLC_SNDCP");
+ sFailCauseMap.put(INSUFFICIENT_RESOURCES, "INSUFFICIENT_RESOURCES");
+ sFailCauseMap.put(MISSING_UNKNOWN_APN, "MISSING_UNKNOWN_APN");
+ sFailCauseMap.put(UNKNOWN_PDP_ADDRESS_TYPE, "UNKNOWN_PDP_ADDRESS_TYPE");
+ sFailCauseMap.put(USER_AUTHENTICATION, "USER_AUTHENTICATION");
+ sFailCauseMap.put(ACTIVATION_REJECT_GGSN, "ACTIVATION_REJECT_GGSN");
+ sFailCauseMap.put(ACTIVATION_REJECT_UNSPECIFIED,
+ "ACTIVATION_REJECT_UNSPECIFIED");
+ sFailCauseMap.put(SERVICE_OPTION_NOT_SUPPORTED,
+ "SERVICE_OPTION_NOT_SUPPORTED");
+ sFailCauseMap.put(SERVICE_OPTION_NOT_SUBSCRIBED,
+ "SERVICE_OPTION_NOT_SUBSCRIBED");
+ sFailCauseMap.put(SERVICE_OPTION_OUT_OF_ORDER, "SERVICE_OPTION_OUT_OF_ORDER");
+ sFailCauseMap.put(NSAPI_IN_USE, "NSAPI_IN_USE");
+ sFailCauseMap.put(REGULAR_DEACTIVATION, "REGULAR_DEACTIVATION");
+ sFailCauseMap.put(QOS_NOT_ACCEPTED, "QOS_NOT_ACCEPTED");
+ sFailCauseMap.put(NETWORK_FAILURE, "NETWORK_FAILURE");
+ sFailCauseMap.put(UMTS_REACTIVATION_REQ, "UMTS_REACTIVATION_REQ");
+ sFailCauseMap.put(FEATURE_NOT_SUPP, "FEATURE_NOT_SUPP");
+ sFailCauseMap.put(TFT_SEMANTIC_ERROR, "TFT_SEMANTIC_ERROR");
+ sFailCauseMap.put(TFT_SYTAX_ERROR, "TFT_SYTAX_ERROR");
+ sFailCauseMap.put(UNKNOWN_PDP_CONTEXT, "UNKNOWN_PDP_CONTEXT");
+ sFailCauseMap.put(FILTER_SEMANTIC_ERROR, "FILTER_SEMANTIC_ERROR");
+ sFailCauseMap.put(FILTER_SYTAX_ERROR, "FILTER_SYTAX_ERROR");
+ sFailCauseMap.put(PDP_WITHOUT_ACTIVE_TFT, "PDP_WITHOUT_ACTIVE_TFT");
+ sFailCauseMap.put(ONLY_IPV4_ALLOWED, "ONLY_IPV4_ALLOWED");
+ sFailCauseMap.put(ONLY_IPV6_ALLOWED, "ONLY_IPV6_ALLOWED");
+ sFailCauseMap.put(ONLY_SINGLE_BEARER_ALLOWED, "ONLY_SINGLE_BEARER_ALLOWED");
+ sFailCauseMap.put(ESM_INFO_NOT_RECEIVED, "ESM_INFO_NOT_RECEIVED");
+ sFailCauseMap.put(PDN_CONN_DOES_NOT_EXIST, "PDN_CONN_DOES_NOT_EXIST");
+ sFailCauseMap.put(MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED,
+ "MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED");
+ sFailCauseMap.put(MAX_ACTIVE_PDP_CONTEXT_REACHED,
+ "MAX_ACTIVE_PDP_CONTEXT_REACHED");
+ sFailCauseMap.put(UNSUPPORTED_APN_IN_CURRENT_PLMN,
+ "UNSUPPORTED_APN_IN_CURRENT_PLMN");
+ sFailCauseMap.put(INVALID_TRANSACTION_ID, "INVALID_TRANSACTION_ID");
+ sFailCauseMap.put(MESSAGE_INCORRECT_SEMANTIC, "MESSAGE_INCORRECT_SEMANTIC");
+ sFailCauseMap.put(INVALID_MANDATORY_INFO, "INVALID_MANDATORY_INFO");
+ sFailCauseMap.put(MESSAGE_TYPE_UNSUPPORTED, "MESSAGE_TYPE_UNSUPPORTED");
+ sFailCauseMap.put(MSG_TYPE_NONCOMPATIBLE_STATE, "MSG_TYPE_NONCOMPATIBLE_STATE");
+ sFailCauseMap.put(UNKNOWN_INFO_ELEMENT, "UNKNOWN_INFO_ELEMENT");
+ sFailCauseMap.put(CONDITIONAL_IE_ERROR, "CONDITIONAL_IE_ERROR");
+ sFailCauseMap.put(MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE,
+ "MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE");
+ sFailCauseMap.put(PROTOCOL_ERRORS, "PROTOCOL_ERRORS");
+ sFailCauseMap.put(APN_TYPE_CONFLICT, "APN_TYPE_CONFLICT");
+ sFailCauseMap.put(INVALID_PCSCF_ADDR, "INVALID_PCSCF_ADDR");
+ sFailCauseMap.put(INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN,
+ "INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN");
+ sFailCauseMap.put(EMM_ACCESS_BARRED, "EMM_ACCESS_BARRED");
+ sFailCauseMap.put(EMERGENCY_IFACE_ONLY, "EMERGENCY_IFACE_ONLY");
+ sFailCauseMap.put(IFACE_MISMATCH, "IFACE_MISMATCH");
+ sFailCauseMap.put(COMPANION_IFACE_IN_USE, "COMPANION_IFACE_IN_USE");
+ sFailCauseMap.put(IP_ADDRESS_MISMATCH, "IP_ADDRESS_MISMATCH");
+ sFailCauseMap.put(IFACE_AND_POL_FAMILY_MISMATCH,
+ "IFACE_AND_POL_FAMILY_MISMATCH");
+ sFailCauseMap.put(EMM_ACCESS_BARRED_INFINITE_RETRY,
+ "EMM_ACCESS_BARRED_INFINITE_RETRY");
+ sFailCauseMap.put(AUTH_FAILURE_ON_EMERGENCY_CALL,
+ "AUTH_FAILURE_ON_EMERGENCY_CALL");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_1, "OEM_DCFAILCAUSE_1");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_2, "OEM_DCFAILCAUSE_2");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_3, "OEM_DCFAILCAUSE_3");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_4, "OEM_DCFAILCAUSE_4");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_5, "OEM_DCFAILCAUSE_5");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_6, "OEM_DCFAILCAUSE_6");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_7, "OEM_DCFAILCAUSE_7");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_8, "OEM_DCFAILCAUSE_8");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_9, "OEM_DCFAILCAUSE_9");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_10, "OEM_DCFAILCAUSE_10");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_11, "OEM_DCFAILCAUSE_11");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_12, "OEM_DCFAILCAUSE_12");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_13, "OEM_DCFAILCAUSE_13");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_14, "OEM_DCFAILCAUSE_14");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_15, "OEM_DCFAILCAUSE_15");
+ sFailCauseMap.put(REGISTRATION_FAIL, "REGISTRATION_FAIL");
+ sFailCauseMap.put(GPRS_REGISTRATION_FAIL, "GPRS_REGISTRATION_FAIL");
+ sFailCauseMap.put(SIGNAL_LOST, "SIGNAL_LOST");
+ sFailCauseMap.put(PREF_RADIO_TECH_CHANGED, "PREF_RADIO_TECH_CHANGED");
+ sFailCauseMap.put(RADIO_POWER_OFF, "RADIO_POWER_OFF");
+ sFailCauseMap.put(TETHERED_CALL_ACTIVE, "TETHERED_CALL_ACTIVE");
+ sFailCauseMap.put(ERROR_UNSPECIFIED, "ERROR_UNSPECIFIED");
+ sFailCauseMap.put(UNKNOWN, "UNKNOWN");
+ sFailCauseMap.put(RADIO_NOT_AVAILABLE, "RADIO_NOT_AVAILABLE");
+ sFailCauseMap.put(UNACCEPTABLE_NETWORK_PARAMETER,
+ "UNACCEPTABLE_NETWORK_PARAMETER");
+ sFailCauseMap.put(CONNECTION_TO_DATACONNECTIONAC_BROKEN,
+ "CONNECTION_TO_DATACONNECTIONAC_BROKEN");
+ sFailCauseMap.put(LOST_CONNECTION, "LOST_CONNECTION");
+ sFailCauseMap.put(RESET_BY_FRAMEWORK, "RESET_BY_FRAMEWORK");
}
/**
* Map of subId -> set of data call setup permanent failure for the carrier.
*/
- private static final HashMap<Integer, HashSet<DataFailCause>> sPermanentFailureCache =
+ private static final HashMap<Integer, Set<Integer>> sPermanentFailureCache =
new HashMap<>();
- DataFailCause(int errorCode) {
- mErrorCode = errorCode;
- }
-
- public int getErrorCode() {
- return mErrorCode;
- }
-
/**
* Returns whether or not the fail cause is a failure that requires a modem restart
*
* @param context device context
+ * @param cause data disconnect cause
* @param subId subscription index
* @return true if the fail cause code needs platform to trigger a modem restart.
*/
- public boolean isRadioRestartFailure(Context context, int subId) {
+ public static boolean isRadioRestartFailure(@NonNull Context context, @FailCause int cause,
+ int subId) {
CarrierConfigManager configManager = (CarrierConfigManager)
context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
if (configManager != null) {
PersistableBundle b = configManager.getConfigForSubId(subId);
if (b != null) {
- if (this == REGULAR_DEACTIVATION
+ if (cause == REGULAR_DEACTIVATION
&& b.getBoolean(CarrierConfigManager
.KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL)) {
// This is for backward compatibility support. We need to continue support this
@@ -170,7 +402,7 @@
int[] causeCodes = b.getIntArray(CarrierConfigManager
.KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY);
if (causeCodes != null) {
- return Arrays.stream(causeCodes).anyMatch(i -> i == getErrorCode());
+ return Arrays.stream(causeCodes).anyMatch(i -> i == cause);
}
}
}
@@ -178,11 +410,11 @@
return false;
}
- public boolean isPermanentFailure(Context context, int subId) {
-
+ public static boolean isPermanentFailure(@NonNull Context context, @FailCause int failCause,
+ int subId) {
synchronized (sPermanentFailureCache) {
- HashSet<DataFailCause> permanentFailureSet = sPermanentFailureCache.get(subId);
+ Set<Integer> permanentFailureSet = sPermanentFailureCache.get(subId);
// In case of cache miss, we need to look up the settings from carrier config.
if (permanentFailureSet == null) {
@@ -194,11 +426,12 @@
if (b != null) {
String[] permanentFailureStrings = b.getStringArray(CarrierConfigManager.
KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS);
-
if (permanentFailureStrings != null) {
permanentFailureSet = new HashSet<>();
- for (String failure : permanentFailureStrings) {
- permanentFailureSet.add(DataFailCause.valueOf(failure));
+ for (Map.Entry<Integer, String> e : sFailCauseMap.entrySet()) {
+ if (ArrayUtils.contains(permanentFailureStrings, e.getValue())) {
+ permanentFailureSet.add(e.getKey());
+ }
}
}
}
@@ -207,7 +440,7 @@
// If we are not able to find the configuration from carrier config, use the default
// ones.
if (permanentFailureSet == null) {
- permanentFailureSet = new HashSet<DataFailCause>() {
+ permanentFailureSet = new HashSet<Integer>() {
{
add(OPERATOR_BARRED);
add(MISSING_UNKNOWN_APN);
@@ -232,28 +465,39 @@
sPermanentFailureCache.put(subId, permanentFailureSet);
}
- return permanentFailureSet.contains(this);
+ return permanentFailureSet.contains(failCause);
}
}
- public boolean isEventLoggable() {
- return (this == OPERATOR_BARRED) || (this == INSUFFICIENT_RESOURCES) ||
- (this == UNKNOWN_PDP_ADDRESS_TYPE) || (this == USER_AUTHENTICATION) ||
- (this == ACTIVATION_REJECT_GGSN) || (this == ACTIVATION_REJECT_UNSPECIFIED) ||
- (this == SERVICE_OPTION_NOT_SUBSCRIBED) ||
- (this == SERVICE_OPTION_NOT_SUPPORTED) ||
- (this == SERVICE_OPTION_OUT_OF_ORDER) || (this == NSAPI_IN_USE) ||
- (this == ONLY_IPV4_ALLOWED) || (this == ONLY_IPV6_ALLOWED) ||
- (this == PROTOCOL_ERRORS) || (this == SIGNAL_LOST) ||
- (this == RADIO_POWER_OFF) || (this == TETHERED_CALL_ACTIVE) ||
- (this == UNACCEPTABLE_NETWORK_PARAMETER);
+ public static boolean isEventLoggable(@FailCause int dataFailCause) {
+ return (dataFailCause == OPERATOR_BARRED) || (dataFailCause == INSUFFICIENT_RESOURCES)
+ || (dataFailCause == UNKNOWN_PDP_ADDRESS_TYPE)
+ || (dataFailCause == USER_AUTHENTICATION)
+ || (dataFailCause == ACTIVATION_REJECT_GGSN)
+ || (dataFailCause == ACTIVATION_REJECT_UNSPECIFIED)
+ || (dataFailCause == SERVICE_OPTION_NOT_SUBSCRIBED)
+ || (dataFailCause == SERVICE_OPTION_NOT_SUPPORTED)
+ || (dataFailCause == SERVICE_OPTION_OUT_OF_ORDER)
+ || (dataFailCause == NSAPI_IN_USE)
+ || (dataFailCause == ONLY_IPV4_ALLOWED)
+ || (dataFailCause == ONLY_IPV6_ALLOWED)
+ || (dataFailCause == PROTOCOL_ERRORS)
+ || (dataFailCause == SIGNAL_LOST)
+ || (dataFailCause == RADIO_POWER_OFF)
+ || (dataFailCause == TETHERED_CALL_ACTIVE)
+ || (dataFailCause == UNACCEPTABLE_NETWORK_PARAMETER);
}
- public static DataFailCause fromInt(int errorCode) {
- DataFailCause fc = sErrorCodeToFailCauseMap.get(errorCode);
- if (fc == null) {
- fc = UNKNOWN;
+ public static String toString(@FailCause int dataFailCause) {
+ int cause = getFailCause(dataFailCause);
+ return (cause == UNKNOWN) ? "UNKNOWN(" + dataFailCause + ")" : sFailCauseMap.get(cause);
+ }
+
+ public static int getFailCause(@FailCause int failCause) {
+ if (sFailCauseMap.containsKey(failCause)) {
+ return failCause;
+ } else {
+ return UNKNOWN;
}
- return fc;
}
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index b61e99b..2c712a1 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2617,8 +2617,7 @@
if (availableList == null) {
return null;
} else {
- return getAvailableSubscriptionInfoList().stream()
- .filter(subInfo -> !shouldHideSubscription(subInfo))
+ return availableList.stream().filter(subInfo -> !shouldHideSubscription(subInfo))
.collect(Collectors.toList());
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 29d32e9..f241d45 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -6382,8 +6382,9 @@
public @PrefNetworkMode int getPreferredNetworkType(int subId) {
try {
ITelephony telephony = getITelephony();
- if (telephony != null)
+ if (telephony != null) {
return telephony.getPreferredNetworkType(subId);
+ }
} catch (RemoteException ex) {
Rlog.e(TAG, "getPreferredNetworkType RemoteException", ex);
} catch (NullPointerException ex) {
@@ -6393,6 +6394,37 @@
}
/**
+ * Get the preferred network type bitmap.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+ * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ *
+ * @return a 32-bit bitmap.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SystemApi
+ public @NetworkTypeBitMask int getPreferredNetworkTypeBitmap() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return RadioAccessFamily.getRafFromNetworkType(
+ telephony.getPreferredNetworkType(getSubId()));
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getPreferredNetworkTypeBitmap RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "getPreferredNetworkTypeBitmap NPE", ex);
+ }
+ return 0;
+ }
+
+ /**
* Sets the network selection mode to automatic.
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
@@ -6607,6 +6639,37 @@
}
/**
+ * Set the preferred network type bitmap.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+ * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ *
+ * @param networkTypeBitmap a 32-bit bitmap.
+ * @return true on success; false on any failure.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @SystemApi
+ public boolean setPreferredNetworkTypeBitmap(@NetworkTypeBitMask int networkTypeBitmap) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.setPreferredNetworkType(
+ getSubId(), RadioAccessFamily.getNetworkTypeFromRaf(networkTypeBitmap));
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "setPreferredNetworkType RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "setPreferredNetworkType NPE", ex);
+ }
+ return false;
+ }
+
+ /**
* Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
*
* <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index ebf1987..6326cc6 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -61,7 +61,6 @@
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 @@
"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 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 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 @@
}
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/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index 553e3fb..0edc002 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -284,8 +284,6 @@
*/
private static boolean reportAccessDeniedToReadIdentifiers(Context context, int subId, int pid,
int uid, String callingPackage, String message) {
- Log.wtf(LOG_TAG,
- "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message);
// If the device identifier check is enabled then enforce the new access requirements for
// both 1P and 3P apps.
boolean enableDeviceIdentifierCheck = Settings.Global.getInt(context.getContentResolver(),
@@ -295,17 +293,40 @@
boolean relax3PDeviceIdentifierCheck = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED, 0) == 1;
boolean is3PApp = true;
+ // Also check if the application is a preloaded non-privileged app; if so there is a
+ // separate setting to relax the check for these apps to ensure users can relax the check
+ // for 3P or non-priv apps as needed while continuing to test the other.
+ boolean relaxNonPrivDeviceIdentifierCheck = Settings.Global.getInt(
+ context.getContentResolver(),
+ Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED, 0) == 1;
+ boolean isNonPrivApp = false;
ApplicationInfo callingPackageInfo = null;
try {
callingPackageInfo = context.getPackageManager().getApplicationInfo(callingPackage, 0);
- if (callingPackageInfo.isSystemApp()) {
+ if (callingPackageInfo.isPrivilegedApp()) {
is3PApp = false;
+ } else if (callingPackageInfo.isSystemApp()) {
+ is3PApp = false;
+ isNonPrivApp = true;
}
} catch (PackageManager.NameNotFoundException e) {
// If the application info for the calling package could not be found then assume the
// calling app is a 3P app to detect any issues with the check
+ Log.e(LOG_TAG, "Exception caught obtaining package info for package " + callingPackage,
+ e);
}
- if (enableDeviceIdentifierCheck || (is3PApp && !relax3PDeviceIdentifierCheck)) {
+ Log.wtf(LOG_TAG, "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message
+ + ":is3PApp=" + is3PApp + ":isNonPrivApp=" + isNonPrivApp);
+ // The new Q restrictions for device identifier access will be enforced if any of the
+ // following are true:
+ // - The PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED setting has been set.
+ // - The app requesting a device identifier is not a preloaded app (3P), and the
+ // PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED setting has not been set.
+ // - The app requesting a device identifier is a preloaded app but is not a privileged app,
+ // and the PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED setting has not been set.
+ if (enableDeviceIdentifierCheck
+ || (is3PApp && !relax3PDeviceIdentifierCheck)
+ || (isNonPrivApp && !relaxNonPrivDeviceIdentifierCheck)) {
boolean targetQBehaviorDisabled = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED, 0) == 0;
if (callingPackage != null) {
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index 0a0ad90..870a689 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 @@
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 369a002..4c2a984 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 @@
}
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/apilint/apilint.py b/tools/apilint/apilint.py
index b5a990e..6476abd 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -209,24 +209,42 @@
return self.raw
-def _parse_stream(f, clazz_cb=None, base_f=None):
+def _parse_stream(f, clazz_cb=None, base_f=None, out_classes_with_base=None,
+ in_classes_with_base=[]):
api = {}
+ in_classes_with_base = _retry_iterator(in_classes_with_base)
if base_f:
- base_classes = _parse_stream_to_generator(base_f)
+ base_classes = _retry_iterator(_parse_stream_to_generator(base_f))
else:
base_classes = []
- for clazz in _parse_stream_to_generator(f):
- base_class = _parse_to_matching_class(base_classes, clazz)
- if base_class:
- clazz.merge_from(base_class)
-
+ def handle_class(clazz):
if clazz_cb:
clazz_cb(clazz)
else: # In callback mode, don't keep track of the full API
api[clazz.fullname] = clazz
+ def handle_missed_classes_with_base(clazz):
+ for c in _yield_until_matching_class(in_classes_with_base, clazz):
+ base_class = _skip_to_matching_class(base_classes, c)
+ if base_class:
+ handle_class(base_class)
+
+ for clazz in _parse_stream_to_generator(f):
+ # Before looking at clazz, let's see if there's some classes that were not present, but
+ # may have an entry in the base stream.
+ handle_missed_classes_with_base(clazz)
+
+ base_class = _skip_to_matching_class(base_classes, clazz)
+ if base_class:
+ clazz.merge_from(base_class)
+ if out_classes_with_base is not None:
+ out_classes_with_base.append(clazz)
+ handle_class(clazz)
+
+ handle_missed_classes_with_base(None)
+
return api
def _parse_stream_to_generator(f):
@@ -257,18 +275,22 @@
elif raw.startswith(" field"):
clazz.fields.append(Field(clazz, line, raw, blame))
elif raw.startswith(" }") and clazz:
- while True:
- retry = yield clazz
- if not retry:
- break
- # send() was called, asking us to redeliver clazz on next(). Still need to yield
- # a dummy value to the send() first though.
- if (yield "Returning clazz on next()"):
- raise TypeError("send() must be followed by next(), not send()")
+ yield clazz
+def _retry_iterator(it):
+ """Wraps an iterator, such that calling send(True) on it will redeliver the same element"""
+ for e in it:
+ while True:
+ retry = yield e
+ if not retry:
+ break
+ # send() was called, asking us to redeliver clazz on next(). Still need to yield
+ # a dummy value to the send() first though.
+ if (yield "Returning clazz on next()"):
+ raise TypeError("send() must be followed by next(), not send()")
-def _parse_to_matching_class(classes, needle):
- """Takes a classes generator and parses it until it returns the class we're looking for
+def _skip_to_matching_class(classes, needle):
+ """Takes a classes iterator and consumes entries until it returns the class we're looking for
This relies on classes being sorted by package and class name."""
@@ -276,8 +298,8 @@
if clazz.pkg.name < needle.pkg.name:
# We haven't reached the right package yet
continue
- if clazz.name < needle.name:
- # We haven't reached the right class yet
+ if clazz.pkg.name == needle.pkg.name and clazz.fullname < needle.fullname:
+ # We're in the right package, but not the right class yet
continue
if clazz.fullname == needle.fullname:
return clazz
@@ -285,6 +307,28 @@
classes.send(clazz)
return None
+def _yield_until_matching_class(classes, needle):
+ """Takes a class iterator and yields entries it until it reaches the class we're looking for.
+
+ This relies on classes being sorted by package and class name."""
+
+ for clazz in classes:
+ if needle is None:
+ yield clazz
+ elif clazz.pkg.name < needle.pkg.name:
+ # We haven't reached the right package yet
+ yield clazz
+ elif clazz.pkg.name == needle.pkg.name and clazz.fullname < needle.fullname:
+ # We're in the right package, but not the right class yet
+ yield clazz
+ elif clazz.fullname == needle.fullname:
+ # Class found, abort.
+ return
+ else:
+ # We ran past the right class. Send it back into the iterator, then abort.
+ classes.send(clazz)
+ return
+
class Failure():
def __init__(self, sig, clazz, detail, error, rule, msg):
self.sig = sig
@@ -1543,12 +1587,14 @@
verify_singleton(clazz)
-def examine_stream(stream, base_stream=None):
+def examine_stream(stream, base_stream=None, in_classes_with_base=[], out_classes_with_base=None):
"""Find all style issues in the given API stream."""
global failures, noticed
failures = {}
noticed = {}
- _parse_stream(stream, examine_clazz, base_f=base_stream)
+ _parse_stream(stream, examine_clazz, base_f=base_stream,
+ in_classes_with_base=in_classes_with_base,
+ out_classes_with_base=out_classes_with_base)
return (failures, noticed)
@@ -1734,19 +1780,24 @@
show_stats(cur, prev)
sys.exit()
+ classes_with_base = []
+
with current_file as f:
if base_current_file:
with base_current_file as base_f:
- cur_fail, cur_noticed = examine_stream(f, base_f)
+ cur_fail, cur_noticed = examine_stream(f, base_f,
+ out_classes_with_base=classes_with_base)
else:
- cur_fail, cur_noticed = examine_stream(f)
+ cur_fail, cur_noticed = examine_stream(f, out_classes_with_base=classes_with_base)
+
if not previous_file is None:
with previous_file as f:
if base_previous_file:
with base_previous_file as base_f:
- prev_fail, prev_noticed = examine_stream(f, base_f)
+ prev_fail, prev_noticed = examine_stream(f, base_f,
+ in_classes_with_base=classes_with_base)
else:
- prev_fail, prev_noticed = examine_stream(f)
+ prev_fail, prev_noticed = examine_stream(f, in_classes_with_base=classes_with_base)
# ignore errors from previous API level
for p in prev_fail:
diff --git a/tools/apilint/apilint_sha_system.sh b/tools/apilint/apilint_sha_system.sh
new file mode 100755
index 0000000..8538a3d
--- /dev/null
+++ b/tools/apilint/apilint_sha_system.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+if git show --name-only --pretty=format: $1 | grep api/ > /dev/null; then
+ python tools/apilint/apilint.py \
+ --base-current <(git show $1:api/current.txt) \
+ --base-previous <(git show $1^:api/current.txt) \
+ <(git show $1:api/system-current.txt) \
+ <(git show $1^:api/system-current.txt)
+fi
diff --git a/tools/apilint/apilint_test.py b/tools/apilint/apilint_test.py
new file mode 100644
index 0000000..ece69a9
--- /dev/null
+++ b/tools/apilint/apilint_test.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python
+
+# 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.
+
+import unittest
+
+import apilint
+
+def cls(pkg, name):
+ return apilint.Class(apilint.Package(999, "package %s {" % pkg, None), 999,
+ "public final class %s {" % name, None)
+
+_ri = apilint._retry_iterator
+
+c1 = cls("android.app", "ActivityManager")
+c2 = cls("android.app", "Notification")
+c3 = cls("android.app", "Notification.Action")
+c4 = cls("android.graphics", "Bitmap")
+
+class UtilTests(unittest.TestCase):
+ def test_retry_iterator(self):
+ it = apilint._retry_iterator([1, 2, 3, 4])
+ self.assertEqual(it.next(), 1)
+ self.assertEqual(it.next(), 2)
+ self.assertEqual(it.next(), 3)
+ it.send("retry")
+ self.assertEqual(it.next(), 3)
+ self.assertEqual(it.next(), 4)
+ with self.assertRaises(StopIteration):
+ it.next()
+
+ def test_retry_iterator_one(self):
+ it = apilint._retry_iterator([1])
+ self.assertEqual(it.next(), 1)
+ it.send("retry")
+ self.assertEqual(it.next(), 1)
+ with self.assertRaises(StopIteration):
+ it.next()
+
+ def test_retry_iterator_one(self):
+ it = apilint._retry_iterator([1])
+ self.assertEqual(it.next(), 1)
+ it.send("retry")
+ self.assertEqual(it.next(), 1)
+ with self.assertRaises(StopIteration):
+ it.next()
+
+ def test_skip_to_matching_class_found(self):
+ it = _ri([c1, c2, c3, c4])
+ self.assertEquals(apilint._skip_to_matching_class(it, c3),
+ c3)
+ self.assertEqual(it.next(), c4)
+
+ def test_skip_to_matching_class_not_found(self):
+ it = _ri([c1, c2, c3, c4])
+ self.assertEquals(apilint._skip_to_matching_class(it, cls("android.content", "ContentProvider")),
+ None)
+ self.assertEqual(it.next(), c4)
+
+ def test_yield_until_matching_class_found(self):
+ it = _ri([c1, c2, c3, c4])
+ self.assertEquals(list(apilint._yield_until_matching_class(it, c3)),
+ [c1, c2])
+ self.assertEqual(it.next(), c4)
+
+ def test_yield_until_matching_class_not_found(self):
+ it = _ri([c1, c2, c3, c4])
+ self.assertEquals(list(apilint._yield_until_matching_class(it, cls("android.content", "ContentProvider"))),
+ [c1, c2, c3])
+ self.assertEqual(it.next(), c4)
+
+ def test_yield_until_matching_class_None(self):
+ it = _ri([c1, c2, c3, c4])
+ self.assertEquals(list(apilint._yield_until_matching_class(it, None)),
+ [c1, c2, c3, c4])
+
+
+faulty_current_txt = """
+package android.app {
+ public final class Activity {
+ }
+
+ public final class WallpaperColors implements android.os.Parcelable {
+ ctor public WallpaperColors(android.os.Parcel);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR;
+ }
+}
+""".split('\n')
+
+ok_current_txt = """
+package android.app {
+ public final class Activity {
+ }
+
+ public final class WallpaperColors implements android.os.Parcelable {
+ ctor public WallpaperColors();
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR;
+ }
+}
+""".split('\n')
+
+system_current_txt = """
+package android.app {
+ public final class WallpaperColors implements android.os.Parcelable {
+ method public int getSomething();
+ }
+}
+""".split('\n')
+
+
+
+class BaseFileTests(unittest.TestCase):
+ def test_base_file_avoids_errors(self):
+ failures, _ = apilint.examine_stream(system_current_txt, ok_current_txt)
+ self.assertEquals(failures, {})
+
+ def test_class_with_base_finds_same_errors(self):
+ failures_with_classes_with_base, _ = apilint.examine_stream("", faulty_current_txt,
+ in_classes_with_base=[cls("android.app", "WallpaperColors")])
+ failures_with_system_txt, _ = apilint.examine_stream(system_current_txt, faulty_current_txt)
+
+ self.assertEquals(failures_with_classes_with_base.keys(), failures_with_system_txt.keys())
+
+ def test_classes_with_base_is_emited(self):
+ classes_with_base = []
+ _, _ = apilint.examine_stream(system_current_txt, faulty_current_txt,
+ out_classes_with_base=classes_with_base)
+ self.assertEquals(map(lambda x: x.fullname, classes_with_base), ["android.app.WallpaperColors"])
+
+if __name__ == "__main__":
+ unittest.main()
\ No newline at end of file
diff --git a/tools/incident_report/printer.h b/tools/incident_report/printer.h
index ed93fa1..63e276b 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/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index faa3547..f6c9c0e 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -453,7 +453,7 @@
map<string, bool> variableNames;
set<string> parents;
vector<const FieldDescriptor*> fieldsInOrder = sortFields(descriptor);
- bool skip[fieldsInOrder.size()];
+ vector<bool> skip(fieldsInOrder.size());
const Destination incidentDest = getPrivacyFlags(descriptor).dest();
for (size_t i=0; i<fieldsInOrder.size(); i++) {
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index 703a67b..5725f0c 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -96,6 +96,7 @@
cc_library_shared {
name: "libstatslog",
+ host_supported: true,
generated_sources: ["statslog.cpp"],
generated_headers: ["statslog.h"],
cflags: [
@@ -105,8 +106,19 @@
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 5192a0e..11b408f 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -106,7 +106,9 @@
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 @@
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 64f8adb..d023b58 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -708,6 +708,12 @@
}
/**
+ * 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 @@
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 @@
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 @@
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 @@
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 @@
config.recentFailure.setAssociationStatus(in.readInt());
config.mRandomizedMacAddress = in.readParcelable(null);
config.macRandomizationSetting = in.readInt();
+ config.osu = in.readInt() != 0;
return config;
}
diff --git a/wifi/tests/Android.mk b/wifi/tests/Android.mk
index 73341ac..401b652 100644
--- a/wifi/tests/Android.mk
+++ b/wifi/tests/Android.mk
@@ -45,7 +45,7 @@
LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := $(jacoco_exclude)
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test \
+ androidx.test.rules \
core-test-rules \
guava \
mockito-target-minus-junit4 \
diff --git a/wifi/tests/AndroidManifest.xml b/wifi/tests/AndroidManifest.xml
index 4eaca2b..b6c38bc 100644
--- a/wifi/tests/AndroidManifest.xml
+++ b/wifi/tests/AndroidManifest.xml
@@ -30,7 +30,7 @@
</activity>
</application>
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="android.net.wifi.test"
android:label="Frameworks Wifi API Tests">
</instrumentation>
diff --git a/wifi/tests/AndroidTest.xml b/wifi/tests/AndroidTest.xml
index 45c7a17..cae19e4 100644
--- a/wifi/tests/AndroidTest.xml
+++ b/wifi/tests/AndroidTest.xml
@@ -22,7 +22,7 @@
<option name="test-tag" value="FrameworksWifiApiTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.net.wifi.test" />
- <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/wifi/tests/README.md b/wifi/tests/README.md
index b418abd..b0594f2 100644
--- a/wifi/tests/README.md
+++ b/wifi/tests/README.md
@@ -37,7 +37,7 @@
If you manually build and push the test APK to the device you can run tests using
```
-adb shell am instrument -w 'android.net.wifi.test/android.support.test.runner.AndroidJUnitRunner'
+adb shell am instrument -w 'android.net.wifi.test/androidx.test.runner.AndroidJUnitRunner'
```
## Adding Tests
diff --git a/wifi/tests/runtests.sh b/wifi/tests/runtests.sh
index 4e52b8f..2caf8a5 100755
--- a/wifi/tests/runtests.sh
+++ b/wifi/tests/runtests.sh
@@ -22,4 +22,4 @@
adb install -r -g "$OUT/data/app/FrameworksWifiApiTests/FrameworksWifiApiTests.apk"
adb shell am instrument --no-hidden-api-checks -w "$@" \
- 'android.net.wifi.test/android.support.test.runner.AndroidJUnitRunner'
+ 'android.net.wifi.test/androidx.test.runner.AndroidJUnitRunner'
diff --git a/wifi/tests/src/android/net/wifi/ParcelUtilTest.java b/wifi/tests/src/android/net/wifi/ParcelUtilTest.java
index 0c3bf3b..917b1df 100644
--- a/wifi/tests/src/android/net/wifi/ParcelUtilTest.java
+++ b/wifi/tests/src/android/net/wifi/ParcelUtilTest.java
@@ -22,7 +22,8 @@
import static org.junit.Assert.assertNull;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/ScanResultTest.java b/wifi/tests/src/android/net/wifi/ScanResultTest.java
index 458c43d..54ec325 100644
--- a/wifi/tests/src/android/net/wifi/ScanResultTest.java
+++ b/wifi/tests/src/android/net/wifi/ScanResultTest.java
@@ -22,15 +22,14 @@
import static org.mockito.Mockito.validateMockitoUsage;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.net.wifi.WifiScanner.ScanSettings;
+
+import androidx.test.filters.SmallTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
-
/**
* Unit tests for {@link android.net.wifi.WifiScanner}.
*/
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 8d97307..c744f18 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -26,7 +26,8 @@
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
index e569efe..6ec64ff 100644
--- a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
@@ -19,8 +19,8 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -28,7 +28,8 @@
import android.net.wifi.WifiEnterpriseConfig.Phase2;
import android.os.Parcel;
import android.security.Credentials;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
@@ -36,7 +37,6 @@
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
-
/**
* Unit tests for {@link android.net.wifi.WifiEnterpriseConfig}.
*/
diff --git a/wifi/tests/src/android/net/wifi/WifiInfoTest.java b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
index f9fb062..fb0af5f 100644
--- a/wifi/tests/src/android/net/wifi/WifiInfoTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
@@ -21,7 +21,8 @@
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 1001b10..c43948b 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -66,7 +66,8 @@
import android.os.Message;
import android.os.Messenger;
import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
index f8ab8a2..2258e4d 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
@@ -25,9 +25,10 @@
import android.net.NetworkRequest;
import android.os.Parcel;
import android.os.PatternMatcher;
-import android.support.test.filters.SmallTest;
import android.util.Pair;
+import androidx.test.filters.SmallTest;
+
import org.junit.Test;
/**
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
index 2505499..83627ad 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
@@ -29,7 +29,8 @@
import android.net.NetworkSpecifier;
import android.os.PatternMatcher;
import android.os.Process;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
index 997282b..fdd11a3 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
@@ -29,8 +29,9 @@
import android.net.wifi.WifiNetworkScoreCache.CacheListener;
import android.os.Handler;
import android.os.HandlerThread;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import com.google.common.collect.ImmutableList;
@@ -44,7 +45,6 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-
/** Unit tests for {@link WifiNetworkScoreCache}. */
@RunWith(AndroidJUnit4.class)
@SmallTest
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
index 856f0c7..2a8df8d 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
@@ -26,9 +26,10 @@
import android.net.MatchAllNetworkSpecifier;
import android.os.Parcel;
import android.os.PatternMatcher;
-import android.support.test.filters.SmallTest;
import android.util.Pair;
+import androidx.test.filters.SmallTest;
+
import org.junit.Test;
/**
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index 5cc8217..31f501f 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -19,7 +19,8 @@
import static org.junit.Assert.*;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
index cf1ed8f..76bfff0 100644
--- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
@@ -32,7 +32,8 @@
import android.os.Handler;
import android.os.Parcel;
import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import com.android.internal.util.test.BidirectionalAsyncChannelServer;
@@ -44,7 +45,6 @@
import java.util.Arrays;
-
/**
* Unit tests for {@link android.net.wifi.WifiScanner}.
*/
diff --git a/wifi/tests/src/android/net/wifi/WifiSsidTest.java b/wifi/tests/src/android/net/wifi/WifiSsidTest.java
index b58f2c7..10a37c0 100644
--- a/wifi/tests/src/android/net/wifi/WifiSsidTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiSsidTest.java
@@ -19,7 +19,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java b/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java
index 6ecd931..83affed 100644
--- a/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java
@@ -18,7 +18,7 @@
import static org.hamcrest.core.IsEqual.equalTo;
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
import org.junit.Rule;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
index 657e5a7..4189e40 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
@@ -21,7 +21,8 @@
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Rule;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 45e1720..e882b6b 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -38,7 +38,8 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import libcore.util.HexEncoding;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java
index f32fe59..d9a1d9af 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java
@@ -22,7 +22,10 @@
import android.net.wifi.FakeKeys;
import android.net.wifi.hotspot2.pps.Credential;
import android.net.wifi.hotspot2.pps.HomeSp;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
import java.io.BufferedReader;
import java.io.IOException;
@@ -30,8 +33,6 @@
import java.io.InputStreamReader;
import java.util.Arrays;
-import org.junit.Test;
-
/**
* Unit tests for {@link android.net.wifi.hotspot2.ConfigParser}.
*/
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java b/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java
index 89ecd0f..c7e009e 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java
@@ -23,7 +23,8 @@
import android.net.Uri;
import android.net.wifi.WifiSsid;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
index ee5a75e..fc03e7e 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -25,9 +25,10 @@
import android.net.wifi.hotspot2.pps.Policy;
import android.net.wifi.hotspot2.pps.UpdateParameter;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
import android.util.Base64;
+import androidx.test.filters.SmallTest;
+
import org.junit.Test;
import java.nio.charset.StandardCharsets;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java
index 707b64f..66c595f 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java
@@ -19,14 +19,13 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import android.net.wifi.hotspot2.omadm.PpsMoParser;
import android.net.wifi.hotspot2.PasspointConfiguration;
import android.net.wifi.hotspot2.pps.Credential;
import android.net.wifi.hotspot2.pps.HomeSp;
import android.net.wifi.hotspot2.pps.Policy;
import android.net.wifi.hotspot2.pps.UpdateParameter;
-import android.support.test.filters.SmallTest;
-import android.text.TextUtils;
+
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java
index ef478c7..85d0a90 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java
@@ -20,7 +20,8 @@
import android.net.wifi.hotspot2.omadm.XMLNode;
import android.net.wifi.hotspot2.omadm.XMLParser;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
index c07db6c..a9d4b8f 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
@@ -22,7 +22,8 @@
import android.net.wifi.EAPConstants;
import android.net.wifi.FakeKeys;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java
index c7993e3..93d471a 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java
@@ -20,7 +20,8 @@
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java
index 171d6ff..980b199 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java
@@ -20,15 +20,15 @@
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
import android.util.Base64;
+import androidx.test.filters.SmallTest;
+
import org.junit.Test;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java
index 2a7526b..0b8cd3d 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java
@@ -20,17 +20,14 @@
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
import android.util.Base64;
+import androidx.test.filters.SmallTest;
+
import org.junit.Test;
import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
/**
* Unit tests for {@link android.net.wifi.hotspot2.pps.UpdateParameter}.
diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
index 80f00a4..f61e6b7 100644
--- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
+++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
@@ -19,7 +19,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java
index 2132b41..9e8dca4 100644
--- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java
@@ -21,7 +21,8 @@
import android.content.Context;
import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import libcore.junit.util.ResourceLeakageDetector;
diff --git a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
index 8997ae9..afc7dff 100644
--- a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
@@ -31,7 +31,8 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;