diff options
261 files changed, 8107 insertions, 2313 deletions
diff --git a/Android.bp b/Android.bp index 7e97e66a9e80..c2024084d95d 100644 --- a/Android.bp +++ b/Android.bp @@ -1067,6 +1067,7 @@ java_library { "core/java/android/annotation/IntDef.java", "core/java/android/annotation/NonNull.java", "core/java/android/annotation/SystemApi.java", + "core/java/android/annotation/TestApi.java", "core/java/android/os/HwBinder.java", "core/java/android/os/HwBlob.java", "core/java/android/os/HwParcel.java", diff --git a/api/current.txt b/api/current.txt index 80fea7bf13ad..b3618601e1be 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5245,8 +5245,8 @@ package android.app { method public android.app.Notification clone(); method public int describeContents(); method public boolean getAllowSystemGeneratedContextualActions(); - method public android.app.PendingIntent getAppOverlayIntent(); method public int getBadgeIconType(); + method public android.app.Notification.BubbleMetadata getBubbleMetadata(); method public java.lang.String getChannelId(); method public java.lang.String getGroup(); method public int getGroupAlertBehavior(); @@ -5459,6 +5459,25 @@ package android.app { method public android.app.Notification.BigTextStyle setSummaryText(java.lang.CharSequence); } + public static final class Notification.BubbleMetadata implements android.os.Parcelable { + method public int describeContents(); + method public int getDesiredHeight(); + method public android.graphics.drawable.Icon getIcon(); + method public android.app.PendingIntent getIntent(); + method public java.lang.CharSequence getTitle(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.Notification.BubbleMetadata> CREATOR; + } + + public static class Notification.BubbleMetadata.Builder { + ctor public Notification.BubbleMetadata.Builder(); + method public android.app.Notification.BubbleMetadata build(); + method public android.app.Notification.BubbleMetadata.Builder setDesiredHeight(int); + method public android.app.Notification.BubbleMetadata.Builder setIcon(android.graphics.drawable.Icon); + method public android.app.Notification.BubbleMetadata.Builder setIntent(android.app.PendingIntent); + method public android.app.Notification.BubbleMetadata.Builder setTitle(java.lang.CharSequence); + } + public static class Notification.Builder { ctor public Notification.Builder(android.content.Context, java.lang.String); ctor public deprecated Notification.Builder(android.content.Context); @@ -5478,9 +5497,9 @@ package android.app { method public static android.app.Notification.Builder recoverBuilder(android.content.Context, android.app.Notification); method public android.app.Notification.Builder setActions(android.app.Notification.Action...); method public android.app.Notification.Builder setAllowSystemGeneratedContextualActions(boolean); - method public android.app.Notification.Builder setAppOverlayIntent(android.app.PendingIntent); method public android.app.Notification.Builder setAutoCancel(boolean); method public android.app.Notification.Builder setBadgeIconType(int); + method public android.app.Notification.Builder setBubbleMetadata(android.app.Notification.BubbleMetadata); method public android.app.Notification.Builder setCategory(java.lang.String); method public android.app.Notification.Builder setChannelId(java.lang.String); method public android.app.Notification.Builder setChronometerCountDown(boolean); @@ -5695,8 +5714,8 @@ package android.app { public final class NotificationChannel implements android.os.Parcelable { ctor public NotificationChannel(java.lang.String, java.lang.CharSequence, int); + method public boolean canBubble(); method public boolean canBypassDnd(); - method public boolean canOverlayApps(); method public boolean canShowBadge(); method public int describeContents(); method public void enableLights(boolean); @@ -5712,7 +5731,7 @@ package android.app { method public android.net.Uri getSound(); method public long[] getVibrationPattern(); method public boolean hasUserSetImportance(); - method public void setAllowAppOverlay(boolean); + method public void setAllowBubbles(boolean); method public void setBypassDnd(boolean); method public void setDescription(java.lang.String); method public void setGroup(java.lang.String); @@ -5746,7 +5765,7 @@ package android.app { public class NotificationManager { method public java.lang.String addAutomaticZenRule(android.app.AutomaticZenRule); - method public boolean areAppOverlaysAllowed(); + method public boolean areBubblesAllowed(); method public boolean areNotificationsEnabled(); method public boolean canNotifyAsPackage(java.lang.String); method public void cancel(int); @@ -11191,6 +11210,7 @@ package android.content.pm { method public void registerCallback(android.content.pm.LauncherApps.Callback); method public void registerCallback(android.content.pm.LauncherApps.Callback, android.os.Handler); method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle); + method public boolean shouldHideFromSuggestions(java.lang.String, android.os.UserHandle); method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle); method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle); method public void startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle); @@ -13736,6 +13756,7 @@ package android.graphics { method public void drawTextOnPath(java.lang.String, android.graphics.Path, float, float, android.graphics.Paint); method public void drawTextRun(char[], int, int, int, int, float, float, boolean, android.graphics.Paint); method public void drawTextRun(java.lang.CharSequence, int, int, int, int, float, float, boolean, android.graphics.Paint); + method public void drawTextRun(android.graphics.text.MeasuredText, int, int, int, int, float, float, boolean, android.graphics.Paint); method public void drawVertices(android.graphics.Canvas.VertexMode, int, float[], int, float[], int, int[], int, short[], int, int, android.graphics.Paint); method public void enableZ(); method public boolean getClipBounds(android.graphics.Rect); @@ -22662,6 +22683,7 @@ package android.location { method public deprecated double getCarrierPhase(); method public deprecated double getCarrierPhaseUncertainty(); method public double getCn0DbHz(); + method public int getCodeType(); method public int getConstellationType(); method public int getMultipathIndicator(); method public double getPseudorangeRateMetersPerSecond(); @@ -22677,6 +22699,7 @@ package android.location { method public boolean hasCarrierFrequencyHz(); method public deprecated boolean hasCarrierPhase(); method public deprecated boolean hasCarrierPhaseUncertainty(); + method public boolean hasCodeType(); method public boolean hasSnrInDb(); method public void writeToParcel(android.os.Parcel, int); field public static final int ADR_STATE_CYCLE_SLIP = 4; // 0x4 @@ -22685,6 +22708,21 @@ package android.location { field public static final int ADR_STATE_RESET = 2; // 0x2 field public static final int ADR_STATE_UNKNOWN = 0; // 0x0 field public static final int ADR_STATE_VALID = 1; // 0x1 + field public static final int CODE_TYPE_A = 0; // 0x0 + field public static final int CODE_TYPE_B = 1; // 0x1 + field public static final int CODE_TYPE_C = 2; // 0x2 + field public static final int CODE_TYPE_CODELESS = 13; // 0xd + field public static final int CODE_TYPE_I = 3; // 0x3 + field public static final int CODE_TYPE_L = 4; // 0x4 + field public static final int CODE_TYPE_M = 5; // 0x5 + field public static final int CODE_TYPE_P = 6; // 0x6 + field public static final int CODE_TYPE_Q = 7; // 0x7 + field public static final int CODE_TYPE_S = 8; // 0x8 + field public static final int CODE_TYPE_UNKNOWN = -1; // 0xffffffff + field public static final int CODE_TYPE_W = 9; // 0x9 + field public static final int CODE_TYPE_X = 10; // 0xa + field public static final int CODE_TYPE_Y = 11; // 0xb + field public static final int CODE_TYPE_Z = 12; // 0xc field public static final android.os.Parcelable.Creator<android.location.GnssMeasurement> CREATOR; field public static final int MULTIPATH_INDICATOR_DETECTED = 1; // 0x1 field public static final int MULTIPATH_INDICATOR_NOT_DETECTED = 2; // 0x2 @@ -24175,8 +24213,11 @@ package android.media { public static final class MediaCodec.CryptoException extends java.lang.RuntimeException { ctor public MediaCodec.CryptoException(int, java.lang.String); method public int getErrorCode(); + field public static final int ERROR_FRAME_TOO_LARGE = 8; // 0x8 field public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4; // 0x4 + field public static final int ERROR_INSUFFICIENT_SECURITY = 7; // 0x7 field public static final int ERROR_KEY_EXPIRED = 2; // 0x2 + field public static final int ERROR_LOST_STATE = 9; // 0x9 field public static final int ERROR_NO_KEY = 1; // 0x1 field public static final int ERROR_RESOURCE_BUSY = 3; // 0x3 field public static final int ERROR_SESSION_NOT_OPENED = 5; // 0x5 @@ -24517,6 +24558,22 @@ package android.media { field public static final int REGULAR_CODECS = 0; // 0x0 } + public class MediaController2 implements java.lang.AutoCloseable { + ctor public MediaController2(android.content.Context, android.media.Session2Token); + ctor public MediaController2(android.content.Context, android.media.Session2Token, java.util.concurrent.Executor, android.media.MediaController2.ControllerCallback); + method public void cancelSessionCommand(java.lang.Object); + method public void close(); + method public java.lang.Object sendSessionCommand(android.media.Session2Command, android.os.Bundle); + } + + public static abstract class MediaController2.ControllerCallback { + ctor public MediaController2.ControllerCallback(); + method public void onCommandResult(android.media.MediaController2, java.lang.Object, android.media.Session2Command, android.media.Session2Command.Result); + method public void onConnected(android.media.MediaController2, android.media.Session2CommandGroup); + method public void onDisconnected(android.media.MediaController2); + method public android.media.Session2Command.Result onSessionCommand(android.media.MediaController2, android.media.Session2Command, android.os.Bundle); + } + public final class MediaCrypto { ctor public MediaCrypto(java.util.UUID, byte[]) throws android.media.MediaCryptoException; method protected void finalize(); @@ -24624,6 +24681,7 @@ package android.media { method public void setOnEventListener(android.media.MediaDrm.OnEventListener); method public void setOnExpirationUpdateListener(android.media.MediaDrm.OnExpirationUpdateListener, android.os.Handler); method public void setOnKeyStatusChangeListener(android.media.MediaDrm.OnKeyStatusChangeListener, android.os.Handler); + method public void setOnSessionLostStateListener(android.media.MediaDrm.OnSessionLostStateListener, android.os.Handler); method public void setPropertyByteArray(java.lang.String, byte[]); method public void setPropertyString(java.lang.String, java.lang.String); field public static final deprecated int EVENT_KEY_EXPIRED = 3; // 0x3 @@ -24742,6 +24800,10 @@ package android.media { method public abstract void onKeyStatusChange(android.media.MediaDrm, byte[], java.util.List<android.media.MediaDrm.KeyStatus>, boolean); } + public static abstract interface MediaDrm.OnSessionLostStateListener { + method public abstract void onSessionLostState(android.media.MediaDrm, byte[]); + } + public static final class MediaDrm.ProvisionRequest { method public byte[] getData(); method public java.lang.String getDefaultUrl(); @@ -24750,6 +24812,12 @@ package android.media { public static abstract class MediaDrm.SecurityLevel implements java.lang.annotation.Annotation { } + public static final class MediaDrm.SessionException extends java.lang.RuntimeException { + ctor public MediaDrm.SessionException(int, java.lang.String); + method public int getErrorCode(); + field public static final int ERROR_RESOURCE_CONTENTION = 1; // 0x1 + } + public class MediaDrmException extends java.lang.Exception { ctor public MediaDrmException(java.lang.String); } @@ -25805,6 +25873,37 @@ package android.media { method public abstract void onScanCompleted(java.lang.String, android.net.Uri); } + public class MediaSession2 implements java.lang.AutoCloseable { + method public void broadcastSessionCommand(android.media.Session2Command, android.os.Bundle); + method public void cancelSessionCommand(android.media.MediaSession2.ControllerInfo, java.lang.Object); + method public void close(); + method public java.lang.String getSessionId(); + method public android.media.Session2Token getSessionToken(); + method public java.lang.Object sendSessionCommand(android.media.MediaSession2.ControllerInfo, android.media.Session2Command, android.os.Bundle); + } + + public static final class MediaSession2.Builder { + ctor public MediaSession2.Builder(android.content.Context); + method public android.media.MediaSession2 build(); + method public android.media.MediaSession2.Builder setId(java.lang.String); + method public android.media.MediaSession2.Builder setSessionActivity(android.app.PendingIntent); + method public android.media.MediaSession2.Builder setSessionCallback(java.util.concurrent.Executor, android.media.MediaSession2.SessionCallback); + } + + public static final class MediaSession2.ControllerInfo { + method public java.lang.String getPackageName(); + method public android.media.session.MediaSessionManager.RemoteUserInfo getRemoteUserInfo(); + method public int getUid(); + } + + public static abstract class MediaSession2.SessionCallback { + ctor public MediaSession2.SessionCallback(); + method public void onCommandResult(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.Object, android.media.Session2Command, android.media.Session2Command.Result); + method public android.media.Session2CommandGroup onConnect(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo); + method public void onDisconnected(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo); + method public android.media.Session2Command.Result onSessionCommand(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.media.Session2Command, android.os.Bundle); + } + public final class MediaSync { ctor public MediaSync(); method public android.view.Surface createInputSurface(); @@ -26118,6 +26217,19 @@ package android.media { method public android.media.Session2CommandGroup.Builder removeCommand(int); } + public final class Session2Token implements android.os.Parcelable { + ctor public Session2Token(android.content.Context, android.content.ComponentName); + method public int describeContents(); + method public java.lang.String getPackageName(); + method public java.lang.String getServiceName(); + method public int getType(); + method public int getUid(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.media.Session2Token> CREATOR; + field public static final int TYPE_SESSION = 0; // 0x0 + field public static final int TYPE_SESSION_SERVICE = 1; // 0x1 + } + public class SoundPool { ctor public deprecated SoundPool(int, int, int); method public final void autoPause(); @@ -29649,8 +29761,10 @@ package android.net.wifi { method public java.lang.String getMacAddress(); method public int getNetworkId(); method public int getRssi(); + method public int getRxLinkSpeedMbps(); method public java.lang.String getSSID(); method public android.net.wifi.SupplicantState getSupplicantState(); + method public int getTxLinkSpeedMbps(); method public void writeToParcel(android.os.Parcel, int); field public static final java.lang.String FREQUENCY_UNITS = "MHz"; field public static final java.lang.String LINK_SPEED_UNITS = "Mbps"; @@ -43057,10 +43171,12 @@ package android.telecom { field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8 field public static final java.lang.String EVENT_CALL_MERGE_FAILED = "android.telecom.event.CALL_MERGE_FAILED"; field public static final java.lang.String EVENT_CALL_PULL_FAILED = "android.telecom.event.CALL_PULL_FAILED"; + field public static final java.lang.String EVENT_RTT_AUDIO_INDICATION_CHANGED = "android.telecom.event.RTT_AUDIO_INDICATION_CHANGED"; field public static final java.lang.String EXTRA_ANSWERING_DROPS_FG_CALL = "android.telecom.extra.ANSWERING_DROPS_FG_CALL"; field public static final java.lang.String EXTRA_ANSWERING_DROPS_FG_CALL_APP_NAME = "android.telecom.extra.ANSWERING_DROPS_FG_CALL_APP_NAME"; field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT"; field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS"; + field public static final java.lang.String EXTRA_IS_RTT_AUDIO_PRESENT = "android.telecom.extra.IS_RTT_AUDIO_PRESENT"; field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER"; field public static final java.lang.String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE"; field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20 @@ -45001,6 +45117,7 @@ package android.telephony.euicc { } public class EuiccManager { + method public android.telephony.euicc.EuiccManager createForCardId(int); method public void deleteSubscription(int, android.app.PendingIntent); method public void downloadSubscription(android.telephony.euicc.DownloadableSubscription, boolean, android.app.PendingIntent); method public java.lang.String getEid(); @@ -47003,7 +47120,7 @@ package android.text.util { public class Linkify { ctor public Linkify(); method public static final boolean addLinks(android.text.Spannable, int); - method public static final boolean addLinks(android.text.Spannable, int, android.text.util.Linkify.UrlSpanFactory); + method public static final boolean addLinks(android.text.Spannable, int, java.util.function.Function<java.lang.String, android.text.style.URLSpan>); method public static final boolean addLinks(android.widget.TextView, int); method public static final void addLinks(android.widget.TextView, java.util.regex.Pattern, java.lang.String); method public static final void addLinks(android.widget.TextView, java.util.regex.Pattern, java.lang.String, android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter); @@ -47011,7 +47128,7 @@ package android.text.util { method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String); method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter); method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, java.lang.String[], android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter); - method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, java.lang.String[], android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter, android.text.util.Linkify.UrlSpanFactory); + method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, java.lang.String[], android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter, java.util.function.Function<java.lang.String, android.text.style.URLSpan>); field public static final int ALL = 15; // 0xf field public static final int EMAIL_ADDRESSES = 2; // 0x2 field public static final deprecated int MAP_ADDRESSES = 8; // 0x8 @@ -47030,11 +47147,6 @@ package android.text.util { method public abstract java.lang.String transformUrl(java.util.regex.Matcher, java.lang.String); } - public static class Linkify.UrlSpanFactory { - ctor public Linkify.UrlSpanFactory(); - method public android.text.style.URLSpan create(java.lang.String); - } - public class Rfc822Token { ctor public Rfc822Token(java.lang.String, java.lang.String, java.lang.String); method public java.lang.String getAddress(); @@ -52938,15 +53050,15 @@ package android.view.inspector { package android.view.textclassifier { - public final class ConversationActions implements android.os.Parcelable { - ctor public ConversationActions(java.util.List<android.view.textclassifier.ConversationActions.ConversationAction>, java.lang.String); + public final class ConversationAction implements android.os.Parcelable { method public int describeContents(); - method public java.util.List<android.view.textclassifier.ConversationActions.ConversationAction> getConversationActions(); - method public java.lang.String getId(); + method public android.app.RemoteAction getAction(); + method public float getConfidenceScore(); + method public android.os.Bundle getExtras(); + method public java.lang.CharSequence getTextReply(); + method public java.lang.String getType(); method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions> CREATOR; - field public static final java.lang.String HINT_FOR_IN_APP = "in_app"; - field public static final java.lang.String HINT_FOR_NOTIFICATION = "notification"; + field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationAction> CREATOR; field public static final java.lang.String TYPE_CALL_PHONE = "call_phone"; field public static final java.lang.String TYPE_CREATE_REMINDER = "create_reminder"; field public static final java.lang.String TYPE_OPEN_URL = "open_url"; @@ -52959,24 +53071,22 @@ package android.view.textclassifier { field public static final java.lang.String TYPE_VIEW_MAP = "view_map"; } - public static final class ConversationActions.ConversationAction implements android.os.Parcelable { - method public int describeContents(); - method public android.app.RemoteAction getAction(); - method public float getConfidenceScore(); - method public android.os.Bundle getExtras(); - method public java.lang.CharSequence getTextReply(); - method public java.lang.String getType(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.ConversationAction> CREATOR; + public static final class ConversationAction.Builder { + ctor public ConversationAction.Builder(java.lang.String); + method public android.view.textclassifier.ConversationAction build(); + method public android.view.textclassifier.ConversationAction.Builder setAction(android.app.RemoteAction); + method public android.view.textclassifier.ConversationAction.Builder setConfidenceScore(float); + method public android.view.textclassifier.ConversationAction.Builder setExtras(android.os.Bundle); + method public android.view.textclassifier.ConversationAction.Builder setTextReply(java.lang.CharSequence); } - public static final class ConversationActions.ConversationAction.Builder { - ctor public ConversationActions.ConversationAction.Builder(java.lang.String); - method public android.view.textclassifier.ConversationActions.ConversationAction build(); - method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setAction(android.app.RemoteAction); - method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setConfidenceScore(float); - method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setExtras(android.os.Bundle); - method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setTextReply(java.lang.CharSequence); + public final class ConversationActions implements android.os.Parcelable { + ctor public ConversationActions(java.util.List<android.view.textclassifier.ConversationAction>, java.lang.String); + method public int describeContents(); + method public java.util.List<android.view.textclassifier.ConversationAction> getConversationActions(); + method public java.lang.String getId(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions> CREATOR; } public static final class ConversationActions.Message implements android.os.Parcelable { @@ -53006,9 +53116,11 @@ package android.view.textclassifier { method public java.lang.String getConversationId(); method public java.util.List<java.lang.String> getHints(); method public int getMaxSuggestions(); - method public android.view.textclassifier.ConversationActions.TypeConfig getTypeConfig(); + method public android.view.textclassifier.TextClassifier.EntityConfig getTypeConfig(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.Request> CREATOR; + field public static final java.lang.String HINT_FOR_IN_APP = "in_app"; + field public static final java.lang.String HINT_FOR_NOTIFICATION = "notification"; } public static final class ConversationActions.Request.Builder { @@ -53017,23 +53129,7 @@ package android.view.textclassifier { method public android.view.textclassifier.ConversationActions.Request.Builder setConversationId(java.lang.String); method public android.view.textclassifier.ConversationActions.Request.Builder setHints(java.util.List<java.lang.String>); method public android.view.textclassifier.ConversationActions.Request.Builder setMaxSuggestions(int); - method public android.view.textclassifier.ConversationActions.Request.Builder setTypeConfig(android.view.textclassifier.ConversationActions.TypeConfig); - } - - public static final class ConversationActions.TypeConfig implements android.os.Parcelable { - method public int describeContents(); - method public java.util.Collection<java.lang.String> resolveTypes(java.util.Collection<java.lang.String>); - method public boolean shouldIncludeTypesFromTextClassifier(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.TypeConfig> CREATOR; - } - - public static final class ConversationActions.TypeConfig.Builder { - ctor public ConversationActions.TypeConfig.Builder(); - method public android.view.textclassifier.ConversationActions.TypeConfig build(); - method public android.view.textclassifier.ConversationActions.TypeConfig.Builder includeTypesFromTextClassifier(boolean); - method public android.view.textclassifier.ConversationActions.TypeConfig.Builder setExcludedTypes(java.util.Collection<java.lang.String>); - method public android.view.textclassifier.ConversationActions.TypeConfig.Builder setIncludedTypes(java.util.Collection<java.lang.String>); + method public android.view.textclassifier.ConversationActions.Request.Builder setTypeConfig(android.view.textclassifier.TextClassifier.EntityConfig); } public final class SelectionEvent implements android.os.Parcelable { @@ -53207,16 +53303,26 @@ package android.view.textclassifier { } public static final class TextClassifier.EntityConfig implements android.os.Parcelable { - method public static android.view.textclassifier.TextClassifier.EntityConfig create(java.util.Collection<java.lang.String>, java.util.Collection<java.lang.String>, java.util.Collection<java.lang.String>); - method public static android.view.textclassifier.TextClassifier.EntityConfig createWithExplicitEntityList(java.util.Collection<java.lang.String>); - method public static android.view.textclassifier.TextClassifier.EntityConfig createWithHints(java.util.Collection<java.lang.String>); + method public static deprecated android.view.textclassifier.TextClassifier.EntityConfig create(java.util.Collection<java.lang.String>, java.util.Collection<java.lang.String>, java.util.Collection<java.lang.String>); + method public static deprecated android.view.textclassifier.TextClassifier.EntityConfig createWithExplicitEntityList(java.util.Collection<java.lang.String>); + method public static deprecated android.view.textclassifier.TextClassifier.EntityConfig createWithHints(java.util.Collection<java.lang.String>); method public int describeContents(); method public java.util.Collection<java.lang.String> getHints(); method public java.util.Collection<java.lang.String> resolveEntityListModifications(java.util.Collection<java.lang.String>); + method public boolean shouldIncludeTypesFromTextClassifier(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifier.EntityConfig> CREATOR; } + public static final class TextClassifier.EntityConfig.Builder { + ctor public TextClassifier.EntityConfig.Builder(); + method public android.view.textclassifier.TextClassifier.EntityConfig build(); + method public android.view.textclassifier.TextClassifier.EntityConfig.Builder includeTypesFromTextClassifier(boolean); + method public android.view.textclassifier.TextClassifier.EntityConfig.Builder setExcludedTypes(java.util.Collection<java.lang.String>); + method public android.view.textclassifier.TextClassifier.EntityConfig.Builder setHints(java.util.Collection<java.lang.String>); + method public android.view.textclassifier.TextClassifier.EntityConfig.Builder setIncludedTypes(java.util.Collection<java.lang.String>); + } + public final class TextClassifierEvent implements android.os.Parcelable { method public int describeContents(); method public int[] getActionIndices(); diff --git a/api/system-current.txt b/api/system-current.txt index 2428b90de9a4..5fac1ee13e2c 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1047,7 +1047,6 @@ package android.app.role { method public void removeRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback); method public boolean removeRoleHolderFromController(java.lang.String, java.lang.String); method public void setRoleNamesFromController(java.util.List<java.lang.String>); - field public static final java.lang.String EXTRA_REQUEST_ROLE_NAME = "android.app.role.extra.REQUEST_ROLE_NAME"; } public abstract interface RoleManagerCallback { @@ -1254,6 +1253,7 @@ package android.content { field public static final java.lang.String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS"; field public static final java.lang.String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION"; field public static final java.lang.String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS"; + field public static final java.lang.String ACTION_MANAGE_DEFAULT_APP = "android.intent.action.MANAGE_DEFAULT_APP"; field public static final java.lang.String ACTION_MANAGE_PERMISSIONS = "android.intent.action.MANAGE_PERMISSIONS"; field public static final java.lang.String ACTION_MANAGE_PERMISSION_APPS = "android.intent.action.MANAGE_PERMISSION_APPS"; field public static final java.lang.String ACTION_MANAGE_SPECIAL_APP_ACCESSES = "android.intent.action.MANAGE_SPECIAL_APP_ACCESSES"; @@ -1284,11 +1284,12 @@ package android.content { field public static final java.lang.String EXTRA_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE"; field public static final java.lang.String EXTRA_ORIGINATING_UID = "android.intent.extra.ORIGINATING_UID"; field public static final java.lang.String EXTRA_PACKAGES = "android.intent.extra.PACKAGES"; - field public static final java.lang.String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME"; field public static final java.lang.String EXTRA_PERMISSION_GROUP_NAME = "android.intent.extra.PERMISSION_GROUP_NAME"; + field public static final java.lang.String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME"; field public static final java.lang.String EXTRA_REASON = "android.intent.extra.REASON"; field public static final java.lang.String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK"; field public static final java.lang.String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED"; + field public static final java.lang.String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME"; field public static final java.lang.String EXTRA_UNKNOWN_INSTANT_APP = "android.intent.extra.UNKNOWN_INSTANT_APP"; field public static final java.lang.String EXTRA_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE"; field public static final java.lang.String METADATA_SETUP_VERSION = "android.SETUP_VERSION"; @@ -1461,6 +1462,7 @@ package android.content.pm { method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle); method public void sendDeviceCustomizationReadyBroadcast(); method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int); + method public java.lang.String[] setDistractingPackageRestrictions(java.lang.String[], int); method public void setHarmfulAppWarning(java.lang.String, java.lang.CharSequence); method public deprecated java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String); method public java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, android.content.pm.SuspendDialogInfo); @@ -1528,6 +1530,9 @@ package android.content.pm { field public static final int MATCH_ANY_USER = 4194304; // 0x400000 field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000 field public static final int MATCH_INSTANT = 8388608; // 0x800000 + field public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 1; // 0x1 + field public static final int RESTRICTION_HIDE_NOTIFICATIONS = 2; // 0x2 + field public static final int RESTRICTION_NONE = 0; // 0x0 } public static abstract class PackageManager.DexModuleRegisterCallback { @@ -1535,6 +1540,9 @@ package android.content.pm { method public abstract void onDexModuleRegistered(java.lang.String, boolean, java.lang.String); } + public static abstract class PackageManager.DistractionRestriction implements java.lang.annotation.Annotation { + } + public static abstract interface PackageManager.OnPermissionsChangedListener { method public abstract void onPermissionsChanged(int); } @@ -3900,24 +3908,24 @@ package android.net { package android.net.wifi { - public abstract class DppStatusCallback { - ctor public DppStatusCallback(); + public abstract class EasyConnectStatusCallback { + ctor public EasyConnectStatusCallback(); method public abstract void onConfiguratorSuccess(int); method public abstract void onEnrolleeSuccess(int); method public abstract void onFailure(int); method public abstract void onProgress(int); - field public static final int DPP_EVENT_FAILURE = -7; // 0xfffffff9 - field public static final int DPP_EVENT_FAILURE_AUTHENTICATION = -2; // 0xfffffffe - field public static final int DPP_EVENT_FAILURE_BUSY = -5; // 0xfffffffb - field public static final int DPP_EVENT_FAILURE_CONFIGURATION = -4; // 0xfffffffc - field public static final int DPP_EVENT_FAILURE_INVALID_NETWORK = -9; // 0xfffffff7 - field public static final int DPP_EVENT_FAILURE_INVALID_URI = -1; // 0xffffffff - field public static final int DPP_EVENT_FAILURE_NOT_COMPATIBLE = -3; // 0xfffffffd - field public static final int DPP_EVENT_FAILURE_NOT_SUPPORTED = -8; // 0xfffffff8 - field public static final int DPP_EVENT_FAILURE_TIMEOUT = -6; // 0xfffffffa - field public static final int DPP_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0; // 0x0 - field public static final int DPP_EVENT_PROGRESS_RESPONSE_PENDING = 1; // 0x1 - field public static final int DPP_EVENT_SUCCESS_CONFIGURATION_SENT = 0; // 0x0 + field public static final int EASY_CONNECT_EVENT_FAILURE = -7; // 0xfffffff9 + field public static final int EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION = -2; // 0xfffffffe + field public static final int EASY_CONNECT_EVENT_FAILURE_BUSY = -5; // 0xfffffffb + field public static final int EASY_CONNECT_EVENT_FAILURE_CONFIGURATION = -4; // 0xfffffffc + field public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK = -9; // 0xfffffff7 + field public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_URI = -1; // 0xffffffff + field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE = -3; // 0xfffffffd + field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED = -8; // 0xfffffff8 + field public static final int EASY_CONNECT_EVENT_FAILURE_TIMEOUT = -6; // 0xfffffffa + field public static final int EASY_CONNECT_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0; // 0x0 + field public static final int EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING = 1; // 0x1 + field public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT = 0; // 0x0 } public deprecated class RttManager { @@ -4148,10 +4156,10 @@ package android.net.wifi { method public void save(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener); method public void setDeviceMobilityState(int); method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration); - method public void startDppAsConfiguratorInitiator(java.lang.String, int, int, android.os.Handler, android.net.wifi.DppStatusCallback); - method public void startDppAsEnrolleeInitiator(java.lang.String, android.os.Handler, android.net.wifi.DppStatusCallback); + method public void startEasyConnectAsConfiguratorInitiator(java.lang.String, int, int, android.os.Handler, android.net.wifi.EasyConnectStatusCallback); + method public void startEasyConnectAsEnrolleeInitiator(java.lang.String, android.os.Handler, android.net.wifi.EasyConnectStatusCallback); method public boolean startScan(android.os.WorkSource); - method public void stopDppSession(); + method public void stopEasyConnectSession(); method public void unregisterNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback); field public static final int CHANGE_REASON_ADDED = 0; // 0x0 field public static final int CHANGE_REASON_CONFIG_CHANGE = 2; // 0x2 @@ -4161,8 +4169,8 @@ package android.net.wifi { field public static final int DEVICE_MOBILITY_STATE_LOW_MVMT = 2; // 0x2 field public static final int DEVICE_MOBILITY_STATE_STATIONARY = 3; // 0x3 field public static final int DEVICE_MOBILITY_STATE_UNKNOWN = 0; // 0x0 - field public static final int DPP_NETWORK_ROLE_AP = 1; // 0x1 - field public static final int DPP_NETWORK_ROLE_STA = 0; // 0x0 + field public static final int EASY_CONNECT_NETWORK_ROLE_AP = 1; // 0x1 + field public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; // 0x0 field public static final java.lang.String EXTRA_CHANGE_REASON = "changeReason"; field public static final java.lang.String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges"; field public static final java.lang.String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state"; @@ -4855,6 +4863,7 @@ package android.permission { method public final android.os.IBinder onBind(android.content.Intent); method public abstract int onCountPermissionApps(java.util.List<java.lang.String>, boolean, boolean); method public abstract java.util.List<android.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(java.lang.String); + method public abstract void onGetRuntimePermissionsBackup(android.os.UserHandle, java.io.OutputStream); method public abstract void onRevokeRuntimePermission(java.lang.String, java.lang.String); method public abstract java.util.Map<java.lang.String, java.util.List<java.lang.String>> onRevokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>, boolean, int, java.lang.String); field public static final java.lang.String SERVICE_INTERFACE = "android.permission.PermissionControllerService"; @@ -6210,6 +6219,40 @@ package android.telephony { field public static final int WWAN = 1; // 0x1 } + public class CallAttributes implements android.os.Parcelable { + ctor public CallAttributes(android.telephony.PreciseCallState, int, android.telephony.CallQuality); + method public int describeContents(); + method public android.telephony.CallQuality getCallQuality(); + method public int getNetworkType(); + method public android.telephony.PreciseCallState getPreciseCallState(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.CallAttributes> CREATOR; + } + + public final class CallQuality implements android.os.Parcelable { + ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int); + method public int describeContents(); + method public int getAverageRelativeJitter(); + method public int getAverageRoundTripTime(); + method public int getCallDuration(); + method public int getCodecType(); + method public int getDownlinkCallQualityLevel(); + method public int getMaxRelativeJitter(); + method public int getNumRtpPacketsNotReceived(); + method public int getNumRtpPacketsReceived(); + method public int getNumRtpPacketsTransmitted(); + method public int getNumRtpPacketsTransmittedLost(); + method public int getUplinkCallQualityLevel(); + method public void writeToParcel(android.os.Parcel, int); + field public static final int CALL_QUALITY_BAD = 4; // 0x4 + field public static final int CALL_QUALITY_EXCELLENT = 0; // 0x0 + field public static final int CALL_QUALITY_FAIR = 2; // 0x2 + field public static final int CALL_QUALITY_GOOD = 1; // 0x1 + field public static final int CALL_QUALITY_NOT_AVAILABLE = 5; // 0x5 + field public static final int CALL_QUALITY_POOR = 3; // 0x3 + field public static final android.os.Parcelable.Creator<android.telephony.CallQuality> CREATOR; + } + public class CarrierConfigManager { method public static android.os.PersistableBundle getDefaultConfig(); method public void overrideConfig(int, android.os.PersistableBundle); @@ -6475,12 +6518,14 @@ package android.telephony { } public class PhoneStateListener { + method public void onCallAttributesChanged(android.telephony.CallAttributes); method public void onCallDisconnectCauseChanged(int, int); method public void onPreciseCallStateChanged(android.telephony.PreciseCallState); method public void onPreciseDataConnectionStateChanged(android.telephony.PreciseDataConnectionState); method public void onRadioPowerStateChanged(int); method public void onSrvccStateChanged(int); method public void onVoiceActivationStateChanged(int); + field public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000 field public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000 field public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800 field public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000 @@ -7207,6 +7252,7 @@ package android.telephony.ims { method public void callSessionInviteParticipantsRequestDelivered(); method public void callSessionInviteParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo); method public void callSessionMayHandover(int, int); + method public void callSessionRttAudioIndicatorChanged(android.telephony.ims.ImsStreamMediaProfile); method public void callSessionMergeComplete(android.telephony.ims.stub.ImsCallSessionImplBase); method public void callSessionMergeFailed(android.telephony.ims.ImsReasonInfo); method public void callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile); @@ -7624,10 +7670,12 @@ package android.telephony.ims { method public int describeContents(); method public int getAudioDirection(); method public int getAudioQuality(); + method public boolean getRttAudioSpeech(); method public int getRttMode(); method public int getVideoDirection(); method public int getVideoQuality(); method public boolean isRttCall(); + method public void setRttAudioSpeech(boolean); method public void setRttMode(int); method public void writeToParcel(android.os.Parcel, int); field public static final int AUDIO_QUALITY_AMR = 1; // 0x1 diff --git a/api/test-current.txt b/api/test-current.txt index 6d40e690b5e4..fb50fa194a92 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -421,6 +421,7 @@ package android.content.pm { public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000 + field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000 field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000 field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000 field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000 @@ -500,6 +501,10 @@ package android.database.sqlite { package android.graphics { + public final class Bitmap implements android.os.Parcelable { + method public void eraseColor(long); + } + public final class ImageDecoder implements java.lang.AutoCloseable { method public static android.graphics.ImageDecoder.Source createSource(android.content.res.Resources, java.io.InputStream, int); } @@ -623,6 +628,7 @@ package android.location { method public void resetCarrierFrequencyHz(); method public deprecated void resetCarrierPhase(); method public deprecated void resetCarrierPhaseUncertainty(); + method public void resetCodeType(); method public void resetSnrInDb(); method public void set(android.location.GnssMeasurement); method public void setAccumulatedDeltaRangeMeters(double); @@ -634,6 +640,7 @@ package android.location { method public deprecated void setCarrierPhase(double); method public deprecated void setCarrierPhaseUncertainty(double); method public void setCn0DbHz(double); + method public void setCodeType(int); method public void setConstellationType(int); method public void setMultipathIndicator(int); method public void setPseudorangeRateMetersPerSecond(double); @@ -790,6 +797,134 @@ package android.os { method public static java.io.File getStorageDirectory(); } + public abstract class HwBinder implements android.os.IHwBinder { + ctor public HwBinder(); + method public static final void configureRpcThreadpool(long, boolean); + method public static void enableInstrumentation(); + method public static final android.os.IHwBinder getService(java.lang.String, java.lang.String) throws java.util.NoSuchElementException, android.os.RemoteException; + method public static final android.os.IHwBinder getService(java.lang.String, java.lang.String, boolean) throws java.util.NoSuchElementException, android.os.RemoteException; + method public static final void joinRpcThreadpool(); + method public abstract void onTransact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException; + method public final void registerService(java.lang.String) throws android.os.RemoteException; + method public final void transact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException; + } + + public class HwBlob { + ctor public HwBlob(int); + method public final void copyToBoolArray(long, boolean[], int); + method public final void copyToDoubleArray(long, double[], int); + method public final void copyToFloatArray(long, float[], int); + method public final void copyToInt16Array(long, short[], int); + method public final void copyToInt32Array(long, int[], int); + method public final void copyToInt64Array(long, long[], int); + method public final void copyToInt8Array(long, byte[], int); + method public final boolean getBool(long); + method public final double getDouble(long); + method public final float getFloat(long); + method public final short getInt16(long); + method public final int getInt32(long); + method public final long getInt64(long); + method public final byte getInt8(long); + method public final java.lang.String getString(long); + method public final long handle(); + method public final void putBlob(long, android.os.HwBlob); + method public final void putBool(long, boolean); + method public final void putBoolArray(long, boolean[]); + method public final void putDouble(long, double); + method public final void putDoubleArray(long, double[]); + method public final void putFloat(long, float); + method public final void putFloatArray(long, float[]); + method public final void putInt16(long, short); + method public final void putInt16Array(long, short[]); + method public final void putInt32(long, int); + method public final void putInt32Array(long, int[]); + method public final void putInt64(long, long); + method public final void putInt64Array(long, long[]); + method public final void putInt8(long, byte); + method public final void putInt8Array(long, byte[]); + method public final void putNativeHandle(long, android.os.NativeHandle); + method public final void putString(long, java.lang.String); + method public static java.lang.Boolean[] wrapArray(boolean[]); + method public static java.lang.Long[] wrapArray(long[]); + method public static java.lang.Byte[] wrapArray(byte[]); + method public static java.lang.Short[] wrapArray(short[]); + method public static java.lang.Integer[] wrapArray(int[]); + method public static java.lang.Float[] wrapArray(float[]); + method public static java.lang.Double[] wrapArray(double[]); + } + + public class HwParcel { + ctor public HwParcel(); + method public final void enforceInterface(java.lang.String); + method public final boolean readBool(); + method public final java.util.ArrayList<java.lang.Boolean> readBoolVector(); + method public final android.os.HwBlob readBuffer(long); + method public final double readDouble(); + method public final java.util.ArrayList<java.lang.Double> readDoubleVector(); + method public final android.os.HwBlob readEmbeddedBuffer(long, long, long, boolean); + method public final android.os.NativeHandle readEmbeddedNativeHandle(long, long); + method public final float readFloat(); + method public final java.util.ArrayList<java.lang.Float> readFloatVector(); + method public final short readInt16(); + method public final java.util.ArrayList<java.lang.Short> readInt16Vector(); + method public final int readInt32(); + method public final java.util.ArrayList<java.lang.Integer> readInt32Vector(); + method public final long readInt64(); + method public final java.util.ArrayList<java.lang.Long> readInt64Vector(); + method public final byte readInt8(); + method public final java.util.ArrayList<java.lang.Byte> readInt8Vector(); + method public final android.os.NativeHandle readNativeHandle(); + method public final java.util.ArrayList<android.os.NativeHandle> readNativeHandleVector(); + method public final java.lang.String readString(); + method public final java.util.ArrayList<java.lang.String> readStringVector(); + method public final android.os.IHwBinder readStrongBinder(); + method public final void release(); + method public final void releaseTemporaryStorage(); + method public final void send(); + method public final void verifySuccess(); + method public final void writeBool(boolean); + method public final void writeBoolVector(java.util.ArrayList<java.lang.Boolean>); + method public final void writeBuffer(android.os.HwBlob); + method public final void writeDouble(double); + method public final void writeDoubleVector(java.util.ArrayList<java.lang.Double>); + method public final void writeFloat(float); + method public final void writeFloatVector(java.util.ArrayList<java.lang.Float>); + method public final void writeInt16(short); + method public final void writeInt16Vector(java.util.ArrayList<java.lang.Short>); + method public final void writeInt32(int); + method public final void writeInt32Vector(java.util.ArrayList<java.lang.Integer>); + method public final void writeInt64(long); + method public final void writeInt64Vector(java.util.ArrayList<java.lang.Long>); + method public final void writeInt8(byte); + method public final void writeInt8Vector(java.util.ArrayList<java.lang.Byte>); + method public final void writeInterfaceToken(java.lang.String); + method public final void writeNativeHandle(android.os.NativeHandle); + method public final void writeNativeHandleVector(java.util.ArrayList<android.os.NativeHandle>); + method public final void writeStatus(int); + method public final void writeString(java.lang.String); + method public final void writeStringVector(java.util.ArrayList<java.lang.String>); + method public final void writeStrongBinder(android.os.IHwBinder); + field public static final int STATUS_SUCCESS = 0; // 0x0 + } + + public static abstract class HwParcel.Status implements java.lang.annotation.Annotation { + } + + public abstract interface IHwBinder { + method public abstract boolean linkToDeath(android.os.IHwBinder.DeathRecipient, long); + method public abstract android.os.IHwInterface queryLocalInterface(java.lang.String); + method public abstract void transact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException; + method public abstract boolean unlinkToDeath(android.os.IHwBinder.DeathRecipient); + } + + public static abstract interface IHwBinder.DeathRecipient { + method public abstract void serviceDied(long); + } + + public abstract interface IHwInterface { + method public abstract android.os.IHwBinder asBinder(); + } + public class IncidentManager { method public void reportIncident(android.os.IncidentReportArgs); } @@ -810,6 +945,18 @@ package android.os { field public static final android.os.Parcelable.Creator<android.os.IncidentReportArgs> CREATOR; } + public final class NativeHandle implements java.io.Closeable { + ctor public NativeHandle(); + ctor public NativeHandle(java.io.FileDescriptor, boolean); + ctor public NativeHandle(java.io.FileDescriptor[], int[], boolean); + method public void close() throws java.io.IOException; + method public android.os.NativeHandle dup() throws java.io.IOException; + method public java.io.FileDescriptor getFileDescriptor(); + method public java.io.FileDescriptor[] getFileDescriptors(); + method public int[] getInts(); + method public boolean hasSingleFileDescriptor(); + } + public final class MessageQueue { method public int postSyncBarrier(); method public void removeSyncBarrier(int); @@ -825,6 +972,11 @@ package android.os { public class Process { method public static final int getThreadScheduler(int) throws java.lang.IllegalArgumentException; + field public static final int FIRST_APP_ZYGOTE_ISOLATED_UID = 90000; // 0x15f90 + field public static final int FIRST_ISOLATED_UID = 99000; // 0x182b8 + field public static final int LAST_APP_ZYGOTE_ISOLATED_UID = 98999; // 0x182b7 + field public static final int LAST_ISOLATED_UID = 99999; // 0x1869f + field public static final int NUM_UIDS_PER_APP_ZYGOTE = 100; // 0x64 } public final class RemoteCallback implements android.os.Parcelable { diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp index ec2decf782e2..b78e942d4049 100644 --- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp +++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp @@ -89,6 +89,7 @@ void RawPrintVisitor::visit(const IdmapData::TypeEntry& type_entry) { } } +// NOLINTNEXTLINE(cert-dcl50-cpp) void RawPrintVisitor::print(uint16_t value, const char* fmt, ...) { va_list ap; va_start(ap, fmt); @@ -101,6 +102,7 @@ void RawPrintVisitor::print(uint16_t value, const char* fmt, ...) { offset_ += sizeof(uint16_t); } +// NOLINTNEXTLINE(cert-dcl50-cpp) void RawPrintVisitor::print(uint32_t value, const char* fmt, ...) { va_list ap; va_start(ap, fmt); @@ -113,6 +115,7 @@ void RawPrintVisitor::print(uint32_t value, const char* fmt, ...) { offset_ += sizeof(uint32_t); } +// NOLINTNEXTLINE(cert-dcl50-cpp) void RawPrintVisitor::print(const std::string& value, const char* fmt, ...) { va_list ap; va_start(ap, fmt); diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp index 8fa298060d60..3d74f8b207af 100644 --- a/cmds/screencap/screencap.cpp +++ b/cmds/screencap/screencap.cpp @@ -81,8 +81,7 @@ static sk_sp<SkColorSpace> dataSpaceToColorSpace(ui::Dataspace d) case ui::Dataspace::V0_SRGB: return SkColorSpace::MakeSRGB(); case ui::Dataspace::DISPLAY_P3: - return SkColorSpace::MakeRGB( - SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kDCIP3_D65_Gamut); + return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3); default: return nullptr; } diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp index 9d37cdb2d4d7..ad5eae361f1c 100644 --- a/cmds/statsd/src/anomaly/subscriber_util.cpp +++ b/cmds/statsd/src/anomaly/subscriber_util.cpp @@ -57,7 +57,7 @@ void triggerSubscribers(const int64_t rule_id, break; case Subscription::SubscriberInformationCase::kPerfettoDetails: if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details(), - rule_id, configKey)) { + subscription.id(), rule_id, configKey)) { ALOGW("Failed to generate perfetto traces."); } break; diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 860e40d67435..0fa7cffa07af 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -181,6 +181,7 @@ message Atom { DocsUISearchTypeReported docs_ui_search_type_reported = 120; DataStallEvent data_stall_event = 121; RescuePartyResetReported rescue_party_reset_reported = 122; + SignedConfigReported signed_config_reported = 123; } // Pulled events will start at field 10000. @@ -3802,7 +3803,7 @@ message NetworkDnsEventReported { //bionic/libc/include/netdb.h //system/netd/resolv/include/netd_resolv/resolv.h enum ReturnCode { - EAI_NO_ERR = 0; + EAI_NO_ERROR = 0; EAI_ADDRFAMILY = 1; EAI_AGAIN = 2; EAI_BADFLAGS = 3; @@ -3859,3 +3860,45 @@ message RescuePartyResetReported { // The rescue level of this reset. A value of 0 indicates missing or unknown level information. optional int32 rescue_level = 1; } + +/** + * Logs when signed config is received from an APK, and if that config was applied successfully. + * Logged from: + * frameworks/base/services/core/java/com/android/server/signedconfig/SignedConfigService.java + */ +message SignedConfigReported { + enum Type { + UNKNOWN_TYPE = 0; + GLOBAL_SETTINGS = 1; + } + optional Type type = 1; + + // The final status of the signed config received. + enum Status { + UNKNOWN_STATUS = 0; + APPLIED = 1; + BASE64_FAILURE_CONFIG = 2; + BASE64_FAILURE_SIGNATURE = 3; + SECURITY_EXCEPTION = 4; + INVALID_CONFIG = 5; + OLD_CONFIG = 6; + SIGNATURE_CHECK_FAILED = 7; + NOT_APPLICABLE = 8; + SIGNATURE_CHECK_FAILED_PROD_KEY_ABSENT = 9; + } + optional Status status = 2; + + // The version of the signed config processed. + optional int32 version = 3; + + // The package name that the config was extracted from. + optional string from_package = 4; + + enum Key { + NO_KEY = 0; + DEBUG = 1; + PRODUCTION = 2; + } + // Which key was used to verify the config. + optional Key verified_with = 5; +} diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp index 42cc543f18a8..0c4c3301e61e 100644 --- a/cmds/statsd/src/external/Perfetto.cpp +++ b/cmds/statsd/src/external/Perfetto.cpp @@ -39,6 +39,7 @@ namespace os { namespace statsd { bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config, + int64_t subscription_id, int64_t alert_id, const ConfigKey& configKey) { VLOG("Starting trace collection through perfetto"); @@ -48,9 +49,11 @@ bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config, return false; } - char alertId[20]; - char configId[20]; - char configUid[20]; + char subscriptionId[25]; + char alertId[25]; + char configId[25]; + char configUid[25]; + snprintf(subscriptionId, sizeof(subscriptionId), "%" PRId64, subscription_id); snprintf(alertId, sizeof(alertId), "%" PRId64, alert_id); snprintf(configId, sizeof(configId), "%" PRId64, configKey.GetId()); snprintf(configUid, sizeof(configUid), "%d", configKey.GetUid()); @@ -94,7 +97,7 @@ bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config, execl("/system/bin/perfetto", "perfetto", "--background", "--config", "-", "--dropbox", kDropboxTag, "--alert-id", alertId, "--config-id", configId, "--config-uid", - configUid, nullptr); + configUid, "--subscription-id", subscriptionId, nullptr); // execl() doesn't return in case of success, if we get here something // failed. diff --git a/cmds/statsd/src/external/Perfetto.h b/cmds/statsd/src/external/Perfetto.h index 1e7f728e1357..ab2c1954e2a0 100644 --- a/cmds/statsd/src/external/Perfetto.h +++ b/cmds/statsd/src/external/Perfetto.h @@ -32,6 +32,7 @@ class PerfettoDetails; // Declared in statsd_config.pb.h // This method returns immediately after passing the config and does NOT wait // for the full duration of the trace. bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config, + int64_t subscription_id, int64_t alert_id, const ConfigKey& configKey); diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index c90e40411240..1f01e2698fb5 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -317,4 +317,7 @@ public abstract class ActivityManagerInternal { /** Returns true if the given uid is the app in the foreground. */ public abstract boolean isAppForeground(int uid); + + /** Remove pending backup for the given userId. */ + public abstract void clearPendingBackup(int userId); } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 94983e1c3672..9bcb36f28d17 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -2276,6 +2276,16 @@ public class ApplicationPackageManager extends PackageManager { } @Override + public String[] setDistractingPackageRestrictions(String[] packages, int distractionFlags) { + try { + return mPM.setDistractingPackageRestrictionsAsUser(packages, distractionFlags, + mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override public String[] setPackagesSuspended(String[] packageNames, boolean suspended, PersistableBundle appExtras, PersistableBundle launcherExtras, String dialogMessage) { diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 347973ea3387..fb65da14a087 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -317,7 +317,6 @@ interface IActivityManager { */ void requestWifiBugReport(in String shareTitle, in String shareDescription); - void clearPendingBackup(); Intent getIntentForIntentSender(in IIntentSender sender); // This is not public because you need to be very careful in how you // manage your activity to make sure it is always the uid you expect. diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 163be8efc8fd..199c1338c50c 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -65,9 +65,9 @@ interface INotificationManager boolean areNotificationsEnabled(String pkg); int getPackageImportance(String pkg); - void setAppOverlaysAllowed(String pkg, int uid, boolean allowed); - boolean areAppOverlaysAllowed(String pkg); - boolean areAppOverlaysAllowedForPackage(String pkg, int uid); + void setBubblesAllowed(String pkg, int uid, boolean allowed); + boolean areBubblesAllowed(String pkg); + boolean areBubblesAllowedForPackage(String pkg, int uid); void createNotificationChannelGroups(String pkg, in ParceledListSlice channelGroupList); void createNotificationChannels(String pkg, in ParceledListSlice channelsList); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index b657a916604b..72819cb1493b 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1276,7 +1276,7 @@ public class Notification implements Parcelable private String mShortcutId; private CharSequence mSettingsText; - private PendingIntent mAppOverlayIntent; + private BubbleMetadata mBubbleMetadata; /** @hide */ @IntDef(prefix = { "GROUP_ALERT_" }, value = { @@ -2278,7 +2278,7 @@ public class Notification implements Parcelable mGroupAlertBehavior = parcel.readInt(); if (parcel.readInt() != 0) { - mAppOverlayIntent = PendingIntent.CREATOR.createFromParcel(parcel); + mBubbleMetadata = BubbleMetadata.CREATOR.createFromParcel(parcel); } mAllowSystemGeneratedContextualActions = parcel.readBoolean(); @@ -2396,7 +2396,7 @@ public class Notification implements Parcelable that.mBadgeIcon = this.mBadgeIcon; that.mSettingsText = this.mSettingsText; that.mGroupAlertBehavior = this.mGroupAlertBehavior; - that.mAppOverlayIntent = this.mAppOverlayIntent; + that.mBubbleMetadata = this.mBubbleMetadata; that.mAllowSystemGeneratedContextualActions = this.mAllowSystemGeneratedContextualActions; if (!heavy) { @@ -2719,9 +2719,9 @@ public class Notification implements Parcelable parcel.writeInt(mGroupAlertBehavior); - if (mAppOverlayIntent != null) { + if (mBubbleMetadata != null) { parcel.writeInt(1); - mAppOverlayIntent.writeToParcel(parcel, 0); + mBubbleMetadata.writeToParcel(parcel, 0); } else { parcel.writeInt(0); } @@ -3141,11 +3141,11 @@ public class Notification implements Parcelable } /** - * Returns the intent that will be used to display app content in a floating window over the - * existing foreground activity. + * Returns the bubble metadata that will be used to display app content in a floating window + * over the existing foreground activity. */ - public PendingIntent getAppOverlayIntent() { - return mAppOverlayIntent; + public BubbleMetadata getBubbleMetadata() { + return mBubbleMetadata; } /** @@ -3508,19 +3508,18 @@ public class Notification implements Parcelable } /** - * Sets the intent that will be used to display app content in a floating window - * over the existing foreground activity. + * Sets the {@link BubbleMetadata} that will be used to display app content in a floating + * window over the existing foreground activity. * - * <p>This intent will be ignored unless this notification is posted to a channel that - * allows {@link NotificationChannel#canOverlayApps() app overlays}.</p> + * <p>This data will be ignored unless the notification is posted to a channel that + * allows {@link NotificationChannel#canBubble() bubbles}.</p> * - * <p>Notifications with a valid and allowed app overlay intent will be displayed as - * floating windows outside of the notification shade on unlocked devices. When a user - * interacts with one of these windows, this app overlay intent will be invoked and - * displayed.</p> + * <b>Notifications with a valid and allowed bubble metadata will display in collapsed state + * outside of the notification shade on unlocked devices. When a user interacts with the + * collapsed state, the bubble intent will be invoked and displayed.</b> */ - public Builder setAppOverlayIntent(PendingIntent intent) { - mN.mAppOverlayIntent = intent; + public Builder setBubbleMetadata(BubbleMetadata data) { + mN.mBubbleMetadata = data; return this; } @@ -8422,6 +8421,186 @@ public class Notification implements Parcelable } } + /** + * Encapsulates the information needed to display a notification as a bubble. + * + * <p>A bubble is used to display app content in a floating window over the existing + * foreground activity. A bubble has a collapsed state represented by an icon, + * {@link BubbleMetadata.Builder#setIcon(Icon)} and an expanded state which is populated + * via {@link BubbleMetadata.Builder#setIntent(PendingIntent)}.</p> + * + * <b>Notifications with a valid and allowed bubble will display in collapsed state + * outside of the notification shade on unlocked devices. When a user interacts with the + * collapsed bubble, the bubble intent will be invoked and displayed.</b> + * + * @see Notification.Builder#setBubbleMetadata(BubbleMetadata) + */ + public static final class BubbleMetadata implements Parcelable { + + private PendingIntent mPendingIntent; + private CharSequence mTitle; + private Icon mIcon; + private int mDesiredHeight; + + private BubbleMetadata(PendingIntent intent, CharSequence title, Icon icon, int height) { + mPendingIntent = intent; + mTitle = title; + mIcon = icon; + mDesiredHeight = height; + } + + private BubbleMetadata(Parcel in) { + mPendingIntent = PendingIntent.CREATOR.createFromParcel(in); + mTitle = in.readCharSequence(); + mIcon = Icon.CREATOR.createFromParcel(in); + mDesiredHeight = in.readInt(); + } + + /** + * @return the pending intent used to populate the floating window for this bubble. + */ + public PendingIntent getIntent() { + return mPendingIntent; + } + + /** + * @return the title that will appear along with the app content defined by + * {@link #getIntent()} for this bubble. + */ + public CharSequence getTitle() { + return mTitle; + } + + /** + * @return the icon that will be displayed for this bubble when it is collapsed. + */ + public Icon getIcon() { + return mIcon; + } + + /** + * @return the ideal height for the floating window that app content defined by + * {@link #getIntent()} for this bubble. + */ + public int getDesiredHeight() { + return mDesiredHeight; + } + + public static final Parcelable.Creator<BubbleMetadata> CREATOR = + new Parcelable.Creator<BubbleMetadata>() { + + @Override + public BubbleMetadata createFromParcel(Parcel source) { + return new BubbleMetadata(source); + } + + @Override + public BubbleMetadata[] newArray(int size) { + return new BubbleMetadata[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + mPendingIntent.writeToParcel(out, 0); + out.writeCharSequence(mTitle); + mIcon.writeToParcel(out, 0); + out.writeInt(mDesiredHeight); + } + + /** + * Builder to construct a {@link BubbleMetadata} object. + */ + public static class Builder { + + private PendingIntent mPendingIntent; + private CharSequence mTitle; + private Icon mIcon; + private int mDesiredHeight; + + /** + * Constructs a new builder object. + */ + public Builder() { + } + + /** + * Sets the intent that will be used when the bubble is expanded. This will display the + * app content in a floating window over the existing foreground activity. + */ + public BubbleMetadata.Builder setIntent(PendingIntent intent) { + if (intent == null) { + throw new IllegalArgumentException("Bubble requires non-null pending intent"); + } + mPendingIntent = intent; + return this; + } + + /** + * Sets the title that will appear along with the app content for this bubble. + * + * <p>A title is required and should expect to fit on a single line and make sense when + * shown with the content defined by {@link #setIntent(PendingIntent)}.</p> + */ + public BubbleMetadata.Builder setTitle(CharSequence title) { + if (TextUtils.isEmpty(title)) { + throw new IllegalArgumentException("Bubbles require non-null or empty title"); + } + mTitle = title; + return this; + } + + /** + * Sets the icon that will represent the bubble when it is collapsed. + * + * <p>An icon is required and should be representative of the content within the bubble. + * If your app produces multiple bubbles, the image should be unique for each of them. + * </p> + */ + public BubbleMetadata.Builder setIcon(Icon icon) { + if (icon == null) { + throw new IllegalArgumentException("Bubbles require non-null icon"); + } + mIcon = icon; + return this; + } + + /** + * Sets the desired height for the app content defined by + * {@link #setIntent(PendingIntent)}, this height may not be respected if there is not + * enough space on the screen or if the provided height is too small to be useful. + */ + public BubbleMetadata.Builder setDesiredHeight(int height) { + mDesiredHeight = Math.max(height, 0); + return this; + } + + /** + * Creates the {@link BubbleMetadata} defined by this builder. + * <p>Will throw {@link IllegalStateException} if required fields have not been set + * on this builder.</p> + */ + public BubbleMetadata build() { + if (mPendingIntent == null) { + throw new IllegalStateException("Must supply pending intent to bubble"); + } + if (TextUtils.isEmpty(mTitle)) { + throw new IllegalStateException("Must supply a title for the bubble"); + } + if (mIcon == null) { + throw new IllegalStateException("Must supply an icon for the bubble"); + } + return new BubbleMetadata(mPendingIntent, mTitle, mIcon, mDesiredHeight); + } + } + } + + // When adding a new Style subclass here, don't forget to update // Builder.getNotificationStyleClass. diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index 950e9aa939f8..e95d62fc96eb 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -85,7 +85,7 @@ public final class NotificationChannel implements Parcelable { private static final String ATT_FG_SERVICE_SHOWN = "fgservice"; private static final String ATT_GROUP = "group"; private static final String ATT_BLOCKABLE_SYSTEM = "blockable_system"; - private static final String ATT_ALLOW_APP_OVERLAY = "app_overlay"; + private static final String ATT_ALLOW_BUBBLE = "allow_bubble"; private static final String DELIMITER = ","; /** @@ -121,7 +121,7 @@ public final class NotificationChannel implements Parcelable { /** * @hide */ - public static final int USER_LOCKED_ALLOW_APP_OVERLAY = 0x00000100; + public static final int USER_LOCKED_ALLOW_BUBBLE = 0x00000100; /** * @hide @@ -134,7 +134,7 @@ public final class NotificationChannel implements Parcelable { USER_LOCKED_VIBRATION, USER_LOCKED_SOUND, USER_LOCKED_SHOW_BADGE, - USER_LOCKED_ALLOW_APP_OVERLAY + USER_LOCKED_ALLOW_BUBBLE }; private static final int DEFAULT_LIGHT_COLOR = 0; @@ -144,7 +144,7 @@ public final class NotificationChannel implements Parcelable { NotificationManager.IMPORTANCE_UNSPECIFIED; private static final boolean DEFAULT_DELETED = false; private static final boolean DEFAULT_SHOW_BADGE = true; - private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true; + private static final boolean DEFAULT_ALLOW_BUBBLE = true; @UnsupportedAppUsage private final String mId; @@ -168,7 +168,7 @@ public final class NotificationChannel implements Parcelable { private AudioAttributes mAudioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT; // If this is a blockable system notification channel. private boolean mBlockableSystem = false; - private boolean mAllowAppOverlay = DEFAULT_ALLOW_APP_OVERLAY; + private boolean mAllowBubbles = DEFAULT_ALLOW_BUBBLE; private boolean mImportanceLockedByOEM; /** @@ -231,7 +231,7 @@ public final class NotificationChannel implements Parcelable { mAudioAttributes = in.readInt() > 0 ? AudioAttributes.CREATOR.createFromParcel(in) : null; mLightColor = in.readInt(); mBlockableSystem = in.readBoolean(); - mAllowAppOverlay = in.readBoolean(); + mAllowBubbles = in.readBoolean(); mImportanceLockedByOEM = in.readBoolean(); } @@ -285,7 +285,7 @@ public final class NotificationChannel implements Parcelable { } dest.writeInt(mLightColor); dest.writeBoolean(mBlockableSystem); - dest.writeBoolean(mAllowAppOverlay); + dest.writeBoolean(mAllowBubbles); dest.writeBoolean(mImportanceLockedByOEM); } @@ -480,7 +480,7 @@ public final class NotificationChannel implements Parcelable { /** * Sets whether notifications posted to this channel can appear outside of the notification - * shade, floating over other apps' content. + * shade, floating over other apps' content as a bubble. * * <p>This value will be ignored for channels that aren't allowed to pop on screen (that is, * channels whose {@link #getImportance() importance} is < @@ -488,10 +488,10 @@ public final class NotificationChannel implements Parcelable { * * <p>Only modifiable before the channel is submitted to * * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.</p> - * @see Notification#getAppOverlayIntent() + * @see Notification#getBubbleMetadata() */ - public void setAllowAppOverlay(boolean allowAppOverlay) { - mAllowAppOverlay = allowAppOverlay; + public void setAllowBubbles(boolean allowBubbles) { + mAllowBubbles = allowBubbles; } /** @@ -610,16 +610,16 @@ public final class NotificationChannel implements Parcelable { * Returns whether notifications posted to this channel can display outside of the notification * shade, in a floating window on top of other apps. */ - public boolean canOverlayApps() { - return isAppOverlayAllowed() && getImportance() >= IMPORTANCE_HIGH; + public boolean canBubble() { + return isBubbleAllowed() && getImportance() >= IMPORTANCE_HIGH; } /** - * Like {@link #canOverlayApps()}, but only checks the permission, not the importance. + * Like {@link #canBubble()}, but only checks the permission, not the importance. * @hide */ - public boolean isAppOverlayAllowed() { - return mAllowAppOverlay; + public boolean isBubbleAllowed() { + return mAllowBubbles; } /** @@ -719,7 +719,7 @@ public final class NotificationChannel implements Parcelable { lockFields(safeInt(parser, ATT_USER_LOCKED, 0)); setFgServiceShown(safeBool(parser, ATT_FG_SERVICE_SHOWN, false)); setBlockableSystem(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false)); - setAllowAppOverlay(safeBool(parser, ATT_ALLOW_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY)); + setAllowBubbles(safeBool(parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE)); } @Nullable @@ -838,8 +838,8 @@ public final class NotificationChannel implements Parcelable { if (isBlockableSystem()) { out.attribute(null, ATT_BLOCKABLE_SYSTEM, Boolean.toString(isBlockableSystem())); } - if (canOverlayApps() != DEFAULT_ALLOW_APP_OVERLAY) { - out.attribute(null, ATT_ALLOW_APP_OVERLAY, Boolean.toString(canOverlayApps())); + if (canBubble() != DEFAULT_ALLOW_BUBBLE) { + out.attribute(null, ATT_ALLOW_BUBBLE, Boolean.toString(canBubble())); } out.endTag(null, TAG_CHANNEL); @@ -883,7 +883,7 @@ public final class NotificationChannel implements Parcelable { record.put(ATT_DELETED, Boolean.toString(isDeleted())); record.put(ATT_GROUP, getGroup()); record.put(ATT_BLOCKABLE_SYSTEM, isBlockableSystem()); - record.put(ATT_ALLOW_APP_OVERLAY, canOverlayApps()); + record.put(ATT_ALLOW_BUBBLE, canBubble()); return record; } @@ -983,7 +983,7 @@ public final class NotificationChannel implements Parcelable { && mShowBadge == that.mShowBadge && isDeleted() == that.isDeleted() && isBlockableSystem() == that.isBlockableSystem() - && mAllowAppOverlay == that.mAllowAppOverlay + && mAllowBubbles == that.mAllowBubbles && Objects.equals(getId(), that.getId()) && Objects.equals(getName(), that.getName()) && Objects.equals(mDesc, that.mDesc) @@ -1000,7 +1000,7 @@ public final class NotificationChannel implements Parcelable { getLockscreenVisibility(), getSound(), mLights, getLightColor(), getUserLockedFields(), isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getGroup(), - getAudioAttributes(), isBlockableSystem(), mAllowAppOverlay, + getAudioAttributes(), isBlockableSystem(), mAllowBubbles, mImportanceLockedByOEM); result = 31 * result + Arrays.hashCode(mVibration); return result; @@ -1028,7 +1028,7 @@ public final class NotificationChannel implements Parcelable { + ", mGroup='" + mGroup + '\'' + ", mAudioAttributes=" + mAudioAttributes + ", mBlockableSystem=" + mBlockableSystem - + ", mAllowAppOverlay=" + mAllowAppOverlay + + ", mAllowBubbles=" + mAllowBubbles + ", mImportanceLockedByOEM=" + mImportanceLockedByOEM + '}'; pw.println(prefix + output); @@ -1055,7 +1055,7 @@ public final class NotificationChannel implements Parcelable { + ", mGroup='" + mGroup + '\'' + ", mAudioAttributes=" + mAudioAttributes + ", mBlockableSystem=" + mBlockableSystem - + ", mAllowAppOverlay=" + mAllowAppOverlay + + ", mAllowBubbles=" + mAllowBubbles + ", mImportanceLockedByOEM=" + mImportanceLockedByOEM + '}'; } @@ -1090,7 +1090,7 @@ public final class NotificationChannel implements Parcelable { mAudioAttributes.writeToProto(proto, NotificationChannelProto.AUDIO_ATTRIBUTES); } proto.write(NotificationChannelProto.IS_BLOCKABLE_SYSTEM, mBlockableSystem); - proto.write(NotificationChannelProto.ALLOW_APP_OVERLAY, mAllowAppOverlay); + proto.write(NotificationChannelProto.ALLOW_APP_OVERLAY, mAllowBubbles); proto.end(token); } diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index aad32532bbe8..43614feb28a4 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -1080,14 +1080,14 @@ public class NotificationManager { * notification shade, floating over other apps' content. * * <p>This value will be ignored for notifications that are posted to channels that do not - * allow app overlays ({@link NotificationChannel#canOverlayApps()}. + * allow bubbles ({@link NotificationChannel#canBubble()}. * - * @see Notification#getAppOverlayIntent() + * @see Notification#getBubbleMetadata() */ - public boolean areAppOverlaysAllowed() { + public boolean areBubblesAllowed() { INotificationManager service = getService(); try { - return service.areAppOverlaysAllowed(mContext.getPackageName()); + return service.areBubblesAllowed(mContext.getPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java index 27581fc0088a..a6abe0b30f79 100644 --- a/core/java/android/app/role/RoleManager.java +++ b/core/java/android/app/role/RoleManager.java @@ -179,16 +179,6 @@ public final class RoleManager { public static final String ACTION_REQUEST_ROLE = "android.app.role.action.REQUEST_ROLE"; /** - * The name of the requested role. - * <p> - * <strong>Type:</strong> String - * - * @hide - */ - @SystemApi - public static final String EXTRA_REQUEST_ROLE_NAME = "android.app.role.extra.REQUEST_ROLE_NAME"; - - /** * The permission required to manage records of role holders in {@link RoleManager} directly. * * @hide @@ -236,7 +226,7 @@ public final class RoleManager { Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); Intent intent = new Intent(ACTION_REQUEST_ROLE); intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName()); - intent.putExtra(EXTRA_REQUEST_ROLE_NAME, roleName); + intent.putExtra(Intent.EXTRA_ROLE_NAME, roleName); return intent; } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index e37126b8c2e7..d27cce535c3b 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1833,6 +1833,37 @@ public class Intent implements Parcelable, Cloneable { "android.intent.action.REVIEW_PERMISSIONS"; /** + * Activity action: Launch UI to manage a default app. + * <p> + * Input: {@link #EXTRA_ROLE_NAME} specifies the role of the default app which will be managed + * by the launched UI. + * </p> + * <p> + * Output: Nothing. + * </p> + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + @SystemApi + public static final String ACTION_MANAGE_DEFAULT_APP = + "android.intent.action.MANAGE_DEFAULT_APP"; + + /** + * Intent extra: A role name. + * <p> + * Type: String + * </p> + * + * @see android.app.role.RoleManager + * + * @hide + */ + @SystemApi + public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME"; + + /** * Activity action: Launch UI to manage special app accesses. * <p> * Input: Nothing. @@ -2411,6 +2442,25 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_PACKAGES_UNSUSPENDED = "android.intent.action.PACKAGES_UNSUSPENDED"; /** + * Broadcast Action: Distracting packages have been changed. + * <p>Includes the following extras: + * <ul> + * <li> {@link #EXTRA_CHANGED_PACKAGE_LIST} is the set of packages which have been changed. + * <li> {@link #EXTRA_CHANGED_UID_LIST} is the set of uids which have been changed. + * <li> {@link #EXTRA_DISTRACTION_RESTRICTIONS} the new restrictions set on these packages. + * </ul> + * + * <p class="note">This is a protected intent that can only be sent + * by the system. It is only sent to registered receivers. + * + * @see PackageManager#setDistractingPackageRestrictions(String[], int) + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DISTRACTING_PACKAGES_CHANGED = + "android.intent.action.DISTRACTING_PACKAGES_CHANGED"; + + /** * Broadcast Action: Sent to a package that has been suspended by the system. This is sent * whenever a package is put into a suspended state or any of its app extras change while in the * suspended state. @@ -5120,6 +5170,17 @@ public class Intent implements Parcelable, Cloneable { "android.intent.extra.changed_uid_list"; /** + * An integer denoting a bitwise combination of restrictions set on distracting packages via + * {@link PackageManager#setDistractingPackageRestrictions(String[], int)} + * + * @hide + * @see PackageManager.DistractionRestriction + * @see PackageManager#setDistractingPackageRestrictions(String[], int) + */ + public static final String EXTRA_DISTRACTION_RESTRICTIONS = + "android.intent.extra.distraction_restrictions"; + + /** * @hide * Magic extra system code can use when binding, to give a label for * who it is that has bound to a service. This is an integer giving diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl index ba7710b8ef48..db2b6fd235d3 100644 --- a/core/java/android/content/pm/ILauncherApps.aidl +++ b/core/java/android/content/pm/ILauncherApps.aidl @@ -69,6 +69,7 @@ interface ILauncherApps { int userId); boolean hasShortcutHostPermission(String callingPackage); + boolean shouldHideFromSuggestions(String packageName, in UserHandle user); ParceledListSlice getShortcutConfigActivities( String callingPackage, String packageName, in UserHandle user); diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 64a4479be7ee..d5c3b26b094d 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -273,6 +273,9 @@ interface IPackageManager { void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage); + String[] setDistractingPackageRestrictionsAsUser(in String[] packageNames, int restrictionFlags, + int userId); + String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended, in PersistableBundle appExtras, in PersistableBundle launcherExtras, in SuspendDialogInfo dialogInfo, String callingPackage, int userId); diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 44e652f10094..983ea9f847cd 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -697,6 +697,26 @@ public class LauncherApps { } /** + * Returns whether a package should be hidden from suggestions to the user. Currently, this + * could be done because the package was marked as distracting to the user via + * {@code PackageManager.setDistractingPackageRestrictions(String[], int)}. + * + * @param packageName The package for which to check. + * @param user the {@link UserHandle} of the profile. + * @return + */ + public boolean shouldHideFromSuggestions(@NonNull String packageName, + @NonNull UserHandle user) { + Preconditions.checkNotNull(packageName, "packageName"); + Preconditions.checkNotNull(user, "user"); + try { + return mService.shouldHideFromSuggestions(packageName, user); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** * Returns {@link ApplicationInfo} about an application installed for a specific user profile. * * @param packageName The package name of the application diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 2aeb68da365b..b84567383bfb 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -5555,26 +5555,24 @@ public abstract class PackageManager { } /** - * @deprecated This function no longer does anything; it was an old - * approach to managing preferred activities, which has been superseded - * by (and conflicts with) the modern activity-based preferences. + * @deprecated This function no longer does anything. It is the platform's + * responsibility to assign preferred activities and this cannot be modified + * directly. To determine the activities resolved by the platform, use + * {@link #resolveActivity} or {@link #queryIntentActivities}. */ @Deprecated public abstract void addPackageToPreferred(String packageName); /** - * @deprecated This function no longer does anything; it was an old - * approach to managing preferred activities, which has been superseded - * by (and conflicts with) the modern activity-based preferences. + * @deprecated This function no longer does anything. It is the platform's + * responsibility to assign preferred activities and this cannot be modified + * directly. To determine the activities resolved by the platform, use + * {@link #resolveActivity} or {@link #queryIntentActivities}. */ @Deprecated public abstract void removePackageFromPreferred(String packageName); /** - * @deprecated This function no longer does anything; it was an old - * approach to managing preferred activities, which has been superseded - * by (and conflicts with) the modern activity-based preferences. - * * Retrieve the list of all currently configured preferred packages. The * first package on the list is the most preferred, the last is the least * preferred. @@ -5582,15 +5580,16 @@ public abstract class PackageManager { * @param flags Additional option flags to modify the data returned. * @return A List of PackageInfo objects, one for each preferred * application, in order of preference. + * + * @deprecated This function no longer does anything. It is the platform's + * responsibility to assign preferred activities and this cannot be modified + * directly. To determine the activities resolved by the platform, use + * {@link #resolveActivity} or {@link #queryIntentActivities}. */ @Deprecated public abstract List<PackageInfo> getPreferredPackages(@PackageInfoFlags int flags); /** - * @deprecated This is a protected API that should not have been available - * to third party applications. It is the platform's responsibility for - * assigning preferred activities and this cannot be directly modified. - * * Add a new preferred activity mapping to the system. This will be used * to automatically select the given activity component when * {@link Context#startActivity(Intent) Context.startActivity()} finds @@ -5604,20 +5603,26 @@ public abstract class PackageManager { * this preference was made. * @param activity The component name of the activity that is to be * preferred. + * + * @deprecated This function no longer does anything. It is the platform's + * responsibility to assign preferred activities and this cannot be modified + * directly. To determine the activities resolved by the platform, use + * {@link #resolveActivity} or {@link #queryIntentActivities}. */ @Deprecated public abstract void addPreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity); /** - * @deprecated This is a protected API that should not have been available - * to third party applications. It is the platform's responsibility for - * assigning preferred activities and this cannot be directly modified. - * * Same as {@link #addPreferredActivity(IntentFilter, int, ComponentName[], ComponentName)}, but with a specific userId to apply the preference to. * @hide + * + * @deprecated This function no longer does anything. It is the platform's + * responsibility to assign preferred activities and this cannot be modified + * directly. To determine the activities resolved by the platform, use + * {@link #resolveActivity} or {@link #queryIntentActivities}. */ @Deprecated @UnsupportedAppUsage @@ -5627,10 +5632,6 @@ public abstract class PackageManager { } /** - * @deprecated This is a protected API that should not have been available - * to third party applications. It is the platform's responsibility for - * assigning preferred activities and this cannot be directly modified. - * * Replaces an existing preferred activity mapping to the system, and if that were not present * adds a new preferred activity. This will be used * to automatically select the given activity component when @@ -5645,7 +5646,13 @@ public abstract class PackageManager { * this preference was made. * @param activity The component name of the activity that is to be * preferred. + * * @hide + * + * @deprecated This function no longer does anything. It is the platform's + * responsibility to assign preferred activities and this cannot be modified + * directly. To determine the activities resolved by the platform, use + * {@link #resolveActivity} or {@link #queryIntentActivities}. */ @Deprecated @UnsupportedAppUsage @@ -5653,10 +5660,6 @@ public abstract class PackageManager { ComponentName[] set, ComponentName activity); /** - * @deprecated This is a protected API that should not have been available - * to third party applications. It is the platform's responsibility for - * assigning preferred activities and this cannot be directly modified. - * * Replaces an existing preferred activity mapping to the system, and if that were not present * adds a new preferred activity. This will be used to automatically select the given activity * component when {@link Context#startActivity(Intent) Context.startActivity()} finds multiple @@ -5671,6 +5674,11 @@ public abstract class PackageManager { * @param activity The component name of the activity that is to be preferred. * * @hide + * + * @deprecated This function no longer does anything. It is the platform's + * responsibility to assign preferred activities and this cannot be modified + * directly. To determine the activities resolved by the platform, use + * {@link #resolveActivity} or {@link #queryIntentActivities}. */ @Deprecated @SystemApi @@ -5681,6 +5689,11 @@ public abstract class PackageManager { /** * @hide + * + * @deprecated This function no longer does anything. It is the platform's + * responsibility to assign preferred activities and this cannot be modified + * directly. To determine the activities resolved by the platform, use + * {@link #resolveActivity} or {@link #queryIntentActivities}. */ @Deprecated @UnsupportedAppUsage @@ -5690,10 +5703,6 @@ public abstract class PackageManager { } /** - * @deprecated This function no longer does anything; it was an old - * approach to managing preferred activities, which has been superseded - * by (and conflicts with) the modern activity-based preferences. - * * Remove all preferred activity mappings, previously added with * {@link #addPreferredActivity}, from the * system whose activities are implemented in the given package name. @@ -5701,15 +5710,16 @@ public abstract class PackageManager { * * @param packageName The name of the package whose preferred activity * mappings are to be removed. + * + * @deprecated This function no longer does anything. It is the platform's + * responsibility to assign preferred activities and this cannot be modified + * directly. To determine the activities resolved by the platform, use + * {@link #resolveActivity} or {@link #queryIntentActivities}. */ @Deprecated public abstract void clearPackagePreferredActivities(String packageName); /** - * @deprecated This function no longer does anything; it was an old - * approach to managing preferred activities, which has been superseded - * by (and conflicts with) the modern activity-based preferences. - * * Retrieve all preferred activities, previously added with * {@link #addPreferredActivity}, that are * currently registered with the system. @@ -5725,6 +5735,11 @@ public abstract class PackageManager { * @return Returns the total number of registered preferred activities * (the number of distinct IntentFilter records, not the number of unique * activity components) that were found. + * + * @deprecated This function no longer does anything. It is the platform's + * responsibility to assign preferred activities and this cannot be modified + * directly. To determine the activities resolved by the platform, use + * {@link #resolveActivity} or {@link #queryIntentActivities}. */ @Deprecated public abstract int getPreferredActivities(@NonNull List<IntentFilter> outFilters, @@ -5889,6 +5904,74 @@ public abstract class PackageManager { public abstract boolean isSignedByExactly(String packageName, KeySet ks); /** + * Flag to denote no restrictions. This should be used to clear any restrictions that may have + * been previously set for the package. + * @see PackageManager.DistractionRestriction + * @hide + */ + @SystemApi + public static final int RESTRICTION_NONE = 0x0; + + /** + * Flag to denote that a package should be hidden from any suggestions to the user. + * @see PackageManager.DistractionRestriction + * @hide + */ + @SystemApi + public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 0x00000001; + + /** + * Flag to denote that a package's notifications should be hidden. + * @see PackageManager.DistractionRestriction + * @hide + */ + @SystemApi + public static final int RESTRICTION_HIDE_NOTIFICATIONS = 0x00000002; + + /** + * Restriction flags to set on a package that is considered as distracting to the user. + * These should help the user to restrict their usage of these apps. + * + * @see #setDistractingPackageRestrictions(String[], int) + * @hide + */ + @SystemApi + @IntDef(flag = true, prefix = {"RESTRICTION_"}, value = { + RESTRICTION_NONE, + RESTRICTION_HIDE_FROM_SUGGESTIONS, + RESTRICTION_HIDE_NOTIFICATIONS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DistractionRestriction {} + + /** + * Mark or unmark the given packages as distracting to the user. + * These packages can have certain restrictions set that should discourage the user to launch + * them often. For example, notifications from such an app can be hidden, or the app can be + * removed from launcher suggestions, so the user is able to restrict their use of these apps. + * + * <p>The caller must hold {@link android.Manifest.permission#SUSPEND_APPS} to use this API. + * + * @param packages Packages to mark as distracting. + * @param restrictionFlags Any combination of {@link DistractionRestriction restrictions} to + * impose on the given packages. {@link #RESTRICTION_NONE} can be used + * to clear any existing restrictions. + * @return A list of packages that could not have the {@code restrictionFlags} set. The system + * may prevent restricting critical packages to preserve normal device function. + * + * @see DistractionRestriction + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) + @NonNull + public String[] setDistractingPackageRestrictions(@NonNull String[] packages, + @DistractionRestriction int restrictionFlags) { + throw new UnsupportedOperationException( + "setDistractingPackageRestrictions not implemented"); + } + + /** * Puts the package in a suspended state, where attempts at starting activities are denied. * * <p>It doesn't remove the data or the actual package file. The application's notifications @@ -5911,8 +5994,7 @@ public abstract class PackageManager { * {@link PersistableBundle} objects to be shared with the apps being suspended and the * launcher to support customization that they might need to handle the suspended state. * - * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} or - * {@link Manifest.permission#MANAGE_USERS} to use this api.</p> + * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this API. * * @param packageNames The names of the packages to set the suspended status. * @param suspended If set to {@code true}, the packages will be suspended, if set to @@ -5955,7 +6037,7 @@ public abstract class PackageManager { * <p>When the user tries to launch a suspended app, a system dialog alerting them that the app * is suspended will be shown instead. * The caller can optionally customize the dialog by passing a {@link SuspendDialogInfo} object - * to this api. This dialog will have a button that starts the + * to this API. This dialog will have a button that starts the * {@link Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS} intent if the suspending app declares an * activity which handles this action. * @@ -5966,7 +6048,7 @@ public abstract class PackageManager { * {@link PersistableBundle} objects to be shared with the apps being suspended and the * launcher to support customization that they might need to handle the suspended state. * - * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this api. + * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this API. * * @param packageNames The names of the packages to set the suspended status. * @param suspended If set to {@code true}, the packages will be suspended, if set to @@ -6005,7 +6087,7 @@ public abstract class PackageManager { * #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, * SuspendDialogInfo) setPackagesSuspended}. The platform prevents suspending certain critical * packages to keep the device in a functioning state, e.g. the default dialer. - * Apps need to hold {@link Manifest.permission#SUSPEND_APPS SUSPEND_APPS} to call this api. + * Apps need to hold {@link Manifest.permission#SUSPEND_APPS SUSPEND_APPS} to call this API. * * <p> * Note that this set of critical packages can change with time, so even though a package name diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 83979e925be1..83563d0fd9f6 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -284,6 +284,17 @@ public abstract class PackageManagerInternal { public abstract SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, int userId); /** + * Gets any distraction flags set via + * {@link PackageManager#setDistractingPackageRestrictions(String[], int)} + * + * @param packageName + * @param userId + * @return A bitwise OR of any of the {@link PackageManager.DistractionRestriction} + */ + public abstract @PackageManager.DistractionRestriction int getDistractingPackageRestrictions( + String packageName, int userId); + + /** * Do a straight uid lookup for the given package/application in the given user. * @see PackageManager#getPackageUidAsUser(String, int, int) * @return The app's uid, or < 0 if the package was not found in that user diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index 74dd08fc1d6b..249b6919fc28 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -54,6 +54,7 @@ public class PackageUserState { public boolean stopped; public boolean notLaunched; public boolean hidden; // Is the app restricted by owner / admin + public int distractionFlags; public boolean suspended; public String suspendingPackage; public SuspendDialogInfo dialogInfo; @@ -92,6 +93,7 @@ public class PackageUserState { stopped = o.stopped; notLaunched = o.notLaunched; hidden = o.hidden; + distractionFlags = o.distractionFlags; suspended = o.suspended; suspendingPackage = o.suspendingPackage; dialogInfo = o.dialogInfo; @@ -222,6 +224,9 @@ public class PackageUserState { if (hidden != oldState.hidden) { return false; } + if (distractionFlags != oldState.distractionFlags) { + return false; + } if (suspended != oldState.suspended) { return false; } diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index bb8c92dba71a..a3395ac9fd13 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -158,6 +158,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { * @hide */ @SystemApi + @TestApi public static final int PROTECTION_FLAG_OEM = 0x4000; /** diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index f1a4db25cd14..9e0a9ba0dc7b 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -732,6 +732,38 @@ public final class AssetManager implements AutoCloseable { } } + /** + * Enable resource resolution logging to track the steps taken to resolve the last resource + * entry retrieved. Stores the configuration and package names for each step. + * + * Default disabled. + * + * @param enabled Boolean indicating whether to enable or disable logging. + * + * @hide + */ + public void setResourceResolutionLoggingEnabled(boolean enabled) { + synchronized (this) { + ensureValidLocked(); + nativeSetResourceResolutionLoggingEnabled(mObject, enabled); + } + } + + /** + * Retrieve the last resource resolution path logged. + * + * @return Formatted string containing last resource ID/name and steps taken to resolve final + * entry, including configuration and package names. + * + * @hide + */ + public @Nullable String getLastResourceResolution() { + synchronized (this) { + ensureValidLocked(); + return nativeGetLastResourceResolution(mObject); + } + } + CharSequence getPooledStringForCookie(int cookie, int id) { // Cookies map to ApkAssets starting at 1. return getApkAssets()[cookie - 1].getStringFromPool(id); @@ -1383,6 +1415,8 @@ public final class AssetManager implements AutoCloseable { private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid); private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem); private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr); + private static native void nativeSetResourceResolutionLoggingEnabled(long ptr, boolean enabled); + private static native @Nullable String nativeGetLastResourceResolution(long ptr); // Style attribute retrieval native methods. private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr, diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 365ceac68ee1..c4b315ec90c8 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -2010,22 +2010,36 @@ public class Resources { public String getResourceTypeName(@AnyRes int resid) throws NotFoundException { return mResourcesImpl.getResourceTypeName(resid); } - + /** * Return the entry name for a given resource identifier. - * + * * @param resid The resource identifier whose entry name is to be * retrieved. - * + * * @return A string holding the entry name of the resource. - * + * * @throws NotFoundException Throws NotFoundException if the given ID does not exist. - * + * * @see #getResourceName */ public String getResourceEntryName(@AnyRes int resid) throws NotFoundException { return mResourcesImpl.getResourceEntryName(resid); } + + /** + * Return formatted log of the last retrieved resource's resolution path. + * + * @return A string holding a formatted log of the steps taken to resolve the last resource. + * + * @throws NotFoundException Throws NotFoundException if there hasn't been a resource + * resolved yet. + * + * @hide + */ + public String getLastResourceResolution() throws NotFoundException { + return mResourcesImpl.getLastResourceResolution(); + } /** * Parse a series of {@link android.R.styleable#Extra <extra>} tags from diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 2ad4f625ef8c..77796d9ebdf5 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -299,6 +299,13 @@ public class ResourcesImpl { } @NonNull + String getLastResourceResolution() throws NotFoundException { + String str = mAssets.getLastResourceResolution(); + if (str != null) return str; + throw new NotFoundException("Associated AssetManager hasn't resolved a resource"); + } + + @NonNull CharSequence getQuantityText(@PluralsRes int id, int quantity) throws NotFoundException { PluralRules rule = getPluralRule(); CharSequence res = mAssets.getResourceBagText(id, diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 4a466f300718..617125b3f904 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -66,6 +66,7 @@ public final class LinkProperties implements Parcelable { private int mMtu; // in the format "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max" private String mTcpBufferSizes; + private IpPrefix mNat64Prefix; private static final int MIN_MTU = 68; private static final int MIN_MTU_V6 = 1280; @@ -760,6 +761,32 @@ public final class LinkProperties implements Parcelable { } /** + * Returns the NAT64 prefix in use on this link, if any. + * + * @return the NAT64 prefix. + * @hide + */ + public @Nullable IpPrefix getNat64Prefix() { + return mNat64Prefix; + } + + /** + * Sets the NAT64 prefix in use on this link. + * + * Currently, only 96-bit prefixes (i.e., where the 32-bit IPv4 address is at the end of the + * 128-bit IPv6 address) are supported. + * + * @param prefix the NAT64 prefix. + * @hide + */ + public void setNat64Prefix(IpPrefix prefix) { + if (prefix != null && prefix.getPrefixLength() != 96) { + throw new IllegalArgumentException("Only 96-bit prefixes are supported: " + prefix); + } + mNat64Prefix = prefix; // IpPrefix objects are immutable. + } + + /** * Adds a stacked link. * * If there is already a stacked link with the same interface name as link, @@ -831,6 +858,7 @@ public final class LinkProperties implements Parcelable { mStackedLinks.clear(); mMtu = 0; mTcpBufferSizes = null; + mNat64Prefix = null; } /** @@ -908,6 +936,11 @@ public final class LinkProperties implements Parcelable { resultJoiner.add(mHttpProxy.toString()); } + if (mNat64Prefix != null) { + resultJoiner.add("Nat64Prefix:"); + resultJoiner.add(mNat64Prefix.toString()); + } + final Collection<LinkProperties> stackedLinksValues = mStackedLinks.values(); if (!stackedLinksValues.isEmpty()) { final StringJoiner stackedLinksJoiner = new StringJoiner(",", "Stacked: [", "]"); @@ -1295,6 +1328,17 @@ public final class LinkProperties implements Parcelable { } /** + * Compares this {@code LinkProperties} NAT64 prefix against the target. + * + * @param target LinkProperties to compare. + * @return {@code true} if both are identical, {@code false} otherwise. + * @hide + */ + public boolean isIdenticalNat64Prefix(LinkProperties target) { + return Objects.equals(mNat64Prefix, target.mNat64Prefix); + } + + /** * Compares this {@code LinkProperties} instance against the target * LinkProperties in {@code obj}. Two LinkPropertieses are equal if * all their fields are equal in values. @@ -1330,7 +1374,8 @@ public final class LinkProperties implements Parcelable { && isIdenticalHttpProxy(target) && isIdenticalStackedLinks(target) && isIdenticalMtu(target) - && isIdenticalTcpBufferSizes(target); + && isIdenticalTcpBufferSizes(target) + && isIdenticalNat64Prefix(target); } /** @@ -1443,7 +1488,8 @@ public final class LinkProperties implements Parcelable { + ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode()) + (mUsePrivateDns ? 57 : 0) + mPcscfs.size() * 67 - + ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode()); + + ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode()) + + Objects.hash(mNat64Prefix); } /** @@ -1484,6 +1530,8 @@ public final class LinkProperties implements Parcelable { } else { dest.writeByte((byte)0); } + dest.writeParcelable(mNat64Prefix, 0); + ArrayList<LinkProperties> stackedLinks = new ArrayList<>(mStackedLinks.values()); dest.writeList(stackedLinks); } @@ -1535,6 +1583,7 @@ public final class LinkProperties implements Parcelable { if (in.readByte() == 1) { netProp.setHttpProxy(in.readParcelable(null)); } + netProp.setNat64Prefix(in.readParcelable(null)); ArrayList<LinkProperties> stackedLinks = new ArrayList<LinkProperties>(); in.readList(stackedLinks, LinkProperties.class.getClassLoader()); for (LinkProperties stackedLink: stackedLinks) { diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java index 1343d24d0d94..b15a4d3170b3 100644 --- a/core/java/android/os/BugreportManager.java +++ b/core/java/android/os/BugreportManager.java @@ -114,7 +114,6 @@ public class BugreportManager { } } - // TODO(b/111441001) Connect up with BugreportListener methods. private final class DumpstateListener extends IDumpstateListener.Stub implements DeathRecipient { @@ -130,6 +129,23 @@ public class BugreportManager { } @Override + public void onProgress(int progress) throws RemoteException { + // TODO(b/111441001): implement + } + + @Override + public void onError(int errorCode) throws RemoteException { + // TODO(b/111441001): implement + } + + @Override + public void onFinished(long durationMs, String title, String description) + throws RemoteException { + // TODO(b/111441001): implement + } + + // Old methods; should go away + @Override public void onProgressUpdated(int progress) throws RemoteException { // TODO(b/111441001): implement } diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java index 3de3494e7ea7..9e3e83e14a48 100644 --- a/core/java/android/os/HwBinder.java +++ b/core/java/android/os/HwBinder.java @@ -17,6 +17,7 @@ package android.os; import android.annotation.SystemApi; +import android.annotation.TestApi; import libcore.util.NativeAllocationRegistry; @@ -24,6 +25,7 @@ import java.util.NoSuchElementException; /** @hide */ @SystemApi +@TestApi public abstract class HwBinder implements IHwBinder { private static final String TAG = "HwBinder"; diff --git a/core/java/android/os/HwBlob.java b/core/java/android/os/HwBlob.java index 6a5bb1c0a988..0ec63b5815f1 100644 --- a/core/java/android/os/HwBlob.java +++ b/core/java/android/os/HwBlob.java @@ -18,6 +18,7 @@ package android.os; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.annotation.TestApi; import libcore.util.NativeAllocationRegistry; @@ -28,6 +29,7 @@ import libcore.util.NativeAllocationRegistry; * @hide */ @SystemApi +@TestApi public class HwBlob { private static final String TAG = "HwBlob"; diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java index 7a51db2dc5f9..7919a00166bf 100644 --- a/core/java/android/os/HwParcel.java +++ b/core/java/android/os/HwParcel.java @@ -18,6 +18,7 @@ package android.os; import android.annotation.IntDef; import android.annotation.SystemApi; +import android.annotation.TestApi; import libcore.util.NativeAllocationRegistry; @@ -28,6 +29,7 @@ import java.util.Arrays; /** @hide */ @SystemApi +@TestApi public class HwParcel { private static final String TAG = "HwParcel"; diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java index 249eb3aa3456..46fa6ef3b783 100644 --- a/core/java/android/os/IHwBinder.java +++ b/core/java/android/os/IHwBinder.java @@ -17,9 +17,11 @@ package android.os; import android.annotation.SystemApi; +import android.annotation.TestApi; /** @hide */ @SystemApi +@TestApi public interface IHwBinder { /** * Process a hwbinder transaction. diff --git a/core/java/android/os/IHwInterface.java b/core/java/android/os/IHwInterface.java index f9edd5bf8883..0a5a71550b06 100644 --- a/core/java/android/os/IHwInterface.java +++ b/core/java/android/os/IHwInterface.java @@ -17,8 +17,11 @@ package android.os; import android.annotation.SystemApi; +import android.annotation.TestApi; + /** @hide */ @SystemApi +@TestApi public interface IHwInterface { /** * @return the binder object that corresponds to this interface. diff --git a/core/java/android/os/NativeHandle.java b/core/java/android/os/NativeHandle.java index f7ffc37f085f..f13bf5fccd38 100644 --- a/core/java/android/os/NativeHandle.java +++ b/core/java/android/os/NativeHandle.java @@ -20,6 +20,7 @@ import static android.system.OsConstants.F_DUPFD_CLOEXEC; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.system.ErrnoException; import android.system.Os; @@ -32,6 +33,7 @@ import java.io.FileDescriptor; * @hide */ @SystemApi +@TestApi public final class NativeHandle implements Closeable { // whether this object owns mFds private boolean mOwn = false; diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index ee56e3d0ad16..760fef7566c7 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -208,30 +208,35 @@ public class Process { * First uid used for fully isolated sandboxed processes spawned from an app zygote * @hide */ + @TestApi public static final int FIRST_APP_ZYGOTE_ISOLATED_UID = 90000; /** * Number of UIDs we allocate per application zygote * @hide */ + @TestApi public static final int NUM_UIDS_PER_APP_ZYGOTE = 100; /** * Last uid used for fully isolated sandboxed processes spawned from an app zygote * @hide */ + @TestApi public static final int LAST_APP_ZYGOTE_ISOLATED_UID = 98999; /** * First uid used for fully isolated sandboxed processes (with no permissions of their own) * @hide */ + @TestApi public static final int FIRST_ISOLATED_UID = 99000; /** * Last uid used for fully isolated sandboxed processes (with no permissions of their own) * @hide */ + @TestApi public static final int LAST_ISOLATED_UID = 99999; /** diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl index 7a7bd83089f7..249b622dac4d 100644 --- a/core/java/android/permission/IPermissionController.aidl +++ b/core/java/android/permission/IPermissionController.aidl @@ -18,6 +18,8 @@ package android.permission; import android.os.RemoteCallback; import android.os.Bundle; +import android.os.ParcelFileDescriptor; +import android.os.UserHandle; /** * Interface for system apps to communication with the permission controller. @@ -27,6 +29,7 @@ import android.os.Bundle; oneway interface IPermissionController { void revokeRuntimePermissions(in Bundle request, boolean doDryRun, int reason, String callerPackageName, in RemoteCallback callback); + void getRuntimePermissionBackup(in UserHandle user, in ParcelFileDescriptor pipe); void getAppPermissions(String packageName, in RemoteCallback callback); void revokeRuntimePermission(String packageName, String permissionName); void countPermissionApps(in List<String> permissionNames, boolean countOnlyGranted, diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java index 0865b623028c..bfcca7c10151 100644 --- a/core/java/android/permission/PermissionControllerManager.java +++ b/core/java/android/permission/PermissionControllerManager.java @@ -36,10 +36,12 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.os.AsyncTask; import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.ParcelFileDescriptor; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.UserHandle; @@ -51,6 +53,11 @@ import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService; import com.android.internal.infra.AbstractRemoteService; import com.android.internal.util.Preconditions; +import libcore.io.IoUtils; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -58,6 +65,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.Executor; +import java.util.function.Consumer; /** * Interface for communicating with the permission controller. @@ -118,6 +126,20 @@ public final class PermissionControllerManager { } /** + * Callback for delivering the result of {@link #getRuntimePermissionBackup}. + * + * @hide + */ + public interface OnGetRuntimePermissionBackupCallback { + /** + * The result for {@link #getRuntimePermissionBackup}. + * + * @param backup The backup file + */ + void onGetRuntimePermissionsBackup(@NonNull byte[] backup); + } + + /** * Callback for delivering the result of {@link #getAppPermissions}. * * @hide @@ -219,6 +241,26 @@ public final class PermissionControllerManager { } /** + * Create a backup of the runtime permissions. + * + * @param user The user to be backed up + * @param executor Executor on which to invoke the callback + * @param callback Callback to receive the result + * + * @hide + */ + @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS) + public void getRuntimePermissionBackup(@NonNull UserHandle user, + @NonNull @CallbackExecutor Executor executor, + @NonNull OnGetRuntimePermissionBackupCallback callback) { + checkNotNull(executor); + checkNotNull(callback); + + sRemoteService.scheduleRequest(new PendingGetRuntimePermissionBackup(sRemoteService, + user, executor, callback)); + } + + /** * Gets the runtime permissions for an app. * * @param packageName The package for which to query. @@ -355,6 +397,89 @@ public final class PermissionControllerManager { } /** + * Task to read a large amount of data from a remote service. + */ + private static class FileReaderTask<Callback extends Consumer<byte[]>> + extends AsyncTask<Void, Void, byte[]> { + private ParcelFileDescriptor mLocalPipe; + private ParcelFileDescriptor mRemotePipe; + + private final @NonNull Callback mCallback; + + FileReaderTask(@NonNull Callback callback) { + mCallback = callback; + } + + @Override + protected void onPreExecute() { + ParcelFileDescriptor[] pipe; + try { + pipe = ParcelFileDescriptor.createPipe(); + } catch (IOException e) { + Log.e(TAG, "Could not create pipe needed to get runtime permission backup", e); + return; + } + + mLocalPipe = pipe[0]; + mRemotePipe = pipe[1]; + } + + /** + * Get the file descriptor the remote service should write the data to. + * + * <p>Needs to be closed <u>locally</u> before the FileReader can finish. + * + * @return The file the data should be written to + */ + ParcelFileDescriptor getRemotePipe() { + return mRemotePipe; + } + + @Override + protected byte[] doInBackground(Void... ignored) { + ByteArrayOutputStream combinedBuffer = new ByteArrayOutputStream(); + + try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(mLocalPipe)) { + byte[] buffer = new byte[16 * 1024]; + + while (!isCancelled()) { + int numRead = in.read(buffer); + if (numRead == -1) { + break; + } + + combinedBuffer.write(buffer, 0, numRead); + } + } catch (IOException | NullPointerException e) { + Log.e(TAG, "Error reading runtime permission backup", e); + combinedBuffer.reset(); + } + + return combinedBuffer.toByteArray(); + } + + /** + * Interrupt the reading of the data. + * + * <p>Needs to be called when canceling this task as it might be hung. + */ + void interruptRead() { + IoUtils.closeQuietly(mLocalPipe); + } + + @Override + protected void onCancelled() { + onPostExecute(new byte[]{}); + } + + @Override + protected void onPostExecute(byte[] backup) { + IoUtils.closeQuietly(mLocalPipe); + mCallback.accept(backup); + } + } + + /** * Request for {@link #revokeRuntimePermissions} */ private static final class PendingRevokeRuntimePermissionRequest extends @@ -441,6 +566,68 @@ public final class PermissionControllerManager { } /** + * Request for {@link #getRuntimePermissionBackup} + */ + private static final class PendingGetRuntimePermissionBackup extends + AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> + implements Consumer<byte[]> { + private final @NonNull FileReaderTask<PendingGetRuntimePermissionBackup> mBackupReader; + private final @NonNull Executor mExecutor; + private final @NonNull OnGetRuntimePermissionBackupCallback mCallback; + private final @NonNull UserHandle mUser; + + private PendingGetRuntimePermissionBackup(@NonNull RemoteService service, + @NonNull UserHandle user, @NonNull @CallbackExecutor Executor executor, + @NonNull OnGetRuntimePermissionBackupCallback callback) { + super(service); + + mUser = user; + mExecutor = executor; + mCallback = callback; + + mBackupReader = new FileReaderTask<>(this); + } + + @Override + protected void onTimeout(RemoteService remoteService) { + mBackupReader.cancel(true); + mBackupReader.interruptRead(); + } + + @Override + public void run() { + mBackupReader.execute(); + + ParcelFileDescriptor remotePipe = mBackupReader.getRemotePipe(); + try { + getService().getServiceInterface().getRuntimePermissionBackup(mUser, remotePipe); + } catch (RemoteException e) { + Log.e(TAG, "Error getting runtime permission backup", e); + } finally { + // Remote pipe end is duped by binder call. Local copy is not needed anymore + IoUtils.closeQuietly(remotePipe); + } + } + + /** + * Called when the {@link #mBackupReader} finished reading the file. + * + * @param backup The data read + */ + @Override + public void accept(byte[] backup) { + long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onGetRuntimePermissionsBackup(backup)); + } finally { + Binder.restoreCallingIdentity(token); + } + + finish(); + } + } + + /** * Request for {@link #getAppPermissions} */ private static final class PendingGetAppPermissionRequest extends diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java index 75d61e6fe570..10e8c8d5b8dd 100644 --- a/core/java/android/permission/PermissionControllerService.java +++ b/core/java/android/permission/PermissionControllerService.java @@ -33,11 +33,16 @@ import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.ParcelFileDescriptor; import android.os.RemoteCallback; +import android.os.UserHandle; import android.util.ArrayMap; +import android.util.Log; import com.android.internal.util.Preconditions; +import java.io.IOException; +import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -51,6 +56,7 @@ import java.util.Map; */ @SystemApi public abstract class PermissionControllerService extends Service { + private static final String LOG_TAG = PermissionControllerService.class.getSimpleName(); /** * The {@link Intent} action that must be declared as handled by a service @@ -83,6 +89,15 @@ public abstract class PermissionControllerService extends Service { @PermissionControllerManager.Reason int reason, @NonNull String callerPackageName); /** + * Create a backup of the runtime permissions. + * + * @param user The user to back up + * @param out The stream to write the backup to + */ + public abstract void onGetRuntimePermissionsBackup(@NonNull UserHandle user, + @NonNull OutputStream out); + + /** * Gets the runtime permissions for an app. * * @param packageName The package for which to query. @@ -163,6 +178,18 @@ public abstract class PermissionControllerService extends Service { } @Override + public void getRuntimePermissionBackup(UserHandle user, ParcelFileDescriptor pipe) { + checkNotNull(user); + checkNotNull(pipe); + + enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null); + + mHandler.sendMessage(obtainMessage( + PermissionControllerService::getRuntimePermissionsBackup, + PermissionControllerService.this, user, pipe)); + } + + @Override public void getAppPermissions(String packageName, RemoteCallback callback) { checkNotNull(packageName, "packageName"); checkNotNull(callback, "callback"); @@ -237,6 +264,15 @@ public abstract class PermissionControllerService extends Service { callback.sendResult(result); } + private void getRuntimePermissionsBackup(@NonNull UserHandle user, + @NonNull ParcelFileDescriptor outFile) { + try (OutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(outFile)) { + onGetRuntimePermissionsBackup(user, out); + } catch (IOException e) { + Log.e(LOG_TAG, "Could not open pipe to write backup tp", e); + } + } + private void getAppPermissions(@NonNull String packageName, @NonNull RemoteCallback callback) { List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName); if (permissions != null && !permissions.isEmpty()) { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index ef117eaaecc6..84825484c36d 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7407,6 +7407,22 @@ public final class Settings { private static final Validator DOZE_WAKE_SCREEN_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR; /** + * Gesture that skips media. + * @hide + */ + public static final String SKIP_GESTURE = "skip_gesture"; + + private static final Validator SKIP_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR; + + /** + * Gesture that silences sound (alarms, notification, calls). + * @hide + */ + public static final String SILENCE_GESTURE = "silence_gesture"; + + private static final Validator SILENCE_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR; + + /** * The current night mode that has been selected by the user. Owned * and controlled by UiModeManagerService. Constants are as per * UiModeManager. @@ -8483,6 +8499,8 @@ public final class Settings { NOTIFICATION_NEW_INTERRUPTION_MODEL, TRUST_AGENTS_EXTEND_UNLOCK, LOCK_SCREEN_WHEN_TRUST_LOST, + SKIP_GESTURE, + SILENCE_GESTURE, }; /** @@ -8650,6 +8668,8 @@ public final class Settings { VALIDATORS.put(TRUST_AGENTS_EXTEND_UNLOCK, TRUST_AGENTS_EXTEND_UNLOCK_VALIDATOR); VALIDATORS.put(LOCK_SCREEN_CUSTOM_CLOCK_FACE, LOCK_SCREEN_CUSTOM_CLOCK_FACE_VALIDATOR); VALIDATORS.put(LOCK_SCREEN_WHEN_TRUST_LOST, LOCK_SCREEN_WHEN_TRUST_LOST_VALIDATOR); + VALIDATORS.put(SKIP_GESTURE, SKIP_GESTURE_VALIDATOR); + VALIDATORS.put(SILENCE_GESTURE, SILENCE_GESTURE_VALIDATOR); } /** @@ -8681,10 +8701,10 @@ public final class Settings { CLONE_TO_MANAGED_PROFILE.add(LOCATION_CHANGER); CLONE_TO_MANAGED_PROFILE.add(LOCATION_MODE); CLONE_TO_MANAGED_PROFILE.add(LOCATION_PROVIDERS_ALLOWED); - CLONE_TO_MANAGED_PROFILE.add(SELECTED_INPUT_METHOD_SUBTYPE); if (!InputMethodSystemProperty.PER_PROFILE_IME_ENABLED) { CLONE_TO_MANAGED_PROFILE.add(DEFAULT_INPUT_METHOD); CLONE_TO_MANAGED_PROFILE.add(ENABLED_INPUT_METHODS); + CLONE_TO_MANAGED_PROFILE.add(SELECTED_INPUT_METHOD_SUBTYPE); CLONE_TO_MANAGED_PROFILE.add(SELECTED_SPELL_CHECKER); CLONE_TO_MANAGED_PROFILE.add(SELECTED_SPELL_CHECKER_SUBTYPE); } diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java index eef7ea232d43..50e7ec30ec3a 100644 --- a/core/java/android/text/util/Linkify.java +++ b/core/java/android/text/util/Linkify.java @@ -37,7 +37,6 @@ import android.widget.TextView; import com.android.i18n.phonenumbers.PhoneNumberMatch; import com.android.i18n.phonenumbers.PhoneNumberUtil; import com.android.i18n.phonenumbers.PhoneNumberUtil.Leniency; -import com.android.internal.annotations.GuardedBy; import libcore.util.EmptyArray; @@ -49,6 +48,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Locale; +import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -69,7 +69,6 @@ import java.util.regex.Pattern; * * @see MatchFilter * @see TransformFilter - * @see UrlSpanFactory */ public class Linkify { @@ -228,44 +227,6 @@ public class Linkify { } /** - * Factory class to create {@link URLSpan}s. While adding spans to a {@link Spannable}, - * {@link Linkify} will call {@link UrlSpanFactory#create(String)} function to create a - * {@link URLSpan}. - * - * @see #addLinks(Spannable, int, UrlSpanFactory) - * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter, - * UrlSpanFactory) - */ - public static class UrlSpanFactory { - private static final Object sInstanceLock = new Object(); - - @GuardedBy("sInstanceLock") - private static volatile UrlSpanFactory sInstance = null; - - private static synchronized UrlSpanFactory getInstance() { - if (sInstance == null) { - synchronized (sInstanceLock) { - if (sInstance == null) { - sInstance = new UrlSpanFactory(); - } - } - } - return sInstance; - } - - /** - * Factory function that will called by {@link Linkify} in order to create a - * {@link URLSpan}. - * - * @param url URL found - * @return a URLSpan instance - */ - public URLSpan create(final String url) { - return new URLSpan(url); - } - } - - /** * Scans the text of the provided Spannable and turns all occurrences * of the link types indicated in the mask into clickable links. * If the mask is nonzero, it also removes any existing URLSpans @@ -277,7 +238,7 @@ public class Linkify { * * @return True if at least one link is found and applied. * - * @see #addLinks(Spannable, int, UrlSpanFactory) + * @see #addLinks(Spannable, int, Function) */ public static final boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask) { return addLinks(text, mask, null, null); @@ -292,11 +253,11 @@ public class Linkify { * * @param text Spannable whose text is to be marked-up with links * @param mask mask to define which kinds of links will be searched - * @param urlSpanFactory factory class used to create {@link URLSpan}s + * @param urlSpanFactory function used to create {@link URLSpan}s * @return True if at least one link is found and applied. */ public static final boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask, - @Nullable UrlSpanFactory urlSpanFactory) { + @Nullable Function<String, URLSpan> urlSpanFactory) { return addLinks(text, mask, null, urlSpanFactory); } @@ -309,11 +270,11 @@ public class Linkify { * @param text Spannable whose text is to be marked-up with links * @param mask mask to define which kinds of links will be searched * @param context Context to be used while identifying phone numbers - * @param urlSpanFactory factory class used to create {@link URLSpan}s + * @param urlSpanFactory function used to create {@link URLSpan}s * @return true if at least one link is found and applied. */ private static boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask, - @Nullable Context context, @Nullable UrlSpanFactory urlSpanFactory) { + @Nullable Context context, @Nullable Function<String, URLSpan> urlSpanFactory) { if (text != null && containsUnsupportedCharacters(text.toString())) { android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, ""); return false; @@ -398,7 +359,7 @@ public class Linkify { * * @return True if at least one link is found and applied. * - * @see #addLinks(Spannable, int, UrlSpanFactory) + * @see #addLinks(Spannable, int, Function) */ public static final boolean addLinks(@NonNull TextView text, @LinkifyMask int mask) { if (mask == 0) { @@ -512,8 +473,7 @@ public class Linkify { * @param pattern Regex pattern to be used for finding links * @param scheme URL scheme string (eg <code>http://</code>) to be * prepended to the links that do not start with this scheme. - * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter, - * UrlSpanFactory) + * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter, Function) */ public static final boolean addLinks(@NonNull Spannable text, @NonNull Pattern pattern, @Nullable String scheme) { @@ -534,8 +494,7 @@ public class Linkify { * @param transformFilter Filter to allow the client code to update the link found. * * @return True if at least one link is found and applied. - * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter, - * UrlSpanFactory) + * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter, Function) */ public static final boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern, @Nullable String scheme, @Nullable MatchFilter matchFilter, @@ -560,8 +519,7 @@ public class Linkify { * * @return True if at least one link is found and applied. * - * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter, - * UrlSpanFactory) + * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter, Function) */ public static final boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern, @Nullable String defaultScheme, @Nullable String[] schemes, @@ -584,14 +542,14 @@ public class Linkify { * @param matchFilter the filter that is used to allow the client code additional control * over which pattern matches are to be converted into links. * @param transformFilter filter to allow the client code to update the link found. - * @param urlSpanFactory factory class used to create {@link URLSpan}s + * @param urlSpanFactory function used to create {@link URLSpan}s * * @return True if at least one link is found and applied. */ public static final boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern, @Nullable String defaultScheme, @Nullable String[] schemes, @Nullable MatchFilter matchFilter, @Nullable TransformFilter transformFilter, - @Nullable UrlSpanFactory urlSpanFactory) { + @Nullable Function<String, URLSpan> urlSpanFactory) { if (spannable != null && containsUnsupportedCharacters(spannable.toString())) { android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, ""); return false; @@ -634,11 +592,11 @@ public class Linkify { } private static void applyLink(String url, int start, int end, Spannable text, - @Nullable UrlSpanFactory urlSpanFactory) { + @Nullable Function<String, URLSpan> urlSpanFactory) { if (urlSpanFactory == null) { - urlSpanFactory = UrlSpanFactory.getInstance(); + urlSpanFactory = DEFAULT_SPAN_FACTORY; } - final URLSpan span = urlSpanFactory.create(url); + final URLSpan span = urlSpanFactory.apply(url); text.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } @@ -805,6 +763,13 @@ public class Linkify { i++; } } + + /** + * Default factory function to create {@link URLSpan}s. While adding spans to a + * {@link Spannable}, {@link Linkify} will call this function to create a {@link URLSpan}. + */ + private static final Function<String, URLSpan> DEFAULT_SPAN_FACTORY = + (String string) -> new URLSpan(string); } class LinkSpec { diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java index cb1d89c54d9a..9c935af09cca 100644 --- a/core/java/android/view/autofill/AutofillId.java +++ b/core/java/android/view/autofill/AutofillId.java @@ -15,6 +15,7 @@ */ package android.view.autofill; +import android.annotation.NonNull; import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -25,33 +26,47 @@ import android.view.View; */ public final class AutofillId implements Parcelable { + /** @hide */ + public static final int NO_SESSION = 0; + + private static final int FLAG_IS_VIRTUAL = 0x1; + private static final int FLAG_HAS_SESSION = 0x2; + private final int mViewId; - private final boolean mVirtual; + private final int mFlags; private final int mVirtualId; + private final int mSessionId; /** @hide */ @TestApi public AutofillId(int id) { - mVirtual = false; - mViewId = id; - mVirtualId = View.NO_ID; + this(/* flags= */ 0, id, View.NO_ID, NO_SESSION); } /** @hide */ @TestApi - public AutofillId(AutofillId parent, int virtualChildId) { - mVirtual = true; - mViewId = parent.mViewId; - mVirtualId = virtualChildId; + public AutofillId(@NonNull AutofillId parent, int virtualChildId) { + this(FLAG_IS_VIRTUAL, parent.mViewId, virtualChildId, NO_SESSION); } /** @hide */ public AutofillId(int parentId, int virtualChildId) { - mVirtual = true; + this(FLAG_IS_VIRTUAL, parentId, virtualChildId, NO_SESSION); + } + + /** @hide */ + public AutofillId(@NonNull AutofillId parent, int virtualChildId, int sessionId) { + this(FLAG_IS_VIRTUAL | FLAG_HAS_SESSION, parent.mViewId, virtualChildId, sessionId); + } + + private AutofillId(int flags, int parentId, int virtualChildId, int sessionId) { + mFlags = flags; mViewId = parentId; mVirtualId = virtualChildId; + mSessionId = sessionId; } + /** @hide */ public int getViewId() { return mViewId; @@ -64,7 +79,16 @@ public final class AutofillId implements Parcelable { /** @hide */ public boolean isVirtual() { - return mVirtual; + return (mFlags & FLAG_IS_VIRTUAL) != 0; + } + + private boolean hasSession() { + return (mFlags & FLAG_HAS_SESSION) != 0; + } + + /** @hide */ + public int getSessionId() { + return mSessionId; } ///////////////////////////////// @@ -77,6 +101,7 @@ public final class AutofillId implements Parcelable { int result = 1; result = prime * result + mViewId; result = prime * result + mVirtualId; + result = prime * result + mSessionId; return result; } @@ -88,15 +113,19 @@ public final class AutofillId implements Parcelable { final AutofillId other = (AutofillId) obj; if (mViewId != other.mViewId) return false; if (mVirtualId != other.mVirtualId) return false; + if (mSessionId != other.mSessionId) return false; return true; } @Override public String toString() { final StringBuilder builder = new StringBuilder().append(mViewId); - if (mVirtual) { + if (isVirtual()) { builder.append(':').append(mVirtualId); } + if (hasSession()) { + builder.append('@').append(mSessionId); + } return builder.toString(); } @@ -108,21 +137,24 @@ public final class AutofillId implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(mViewId); - parcel.writeInt(mVirtual ? 1 : 0); - parcel.writeInt(mVirtualId); - } - - private AutofillId(Parcel parcel) { - mViewId = parcel.readInt(); - mVirtual = parcel.readInt() == 1; - mVirtualId = parcel.readInt(); + parcel.writeInt(mFlags); + if (isVirtual()) { + parcel.writeInt(mVirtualId); + } + if (hasSession()) { + parcel.writeInt(mSessionId); + } } public static final Parcelable.Creator<AutofillId> CREATOR = new Parcelable.Creator<AutofillId>() { @Override public AutofillId createFromParcel(Parcel source) { - return new AutofillId(source); + final int viewId = source.readInt(); + final int flags = source.readInt(); + final int virtualId = (flags & FLAG_IS_VIRTUAL) != 0 ? source.readInt() : View.NO_ID; + final int sessionId = (flags & FLAG_HAS_SESSION) != 0 ? source.readInt() : NO_SESSION; + return new AutofillId(flags, viewId, virtualId, sessionId); } @Override diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index 6890beaf4c16..d9a8416dcb4d 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -28,6 +28,7 @@ import android.view.autofill.AutofillId; import android.view.contentcapture.ViewNode.ViewStructureImpl; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import dalvik.system.CloseGuard; @@ -107,7 +108,7 @@ public abstract class ContentCaptureSession implements AutoCloseable { /** @hide */ @Nullable - protected final String mId = UUID.randomUUID().toString(); + protected final String mId; private int mState = STATE_UNKNOWN; @@ -123,6 +124,13 @@ public abstract class ContentCaptureSession implements AutoCloseable { /** @hide */ protected ContentCaptureSession() { + this(UUID.randomUUID().toString()); + } + + /** @hide */ + @VisibleForTesting + public ContentCaptureSession(@NonNull String id) { + mId = Preconditions.checkNotNull(id); mCloseGuard.open("destroy"); } @@ -140,6 +148,13 @@ public abstract class ContentCaptureSession implements AutoCloseable { return mContentCaptureSessionId; } + /** @hide */ + @VisibleForTesting + public int getIdAsInt() { + // TODO(b/121197119): use sessionId instead of hashcode once it's changed to int + return mId.hashCode(); + } + /** * Creates a new {@link ContentCaptureSession}. * @@ -315,9 +330,7 @@ public abstract class ContentCaptureSession implements AutoCloseable { public @NonNull AutofillId newAutofillId(@NonNull AutofillId parentId, int virtualChildId) { Preconditions.checkNotNull(parentId); Preconditions.checkArgument(!parentId.isVirtual(), "virtual ids cannot have children"); - // TODO(b/121197119): we need to add the session id to the AutofillId to make them unique - // per session - return new AutofillId(parentId, virtualChildId); + return new AutofillId(parentId, virtualChildId, getIdAsInt()); } /** @@ -333,8 +346,7 @@ public abstract class ContentCaptureSession implements AutoCloseable { @NonNull public final ViewStructure newVirtualViewStructure(@NonNull AutofillId parentId, int virtualId) { - // TODO(b/121197119): use the constructor that takes a session id / assert on unit test. - return new ViewNode.ViewStructureImpl(parentId, virtualId); + return new ViewNode.ViewStructureImpl(parentId, virtualId, getIdAsInt()); } boolean isContentCaptureEnabled() { diff --git a/core/java/android/view/contentcapture/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java index b7a486afc118..ddfecb0fae14 100644 --- a/core/java/android/view/contentcapture/ViewNode.java +++ b/core/java/android/view/contentcapture/ViewNode.java @@ -672,9 +672,9 @@ public final class ViewNode extends AssistStructure.ViewNode { } @VisibleForTesting // Must be public to be accessed from FrameworkCoreTests' apk. - public ViewStructureImpl(@NonNull AutofillId parentId, int virtualId) { + public ViewStructureImpl(@NonNull AutofillId parentId, int virtualId, int sessionId) { mNode.mParentAutofillId = Preconditions.checkNotNull(parentId); - mNode.mAutofillId = new AutofillId(parentId, virtualId); + mNode.mAutofillId = new AutofillId(parentId, virtualId, sessionId); } @VisibleForTesting // Must be public to be accessed from FrameworkCoreTests' apk. diff --git a/core/java/android/view/textclassifier/ConversationAction.java b/core/java/android/view/textclassifier/ConversationAction.java new file mode 100644 index 000000000000..1a6e5d8e8b03 --- /dev/null +++ b/core/java/android/view/textclassifier/ConversationAction.java @@ -0,0 +1,266 @@ +/* + * 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.textclassifier; + +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.FloatRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.StringDef; +import android.app.RemoteAction; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + +import java.lang.annotation.Retention; + +/** Represents the action suggested by a {@link TextClassifier} on a given conversation. */ +public final class ConversationAction implements Parcelable { + + /** @hide */ + @Retention(SOURCE) + @StringDef( + value = { + TYPE_VIEW_CALENDAR, + TYPE_VIEW_MAP, + TYPE_TRACK_FLIGHT, + TYPE_OPEN_URL, + TYPE_SEND_SMS, + TYPE_CALL_PHONE, + TYPE_SEND_EMAIL, + TYPE_TEXT_REPLY, + TYPE_CREATE_REMINDER, + TYPE_SHARE_LOCATION + }, + prefix = "TYPE_") + public @interface ActionType {} + + /** + * Indicates an action to view a calendar at a specified time. + */ + public static final String TYPE_VIEW_CALENDAR = "view_calendar"; + /** + * Indicates an action to view the map at a specified location. + */ + public static final String TYPE_VIEW_MAP = "view_map"; + /** + * Indicates an action to track a flight. + */ + public static final String TYPE_TRACK_FLIGHT = "track_flight"; + /** + * Indicates an action to open an URL. + */ + public static final String TYPE_OPEN_URL = "open_url"; + /** + * Indicates an action to send a SMS. + */ + public static final String TYPE_SEND_SMS = "send_sms"; + /** + * Indicates an action to call a phone number. + */ + public static final String TYPE_CALL_PHONE = "call_phone"; + /** + * Indicates an action to send an email. + */ + public static final String TYPE_SEND_EMAIL = "send_email"; + /** + * Indicates an action to reply with a text message. + */ + public static final String TYPE_TEXT_REPLY = "text_reply"; + /** + * Indicates an action to create a reminder. + */ + public static final String TYPE_CREATE_REMINDER = "create_reminder"; + /** + * Indicates an action to reply with a location. + */ + public static final String TYPE_SHARE_LOCATION = "share_location"; + + public static final Creator<ConversationAction> CREATOR = + new Creator<ConversationAction>() { + @Override + public ConversationAction createFromParcel(Parcel in) { + return new ConversationAction(in); + } + + @Override + public ConversationAction[] newArray(int size) { + return new ConversationAction[size]; + } + }; + + @NonNull + @ActionType + private final String mType; + @NonNull + private final CharSequence mTextReply; + @Nullable + private final RemoteAction mAction; + + @FloatRange(from = 0, to = 1) + private final float mScore; + + @NonNull + private final Bundle mExtras; + + private ConversationAction( + @NonNull String type, + @Nullable RemoteAction action, + @Nullable CharSequence textReply, + float score, + @NonNull Bundle extras) { + mType = Preconditions.checkNotNull(type); + mAction = action; + mTextReply = textReply; + mScore = score; + mExtras = Preconditions.checkNotNull(extras); + } + + private ConversationAction(Parcel in) { + mType = in.readString(); + mAction = in.readParcelable(null); + mTextReply = in.readCharSequence(); + mScore = in.readFloat(); + mExtras = in.readBundle(); + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeString(mType); + parcel.writeParcelable(mAction, flags); + parcel.writeCharSequence(mTextReply); + parcel.writeFloat(mScore); + parcel.writeBundle(mExtras); + } + + @Override + public int describeContents() { + return 0; + } + + /** Returns the type of this action, for example, {@link #TYPE_VIEW_CALENDAR}. */ + @NonNull + @ActionType + public String getType() { + return mType; + } + + /** + * Returns a RemoteAction object, which contains the icon, label and a PendingIntent, for + * the specified action type. + */ + @Nullable + public RemoteAction getAction() { + return mAction; + } + + /** + * Returns the confidence score for the specified action. The value ranges from 0 (low + * confidence) to 1 (high confidence). + */ + @FloatRange(from = 0, to = 1) + public float getConfidenceScore() { + return mScore; + } + + /** + * Returns the text reply that could be sent as a reply to the given conversation. + * <p> + * This is only available when the type of the action is {@link #TYPE_TEXT_REPLY}. + */ + @Nullable + public CharSequence getTextReply() { + return mTextReply; + } + + /** + * Returns the extended data related to this conversation action. + * + * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should + * prefer to hold a reference to the returned bundle rather than frequently calling this + * method. + */ + @NonNull + public Bundle getExtras() { + return mExtras.deepCopy(); + } + + /** Builder class to construct {@link ConversationAction}. */ + public static final class Builder { + @Nullable + @ActionType + private String mType; + @Nullable + private RemoteAction mAction; + @Nullable + private CharSequence mTextReply; + private float mScore; + @Nullable + private Bundle mExtras; + + public Builder(@NonNull @ActionType String actionType) { + mType = Preconditions.checkNotNull(actionType); + } + + /** + * Sets an action that may be performed on the given conversation. + */ + @NonNull + public Builder setAction(@Nullable RemoteAction action) { + mAction = action; + return this; + } + + /** + * Sets a text reply that may be performed on the given conversation. + */ + @NonNull + public Builder setTextReply(@Nullable CharSequence textReply) { + mTextReply = textReply; + return this; + } + + /** Sets the confident score. */ + @NonNull + public Builder setConfidenceScore(@FloatRange(from = 0, to = 1) float score) { + mScore = score; + return this; + } + + /** + * Sets the extended data for the conversation action object. + */ + @NonNull + public Builder setExtras(@Nullable Bundle extras) { + mExtras = extras; + return this; + } + + /** Builds the {@link ConversationAction} object. */ + @NonNull + public ConversationAction build() { + return new ConversationAction( + mType, + mAction, + mTextReply, + mScore, + mExtras == null ? Bundle.EMPTY : mExtras.deepCopy()); + } + } +} diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java index 3f690f70d275..f7c1a2640dc5 100644 --- a/core/java/android/view/textclassifier/ConversationActions.java +++ b/core/java/android/view/textclassifier/ConversationActions.java @@ -17,18 +17,15 @@ package android.view.textclassifier; import static java.lang.annotation.RetentionPolicy.SOURCE; -import android.annotation.FloatRange; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; import android.app.Person; -import android.app.RemoteAction; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.text.SpannedString; -import android.util.ArraySet; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; @@ -37,10 +34,8 @@ import java.lang.annotation.Retention; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Set; /** * Represents a list of actions suggested by a {@link TextClassifier} on a given conversation. @@ -62,83 +57,6 @@ public final class ConversationActions implements Parcelable { } }; - /** @hide */ - @Retention(SOURCE) - @StringDef( - value = { - TYPE_VIEW_CALENDAR, - TYPE_VIEW_MAP, - TYPE_TRACK_FLIGHT, - TYPE_OPEN_URL, - TYPE_SEND_SMS, - TYPE_CALL_PHONE, - TYPE_SEND_EMAIL, - TYPE_TEXT_REPLY, - TYPE_CREATE_REMINDER, - TYPE_SHARE_LOCATION - }, - prefix = "TYPE_") - public @interface ActionType {} - - /** - * Indicates an action to view a calendar at a specified time. - */ - public static final String TYPE_VIEW_CALENDAR = "view_calendar"; - /** - * Indicates an action to view the map at a specified location. - */ - public static final String TYPE_VIEW_MAP = "view_map"; - /** - * Indicates an action to track a flight. - */ - public static final String TYPE_TRACK_FLIGHT = "track_flight"; - /** - * Indicates an action to open an URL. - */ - public static final String TYPE_OPEN_URL = "open_url"; - /** - * Indicates an action to send a SMS. - */ - public static final String TYPE_SEND_SMS = "send_sms"; - /** - * Indicates an action to call a phone number. - */ - public static final String TYPE_CALL_PHONE = "call_phone"; - /** - * Indicates an action to send an email. - */ - public static final String TYPE_SEND_EMAIL = "send_email"; - /** - * Indicates an action to reply with a text message. - */ - public static final String TYPE_TEXT_REPLY = "text_reply"; - /** - * Indicates an action to create a reminder. - */ - public static final String TYPE_CREATE_REMINDER = "create_reminder"; - /** - * Indicates an action to reply with a location. - */ - public static final String TYPE_SHARE_LOCATION = "share_location"; - - /** @hide */ - @Retention(SOURCE) - @StringDef( - value = { - HINT_FOR_NOTIFICATION, - HINT_FOR_IN_APP, - }, - prefix = "HINT_") - public @interface Hint {} - /** - * To indicate the generated actions will be used within the app. - */ - public static final String HINT_FOR_IN_APP = "in_app"; - /** - * To indicate the generated actions will be used for notification. - */ - public static final String HINT_FOR_NOTIFICATION = "notification"; - private final List<ConversationAction> mConversationActions; private final String mId; @@ -184,182 +102,6 @@ public final class ConversationActions implements Parcelable { return mId; } - /** Represents the action suggested by a {@link TextClassifier} on a given conversation. */ - public static final class ConversationAction implements Parcelable { - - public static final Creator<ConversationAction> CREATOR = - new Creator<ConversationAction>() { - @Override - public ConversationAction createFromParcel(Parcel in) { - return new ConversationAction(in); - } - - @Override - public ConversationAction[] newArray(int size) { - return new ConversationAction[size]; - } - }; - - @NonNull - @ActionType - private final String mType; - @NonNull - private final CharSequence mTextReply; - @Nullable - private final RemoteAction mAction; - - @FloatRange(from = 0, to = 1) - private final float mScore; - - @NonNull - private final Bundle mExtras; - - private ConversationAction( - @NonNull String type, - @Nullable RemoteAction action, - @Nullable CharSequence textReply, - float score, - @NonNull Bundle extras) { - mType = Preconditions.checkNotNull(type); - mAction = action; - mTextReply = textReply; - mScore = score; - mExtras = Preconditions.checkNotNull(extras); - } - - private ConversationAction(Parcel in) { - mType = in.readString(); - mAction = in.readParcelable(null); - mTextReply = in.readCharSequence(); - mScore = in.readFloat(); - mExtras = in.readBundle(); - } - - @Override - public void writeToParcel(Parcel parcel, int flags) { - parcel.writeString(mType); - parcel.writeParcelable(mAction, flags); - parcel.writeCharSequence(mTextReply); - parcel.writeFloat(mScore); - parcel.writeBundle(mExtras); - } - - @Override - public int describeContents() { - return 0; - } - - @NonNull - @ActionType - /** Returns the type of this action, for example, {@link #TYPE_VIEW_CALENDAR}. */ - public String getType() { - return mType; - } - - @Nullable - /** - * Returns a RemoteAction object, which contains the icon, label and a PendingIntent, for - * the specified action type. - */ - public RemoteAction getAction() { - return mAction; - } - - /** - * Returns the confidence score for the specified action. The value ranges from 0 (low - * confidence) to 1 (high confidence). - */ - @FloatRange(from = 0, to = 1) - public float getConfidenceScore() { - return mScore; - } - - /** - * Returns the text reply that could be sent as a reply to the given conversation. - * <p> - * This is only available when the type of the action is {@link #TYPE_TEXT_REPLY}. - */ - @Nullable - public CharSequence getTextReply() { - return mTextReply; - } - - /** - * Returns the extended data related to this conversation action. - * - * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should - * prefer to hold a reference to the returned bundle rather than frequently calling this - * method. - */ - @NonNull - public Bundle getExtras() { - return mExtras.deepCopy(); - } - - /** Builder class to construct {@link ConversationAction}. */ - public static final class Builder { - @Nullable - @ActionType - private String mType; - @Nullable - private RemoteAction mAction; - @Nullable - private CharSequence mTextReply; - private float mScore; - @Nullable - private Bundle mExtras; - - public Builder(@NonNull @ActionType String actionType) { - mType = Preconditions.checkNotNull(actionType); - } - - /** - * Sets an action that may be performed on the given conversation. - */ - @NonNull - public Builder setAction(@Nullable RemoteAction action) { - mAction = action; - return this; - } - - /** - * Sets a text reply that may be performed on the given conversation. - */ - @NonNull - public Builder setTextReply(@Nullable CharSequence textReply) { - mTextReply = textReply; - return this; - } - - /** Sets the confident score. */ - @NonNull - public Builder setConfidenceScore(@FloatRange(from = 0, to = 1) float score) { - mScore = score; - return this; - } - - /** - * Sets the extended data for the conversation action object. - */ - @NonNull - public Builder setExtras(@Nullable Bundle extras) { - mExtras = extras; - return this; - } - - /** Builds the {@link ConversationAction} object. */ - @NonNull - public ConversationAction build() { - return new ConversationAction( - mType, - mAction, - mTextReply, - mScore, - mExtras == null ? Bundle.EMPTY : mExtras.deepCopy()); - } - } - } - /** Represents a message in the conversation. */ public static final class Message implements Parcelable { /** @@ -538,156 +280,36 @@ public final class ConversationActions implements Parcelable { } } - /** Configuration object for specifying what action types to identify. */ - public static final class TypeConfig implements Parcelable { - @NonNull - @ActionType - private final Set<String> mExcludedTypes; - @NonNull - @ActionType - private final Set<String> mIncludedTypes; - private final boolean mIncludeTypesFromTextClassifier; - - private TypeConfig( - @NonNull Set<String> includedTypes, - @NonNull Set<String> excludedTypes, - boolean includeTypesFromTextClassifier) { - mIncludedTypes = Preconditions.checkNotNull(includedTypes); - mExcludedTypes = Preconditions.checkNotNull(excludedTypes); - mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier; - } - - private TypeConfig(Parcel in) { - mIncludedTypes = new ArraySet<>(in.createStringArrayList()); - mExcludedTypes = new ArraySet<>(in.createStringArrayList()); - mIncludeTypesFromTextClassifier = in.readByte() != 0; - } - - @Override - public void writeToParcel(Parcel parcel, int flags) { - parcel.writeStringList(new ArrayList<>(mIncludedTypes)); - parcel.writeStringList(new ArrayList<>(mExcludedTypes)); - parcel.writeByte((byte) (mIncludeTypesFromTextClassifier ? 1 : 0)); - } - - @Override - public int describeContents() { - return 0; - } - - public static final Creator<TypeConfig> CREATOR = - new Creator<TypeConfig>() { - @Override - public TypeConfig createFromParcel(Parcel in) { - return new TypeConfig(in); - } + /** + * A request object for generating conversation action suggestions. + * + * @see TextClassifier#suggestConversationActions(Request) + */ + public static final class Request implements Parcelable { - @Override - public TypeConfig[] newArray(int size) { - return new TypeConfig[size]; - } - }; + /** @hide */ + @Retention(SOURCE) + @StringDef( + value = { + HINT_FOR_NOTIFICATION, + HINT_FOR_IN_APP, + }, + prefix = "HINT_") + public @interface Hint {} /** - * Returns a final list of types that the text classifier should look for. - * - * <p>NOTE: This method is intended for use by a text classifier. - * - * @param defaultTypes types the text classifier thinks should be included before factoring - * in the included/excluded types given by the client. + * To indicate the generated actions will be used within the app. */ - @NonNull - public Collection<String> resolveTypes(@Nullable Collection<String> defaultTypes) { - Set<String> types = new ArraySet<>(); - if (mIncludeTypesFromTextClassifier && defaultTypes != null) { - types.addAll(defaultTypes); - } - types.addAll(mIncludedTypes); - types.removeAll(mExcludedTypes); - return Collections.unmodifiableCollection(types); - } - + public static final String HINT_FOR_IN_APP = "in_app"; /** - * Return whether the client allows the text classifier to include its own list of default - * types. If this function returns {@code true}, the text classifier can consider specifying - * a default list of entity types in {@link #resolveTypes(Collection)}. - * - * <p>NOTE: This method is intended for use by a text classifier. - * - * @see #resolveTypes(Collection) + * To indicate the generated actions will be used for notification. */ - public boolean shouldIncludeTypesFromTextClassifier() { - return mIncludeTypesFromTextClassifier; - } - - /** Builder class to construct the {@link TypeConfig} object. */ - public static final class Builder { - @Nullable - private Collection<String> mExcludedTypes; - @Nullable - private Collection<String> mIncludedTypes; - private boolean mIncludeTypesFromTextClassifier = true; - - /** - * Sets a collection of types that are explicitly included, for example, {@link - * #TYPE_VIEW_CALENDAR}. - */ - @NonNull - public Builder setIncludedTypes( - @Nullable @ActionType Collection<String> includedTypes) { - mIncludedTypes = includedTypes; - return this; - } - - /** - * Sets a collection of types that are explicitly excluded, for example, {@link - * #TYPE_VIEW_CALENDAR}. - */ - @NonNull - public Builder setExcludedTypes( - @Nullable @ActionType Collection<String> excludedTypes) { - mExcludedTypes = excludedTypes; - return this; - } - - /** - * Specifies whether or not to include the types suggested by the text classifier. By - * default, it is included. - */ - @NonNull - public Builder includeTypesFromTextClassifier(boolean includeTypesFromTextClassifier) { - mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier; - return this; - } - - /** - * Combines all of the options that have been set and returns a new {@link TypeConfig} - * object. - */ - @NonNull - public TypeConfig build() { - return new TypeConfig( - mIncludedTypes == null - ? Collections.emptySet() - : new ArraySet<>(mIncludedTypes), - mExcludedTypes == null - ? Collections.emptySet() - : new ArraySet<>(mExcludedTypes), - mIncludeTypesFromTextClassifier); - } - } - } + public static final String HINT_FOR_NOTIFICATION = "notification"; - /** - * A request object for generating conversation action suggestions. - * - * @see TextClassifier#suggestConversationActions(Request) - */ - public static final class Request implements Parcelable { @NonNull private final List<Message> mConversation; @NonNull - private final TypeConfig mTypeConfig; + private final TextClassifier.EntityConfig mTypeConfig; private final int mMaxSuggestions; @NonNull @Hint @@ -699,7 +321,7 @@ public final class ConversationActions implements Parcelable { private Request( @NonNull List<Message> conversation, - @NonNull TypeConfig typeConfig, + @NonNull TextClassifier.EntityConfig typeConfig, int maxSuggestions, String conversationId, @Nullable @Hint List<String> hints) { @@ -713,7 +335,7 @@ public final class ConversationActions implements Parcelable { private static Request readFromParcel(Parcel in) { List<Message> conversation = new ArrayList<>(); in.readParcelableList(conversation, null); - TypeConfig typeConfig = in.readParcelable(null); + TextClassifier.EntityConfig typeConfig = in.readParcelable(null); int maxSuggestions = in.readInt(); String conversationId = in.readString(); List<String> hints = new ArrayList<>(); @@ -760,7 +382,7 @@ public final class ConversationActions implements Parcelable { /** Returns the type config. */ @NonNull - public TypeConfig getTypeConfig() { + public TextClassifier.EntityConfig getTypeConfig() { return mTypeConfig; } @@ -820,7 +442,7 @@ public final class ConversationActions implements Parcelable { @NonNull private List<Message> mConversation; @Nullable - private TypeConfig mTypeConfig; + private TextClassifier.EntityConfig mTypeConfig; private int mMaxSuggestions; @Nullable private String mConversationId; @@ -849,7 +471,7 @@ public final class ConversationActions implements Parcelable { /** Sets the type config. */ @NonNull - public Builder setTypeConfig(@Nullable TypeConfig typeConfig) { + public Builder setTypeConfig(@Nullable TextClassifier.EntityConfig typeConfig) { mTypeConfig = typeConfig; return this; } @@ -879,7 +501,9 @@ public final class ConversationActions implements Parcelable { public Request build() { return new Request( Collections.unmodifiableList(mConversation), - mTypeConfig == null ? new TypeConfig.Builder().build() : mTypeConfig, + mTypeConfig == null + ? new TextClassifier.EntityConfig.Builder().build() + : mTypeConfig, mMaxSuggestions, mConversationId, mHints == null diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java index 50801a2b3e3f..ce680ecbd119 100644 --- a/core/java/android/view/textclassifier/TextClassificationConstants.java +++ b/core/java/android/view/textclassifier/TextClassificationConstants.java @@ -117,15 +117,15 @@ public final class TextClassificationConstants { .add(TextClassifier.TYPE_FLIGHT_NUMBER).toString(); private static final String CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES = new StringJoiner(ENTITY_LIST_DELIMITER) - .add(ConversationActions.TYPE_TEXT_REPLY) - .add(ConversationActions.TYPE_CREATE_REMINDER) - .add(ConversationActions.TYPE_CALL_PHONE) - .add(ConversationActions.TYPE_OPEN_URL) - .add(ConversationActions.TYPE_SEND_EMAIL) - .add(ConversationActions.TYPE_SEND_SMS) - .add(ConversationActions.TYPE_TRACK_FLIGHT) - .add(ConversationActions.TYPE_VIEW_CALENDAR) - .add(ConversationActions.TYPE_VIEW_MAP) + .add(ConversationAction.TYPE_TEXT_REPLY) + .add(ConversationAction.TYPE_CREATE_REMINDER) + .add(ConversationAction.TYPE_CALL_PHONE) + .add(ConversationAction.TYPE_OPEN_URL) + .add(ConversationAction.TYPE_SEND_EMAIL) + .add(ConversationAction.TYPE_SEND_SMS) + .add(ConversationAction.TYPE_TRACK_FLIGHT) + .add(ConversationAction.TYPE_VIEW_CALENDAR) + .add(ConversationAction.TYPE_VIEW_MAP) .toString(); private final boolean mSystemTextClassifierEnabled; diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java index 8709e09bbf55..5a5613605e36 100644 --- a/core/java/android/view/textclassifier/TextClassifier.java +++ b/core/java/android/view/textclassifier/TextClassifier.java @@ -32,7 +32,6 @@ import android.text.style.URLSpan; import android.text.util.Linkify; import android.text.util.Linkify.LinkifyMask; import android.util.ArrayMap; -import android.util.ArraySet; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; @@ -43,6 +42,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -324,7 +324,7 @@ public interface TextClassifier { } /** - * Detects the language of the specified text. + * Detects the language of the text in the given request. * * <p><strong>NOTE: </strong>Call on a worker thread. * @@ -403,42 +403,59 @@ public interface TextClassifier { default void dump(@NonNull IndentingPrintWriter printWriter) {} /** - * Configuration object for specifying what entities to identify. + * Configuration object for specifying what entity types to identify. * * Configs are initially based on a predefined preset, and can be modified from there. */ final class EntityConfig implements Parcelable { - private final Collection<String> mHints; - private final Collection<String> mExcludedEntityTypes; - private final Collection<String> mIncludedEntityTypes; - private final boolean mUseHints; - - private EntityConfig(boolean useHints, Collection<String> hints, - Collection<String> includedEntityTypes, Collection<String> excludedEntityTypes) { - mHints = hints == null - ? Collections.EMPTY_LIST - : Collections.unmodifiableCollection(new ArraySet<>(hints)); - mExcludedEntityTypes = excludedEntityTypes == null - ? Collections.EMPTY_LIST : new ArraySet<>(excludedEntityTypes); - mIncludedEntityTypes = includedEntityTypes == null - ? Collections.EMPTY_LIST : new ArraySet<>(includedEntityTypes); - mUseHints = useHints; + private final List<String> mIncludedTypes; + private final List<String> mExcludedTypes; + private final List<String> mHints; + private final boolean mIncludeTypesFromTextClassifier; + + private EntityConfig( + List<String> includedEntityTypes, + List<String> excludedEntityTypes, + List<String> hints, + boolean includeTypesFromTextClassifier) { + mIncludedTypes = Preconditions.checkNotNull(includedEntityTypes); + mExcludedTypes = Preconditions.checkNotNull(excludedEntityTypes); + mHints = Preconditions.checkNotNull(hints); + mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier; + } + + private EntityConfig(Parcel in) { + mIncludedTypes = new ArrayList<>(); + in.readStringList(mIncludedTypes); + mExcludedTypes = new ArrayList<>(); + in.readStringList(mExcludedTypes); + List<String> tmpHints = new ArrayList<>(); + in.readStringList(tmpHints); + mHints = Collections.unmodifiableList(tmpHints); + mIncludeTypesFromTextClassifier = in.readByte() != 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeStringList(mIncludedTypes); + parcel.writeStringList(mExcludedTypes); + parcel.writeStringList(mHints); + parcel.writeByte((byte) (mIncludeTypesFromTextClassifier ? 1 : 0)); } /** * Creates an EntityConfig. * * @param hints Hints for the TextClassifier to determine what types of entities to find. + * + * @deprecated Use {@link Builder} instead. */ + @Deprecated public static EntityConfig createWithHints(@Nullable Collection<String> hints) { - return new EntityConfig(/* useHints */ true, hints, - /* includedEntityTypes */null, /* excludedEntityTypes */ null); - } - - // TODO: Remove once apps can build against the latest sdk. - /** @hide */ - public static EntityConfig create(@Nullable Collection<String> hints) { - return createWithHints(hints); + return new EntityConfig.Builder() + .includeTypesFromTextClassifier(true) + .setHints(hints) + .build(); } /** @@ -450,12 +467,19 @@ public interface TextClassifier { * * * Note that if an entity has been excluded, the exclusion will take precedence. + * + * @deprecated Use {@link Builder} instead. */ + @Deprecated public static EntityConfig create(@Nullable Collection<String> hints, @Nullable Collection<String> includedEntityTypes, @Nullable Collection<String> excludedEntityTypes) { - return new EntityConfig(/* useHints */ true, hints, - includedEntityTypes, excludedEntityTypes); + return new EntityConfig.Builder() + .setIncludedTypes(includedEntityTypes) + .setExcludedTypes(excludedEntityTypes) + .setHints(hints) + .includeTypesFromTextClassifier(true) + .build(); } /** @@ -463,34 +487,33 @@ public interface TextClassifier { * * @param entityTypes Complete set of entities, e.g. {@link #TYPE_URL} to find. * + * @deprecated Use {@link Builder} instead. */ + @Deprecated public static EntityConfig createWithExplicitEntityList( @Nullable Collection<String> entityTypes) { - return new EntityConfig(/* useHints */ false, /* hints */ null, - /* includedEntityTypes */ entityTypes, /* excludedEntityTypes */ null); - } - - // TODO: Remove once apps can build against the latest sdk. - /** @hide */ - public static EntityConfig createWithEntityList(@Nullable Collection<String> entityTypes) { - return createWithExplicitEntityList(entityTypes); + return new EntityConfig.Builder() + .setIncludedTypes(entityTypes) + .includeTypesFromTextClassifier(false) + .build(); } /** - * Returns a list of the final set of entities to find. + * Returns a final list of entity types to find. * - * @param entities Entities we think should be found before factoring in includes/excludes + * @param entityTypes Entity types we think should be found before factoring in + * includes/excludes * * This method is intended for use by TextClassifier implementations. */ public Collection<String> resolveEntityListModifications( - @NonNull Collection<String> entities) { - final Set<String> finalSet = new HashSet(); - if (mUseHints) { - finalSet.addAll(entities); + @NonNull Collection<String> entityTypes) { + final Set<String> finalSet = new HashSet<>(); + if (mIncludeTypesFromTextClassifier) { + finalSet.addAll(entityTypes); } - finalSet.addAll(mIncludedEntityTypes); - finalSet.removeAll(mExcludedEntityTypes); + finalSet.addAll(mIncludedTypes); + finalSet.removeAll(mExcludedTypes); return finalSet; } @@ -503,17 +526,22 @@ public interface TextClassifier { return mHints; } - @Override - public int describeContents() { - return 0; + /** + * Return whether the client allows the text classifier to include its own list of + * default types. If this function returns {@code true}, a default list of types suggested + * from a text classifier will be taking into account. + * + * <p>NOTE: This method is intended for use by a text classifier. + * + * @see #resolveEntityListModifications(Collection) + */ + public boolean shouldIncludeTypesFromTextClassifier() { + return mIncludeTypesFromTextClassifier; } @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeStringList(new ArrayList<>(mHints)); - dest.writeStringList(new ArrayList<>(mExcludedEntityTypes)); - dest.writeStringList(new ArrayList<>(mIncludedEntityTypes)); - dest.writeInt(mUseHints ? 1 : 0); + public int describeContents() { + return 0; } public static final Parcelable.Creator<EntityConfig> CREATOR = @@ -529,11 +557,75 @@ public interface TextClassifier { } }; - private EntityConfig(Parcel in) { - mHints = new ArraySet<>(in.createStringArrayList()); - mExcludedEntityTypes = new ArraySet<>(in.createStringArrayList()); - mIncludedEntityTypes = new ArraySet<>(in.createStringArrayList()); - mUseHints = in.readInt() == 1; + + + /** Builder class to construct the {@link EntityConfig} object. */ + public static final class Builder { + @Nullable + private Collection<String> mIncludedTypes; + @Nullable + private Collection<String> mExcludedTypes; + @Nullable + private Collection<String> mHints; + private boolean mIncludeTypesFromTextClassifier = true; + + /** + * Sets a collection of types that are explicitly included. + */ + @NonNull + public Builder setIncludedTypes(@Nullable Collection<String> includedTypes) { + mIncludedTypes = includedTypes; + return this; + } + + /** + * Sets a collection of types that are explicitly excluded. + */ + @NonNull + public Builder setExcludedTypes(@Nullable Collection<String> excludedTypes) { + mExcludedTypes = excludedTypes; + return this; + } + + /** + * Specifies whether or not to include the types suggested by the text classifier. By + * default, it is included. + */ + @NonNull + public Builder includeTypesFromTextClassifier(boolean includeTypesFromTextClassifier) { + mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier; + return this; + } + + + /** + * Sets the hints for the TextClassifier to determine what types of entities to find. + * These hints will only be used if {@link #includeTypesFromTextClassifier} is + * set to be true. + */ + public Builder setHints(Collection<String> hints) { + mHints = hints; + return this; + } + + /** + * Combines all of the options that have been set and returns a new {@link EntityConfig} + * object. + */ + @NonNull + public EntityConfig build() { + return new EntityConfig( + mIncludedTypes == null + ? Collections.emptyList() + : new ArrayList<>(mIncludedTypes), + mExcludedTypes == null + ? Collections.emptyList() + : new ArrayList<>(mExcludedTypes), + mHints == null + ? Collections.emptyList() + : Collections.unmodifiableList(new ArrayList<>(mHints)), + mIncludeTypesFromTextClassifier); + } } } diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java index d5b9eb1d09dc..9ab963e372b7 100644 --- a/core/java/android/view/textclassifier/TextClassifierImpl.java +++ b/core/java/android/view/textclassifier/TextClassifierImpl.java @@ -393,7 +393,7 @@ public final class TextClassifierImpl implements TextClassifier { actionsImpl.suggestActions(nativeConversation, null); Collection<String> expectedTypes = resolveActionTypesFromRequest(request); - List<ConversationActions.ConversationAction> conversationActions = new ArrayList<>(); + List<ConversationAction> conversationActions = new ArrayList<>(); int maxSuggestions = nativeSuggestions.length; if (request.getMaxSuggestions() > 0) { maxSuggestions = Math.min(request.getMaxSuggestions(), nativeSuggestions.length); @@ -405,7 +405,7 @@ public final class TextClassifierImpl implements TextClassifier { continue; } conversationActions.add( - new ConversationActions.ConversationAction.Builder(actionType) + new ConversationAction.Builder(actionType) .setTextReply(nativeSuggestion.getResponseText()) .setConfidenceScore(nativeSuggestion.getScore()) .build()); @@ -445,10 +445,10 @@ public final class TextClassifierImpl implements TextClassifier { private Collection<String> resolveActionTypesFromRequest(ConversationActions.Request request) { List<String> defaultActionTypes = - request.getHints().contains(ConversationActions.HINT_FOR_NOTIFICATION) + request.getHints().contains(ConversationActions.Request.HINT_FOR_NOTIFICATION) ? mSettings.getNotificationConversationActionTypes() : mSettings.getInAppConversationActionTypes(); - return request.getTypeConfig().resolveTypes(defaultActionTypes); + return request.getTypeConfig().resolveEntityListModifications(defaultActionTypes); } private AnnotatorModel getAnnotatorImpl(LocaleList localeList) diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java index 9b9b77196a53..8e88c510ec31 100644 --- a/core/java/com/android/internal/app/procstats/ProcessStats.java +++ b/core/java/com/android/internal/app/procstats/ProcessStats.java @@ -23,6 +23,7 @@ import android.os.Parcelable; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; +import android.service.procstats.ProcessStatsAvailablePagesProto; import android.service.procstats.ProcessStatsPackageProto; import android.service.procstats.ProcessStatsSectionProto; import android.text.format.DateFormat; @@ -178,7 +179,7 @@ public final class ProcessStats implements Parcelable { {"proc", "pkg-proc", "pkg-svc", "pkg-asc", "pkg-all", "all"}; // Current version of the parcel format. - private static final int PARCEL_VERSION = 35; + private static final int PARCEL_VERSION = 36; // In-memory Parcel magic number, used to detect attempts to unmarshall bad data private static final int MAGIC = 0x50535454; @@ -237,10 +238,11 @@ public final class ProcessStats implements Parcelable { ArrayList<String> mIndexToCommonString; private static final Pattern sPageTypeRegex = Pattern.compile( - "^Node\\s+(\\d+),.*. type\\s+(\\w+)\\s+([\\s\\d]+?)\\s*$"); - private final ArrayList<Integer> mPageTypeZones = new ArrayList<Integer>(); - private final ArrayList<String> mPageTypeLabels = new ArrayList<String>(); - private final ArrayList<int[]> mPageTypeSizes = new ArrayList<int[]>(); + "^Node\\s+(\\d+),.* zone\\s+(\\w+),.* type\\s+(\\w+)\\s+([\\s\\d]+?)\\s*$"); + private final ArrayList<Integer> mPageTypeNodes = new ArrayList<>(); + private final ArrayList<String> mPageTypeZones = new ArrayList<>(); + private final ArrayList<String> mPageTypeLabels = new ArrayList<>(); + private final ArrayList<int[]> mPageTypeSizes = new ArrayList<>(); public ProcessStats(boolean running) { mRunning = running; @@ -621,6 +623,7 @@ public final class ProcessStats implements Parcelable { try { reader = new BufferedReader(new FileReader("/proc/pagetypeinfo")); final Matcher matcher = sPageTypeRegex.matcher(""); + mPageTypeNodes.clear(); mPageTypeZones.clear(); mPageTypeLabels.clear(); mPageTypeSizes.clear(); @@ -631,16 +634,18 @@ public final class ProcessStats implements Parcelable { } matcher.reset(line); if (matcher.matches()) { - final Integer zone = Integer.valueOf(matcher.group(1), 10); - if (zone == null) { + final Integer node = Integer.valueOf(matcher.group(1), 10); + if (node == null) { continue; } - mPageTypeZones.add(zone); - mPageTypeLabels.add(matcher.group(2)); - mPageTypeSizes.add(splitAndParseNumbers(matcher.group(3))); + mPageTypeNodes.add(node); + mPageTypeZones.add(matcher.group(2)); + mPageTypeLabels.add(matcher.group(3)); + mPageTypeSizes.add(splitAndParseNumbers(matcher.group(4))); } } } catch (IOException ex) { + mPageTypeNodes.clear(); mPageTypeZones.clear(); mPageTypeLabels.clear(); mPageTypeSizes.clear(); @@ -935,7 +940,8 @@ public final class ProcessStats implements Parcelable { final int NPAGETYPES = mPageTypeLabels.size(); out.writeInt(NPAGETYPES); for (int i=0; i<NPAGETYPES; i++) { - out.writeInt(mPageTypeZones.get(i)); + out.writeInt(mPageTypeNodes.get(i)); + out.writeString(mPageTypeZones.get(i)); out.writeString(mPageTypeLabels.get(i)); out.writeIntArray(mPageTypeSizes.get(i)); } @@ -1244,6 +1250,8 @@ public final class ProcessStats implements Parcelable { // Fragmentation info final int NPAGETYPES = in.readInt(); + mPageTypeNodes.clear(); + mPageTypeNodes.ensureCapacity(NPAGETYPES); mPageTypeZones.clear(); mPageTypeZones.ensureCapacity(NPAGETYPES); mPageTypeLabels.clear(); @@ -1251,7 +1259,8 @@ public final class ProcessStats implements Parcelable { mPageTypeSizes.clear(); mPageTypeSizes.ensureCapacity(NPAGETYPES); for (int i=0; i<NPAGETYPES; i++) { - mPageTypeZones.add(in.readInt()); + mPageTypeNodes.add(in.readInt()); + mPageTypeZones.add(in.readString()); mPageTypeLabels.add(in.readString()); mPageTypeSizes.add(in.createIntArray()); } @@ -1764,7 +1773,8 @@ public final class ProcessStats implements Parcelable { pw.println("Available pages by page size:"); final int NPAGETYPES = mPageTypeLabels.size(); for (int i=0; i<NPAGETYPES; i++) { - pw.format("Zone %3d %14s ", mPageTypeZones.get(i), mPageTypeLabels.get(i)); + pw.format("Node %3d Zone %7s %14s ", mPageTypeNodes.get(i), mPageTypeZones.get(i), + mPageTypeLabels.get(i)); final int[] sizes = mPageTypeSizes.get(i); final int N = sizes == null ? 0 : sizes.length; for (int j=0; j<N; j++) { @@ -2095,6 +2105,9 @@ public final class ProcessStats implements Parcelable { pw.print(","); pw.print(mPageTypeZones.get(i)); pw.print(","); + // Wasn't included in original output. + //pw.print(mPageTypeNodes.get(i)); + //pw.print(","); final int[] sizes = mPageTypeSizes.get(i); final int N = sizes == null ? 0 : sizes.length; for (int j=0; j<N; j++) { @@ -2135,6 +2148,20 @@ public final class ProcessStats implements Parcelable { proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_PARTIAL); } + final int NPAGETYPES = mPageTypeLabels.size(); + for (int i = 0; i < NPAGETYPES; i++) { + final long token = proto.start(ProcessStatsSectionProto.AVAILABLE_PAGES); + proto.write(ProcessStatsAvailablePagesProto.NODE, mPageTypeNodes.get(i)); + proto.write(ProcessStatsAvailablePagesProto.ZONE, mPageTypeZones.get(i)); + proto.write(ProcessStatsAvailablePagesProto.LABEL, mPageTypeLabels.get(i)); + final int[] sizes = mPageTypeSizes.get(i); + final int N = sizes == null ? 0 : sizes.length; + for (int j = 0; j < N; j++) { + proto.write(ProcessStatsAvailablePagesProto.PAGES_PER_ORDER, sizes[j]); + } + proto.end(token); + } + final ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); if ((section & REPORT_PROC_STATS) != 0) { for (int ip = 0; ip < procMap.size(); ip++) { diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index cc8da5c6317f..5125f5c8c3da 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -30,6 +30,7 @@ import android.content.IntentFilter; import android.database.ContentObserver; import android.hardware.usb.UsbManager; import android.net.ConnectivityManager; +import android.net.INetworkStatsService; import android.net.NetworkStats; import android.net.Uri; import android.net.wifi.WifiActivityEnergyInfo; @@ -86,7 +87,6 @@ import android.view.Display; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.location.gnssmetrics.GnssMetrics; -import com.android.internal.net.NetworkStatsFactory; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; @@ -11012,7 +11012,6 @@ public class BatteryStatsImpl extends BatteryStats { } } - private final NetworkStatsFactory mNetworkStatsFactory = new NetworkStatsFactory(); private final Pools.Pool<NetworkStats> mNetworkStatsPool = new Pools.SynchronizedPool<>(6); private final Object mWifiNetworkLock = new Object(); @@ -11034,11 +11033,16 @@ public class BatteryStatsImpl extends BatteryStats { private NetworkStats readNetworkStatsLocked(String[] ifaces) { try { if (!ArrayUtils.isEmpty(ifaces)) { - return mNetworkStatsFactory.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, - NetworkStats.TAG_NONE, mNetworkStatsPool.acquire()); + INetworkStatsService statsService = INetworkStatsService.Stub.asInterface( + ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); + if (statsService != null) { + return statsService.getDetailedUidStats(ifaces); + } else { + Slog.e(TAG, "Failed to get networkStatsService "); + } } - } catch (IOException e) { - Slog.e(TAG, "failed to read network stats for ifaces: " + Arrays.toString(ifaces)); + } catch (RemoteException e) { + Slog.e(TAG, "failed to read network stats for ifaces: " + Arrays.toString(ifaces) + e); } return null; } diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index eb7338a7ce58..876bd4fbfce4 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -8,7 +8,6 @@ #include "SkImageInfo.h" #include "SkColor.h" #include "SkColorSpace.h" -#include "SkMatrix44.h" #include "GraphicsJNI.h" #include "SkStream.h" @@ -356,8 +355,8 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, if (xyzD50 == nullptr || transferParameters == nullptr) { colorSpace = SkColorSpace::MakeSRGB(); } else { - SkColorSpaceTransferFn p = GraphicsJNI::getNativeTransferParameters(env, transferParameters); - SkMatrix44 xyzMatrix = GraphicsJNI::getNativeXYZMatrix(env, xyzD50); + skcms_TransferFunction p = GraphicsJNI::getNativeTransferParameters(env, transferParameters); + skcms_Matrix3x3 xyzMatrix = GraphicsJNI::getNativeXYZMatrix(env, xyzD50); colorSpace = SkColorSpace::MakeRGB(p, xyzMatrix); } @@ -549,8 +548,7 @@ static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle, if (skbitmap.colorType() == kRGBA_F16_SkColorType) { // Convert to P3 before encoding. This matches SkAndroidCodec::computeOutputColorSpace // for wide gamuts. - auto cs = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, - SkColorSpace::kDCIP3_D65_Gamut); + auto cs = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3); auto info = skbitmap.info().makeColorType(kRGBA_8888_SkColorType) .makeColorSpace(std::move(cs)); SkBitmap p3; @@ -568,11 +566,34 @@ static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle, return SkEncodeImage(strm.get(), skbitmap, fm, quality) ? JNI_TRUE : JNI_FALSE; } +static inline void bitmapErase(SkBitmap bitmap, const SkColor4f& color, + const sk_sp<SkColorSpace>& colorSpace) { + SkPaint p; + p.setColor4f(color, colorSpace.get()); + p.setBlendMode(SkBlendMode::kSrc); + SkCanvas canvas(bitmap); + canvas.drawPaint(p); +} + static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) { LocalScopedBitmap bitmap(bitmapHandle); SkBitmap skBitmap; bitmap->getSkBitmap(&skBitmap); - skBitmap.eraseColor(color); + bitmapErase(skBitmap, SkColor4f::FromColor(color), SkColorSpace::MakeSRGB()); +} + +static void Bitmap_eraseLong(JNIEnv* env, jobject, jlong bitmapHandle, jobject jColorSpace, + jfloat r, jfloat g, jfloat b, jfloat a) { + sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(env, jColorSpace); + if (GraphicsJNI::hasException(env)) { + return; + } + + LocalScopedBitmap bitmap(bitmapHandle); + SkBitmap skBitmap; + bitmap->getSkBitmap(&skBitmap); + SkColor4f color = SkColor4f{r, g, b, a}; + bitmapErase(skBitmap, color, cs); } static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) { @@ -717,7 +738,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { #endif // Dup the file descriptor so we can keep a reference to it after the Parcel // is disposed. - int dupFd = dup(blob.fd()); + int dupFd = fcntl(blob.fd(), F_DUPFD_CLOEXEC, 0); if (dupFd < 0) { ALOGE("Error allocating dup fd. Error:%d", errno); blob.release(); @@ -910,32 +931,32 @@ static jboolean Bitmap_getColorSpace(JNIEnv* env, jobject, jlong bitmapHandle, SkColorSpace* colorSpace = bitmapHolder->info().colorSpace(); if (colorSpace == nullptr) return JNI_FALSE; - SkMatrix44 xyzMatrix(SkMatrix44::kUninitialized_Constructor); + skcms_Matrix3x3 xyzMatrix; if (!colorSpace->toXYZD50(&xyzMatrix)) return JNI_FALSE; jfloat* xyz = env->GetFloatArrayElements(xyzArray, NULL); - xyz[0] = xyzMatrix.getFloat(0, 0); - xyz[1] = xyzMatrix.getFloat(1, 0); - xyz[2] = xyzMatrix.getFloat(2, 0); - xyz[3] = xyzMatrix.getFloat(0, 1); - xyz[4] = xyzMatrix.getFloat(1, 1); - xyz[5] = xyzMatrix.getFloat(2, 1); - xyz[6] = xyzMatrix.getFloat(0, 2); - xyz[7] = xyzMatrix.getFloat(1, 2); - xyz[8] = xyzMatrix.getFloat(2, 2); + xyz[0] = xyzMatrix.vals[0][0]; + xyz[1] = xyzMatrix.vals[1][0]; + xyz[2] = xyzMatrix.vals[2][0]; + xyz[3] = xyzMatrix.vals[0][1]; + xyz[4] = xyzMatrix.vals[1][1]; + xyz[5] = xyzMatrix.vals[2][1]; + xyz[6] = xyzMatrix.vals[0][2]; + xyz[7] = xyzMatrix.vals[1][2]; + xyz[8] = xyzMatrix.vals[2][2]; env->ReleaseFloatArrayElements(xyzArray, xyz, 0); - SkColorSpaceTransferFn transferParams; + skcms_TransferFunction transferParams; if (!colorSpace->isNumericalTransferFn(&transferParams)) return JNI_FALSE; jfloat* params = env->GetFloatArrayElements(paramsArray, NULL); - params[0] = transferParams.fA; - params[1] = transferParams.fB; - params[2] = transferParams.fC; - params[3] = transferParams.fD; - params[4] = transferParams.fE; - params[5] = transferParams.fF; - params[6] = transferParams.fG; + params[0] = transferParams.a; + params[1] = transferParams.b; + params[2] = transferParams.c; + params[3] = transferParams.d; + params[4] = transferParams.e; + params[5] = transferParams.f; + params[6] = transferParams.g; env->ReleaseFloatArrayElements(paramsArray, params, 0); return JNI_TRUE; @@ -1121,8 +1142,8 @@ static jobject Bitmap_createHardwareBitmap(JNIEnv* env, jobject, jobject graphic static jobject Bitmap_wrapHardwareBufferBitmap(JNIEnv* env, jobject, jobject hardwareBuffer, jfloatArray xyzD50, jobject transferParameters) { - SkColorSpaceTransferFn p = GraphicsJNI::getNativeTransferParameters(env, transferParameters); - SkMatrix44 xyzMatrix = GraphicsJNI::getNativeXYZMatrix(env, xyzD50); + skcms_TransferFunction p = GraphicsJNI::getNativeTransferParameters(env, transferParameters); + skcms_Matrix3x3 xyzMatrix = GraphicsJNI::getNativeXYZMatrix(env, xyzD50); sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeRGB(p, xyzMatrix); AHardwareBuffer* hwBuf = android_hardware_HardwareBuffer_getNativeHardwareBuffer(env, hardwareBuffer); @@ -1183,6 +1204,7 @@ static const JNINativeMethod gBitmapMethods[] = { { "nativeCompress", "(JIILjava/io/OutputStream;[B)Z", (void*)Bitmap_compress }, { "nativeErase", "(JI)V", (void*)Bitmap_erase }, + { "nativeErase", "(JLandroid/graphics/ColorSpace;FFFF)V", (void*)Bitmap_eraseLong }, { "nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes }, { "nativeConfig", "(J)I", (void*)Bitmap_config }, { "nativeHasAlpha", "(J)Z", (void*)Bitmap_hasAlpha }, diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index 67d0c8aced61..9e74b883a298 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -424,30 +424,30 @@ jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region) /////////////////////////////////////////////////////////////////////////////// -SkColorSpaceTransferFn GraphicsJNI::getNativeTransferParameters(JNIEnv* env, jobject transferParams) { - SkColorSpaceTransferFn p; - p.fA = (float) env->GetDoubleField(transferParams, gTransferParams_aFieldID); - p.fB = (float) env->GetDoubleField(transferParams, gTransferParams_bFieldID); - p.fC = (float) env->GetDoubleField(transferParams, gTransferParams_cFieldID); - p.fD = (float) env->GetDoubleField(transferParams, gTransferParams_dFieldID); - p.fE = (float) env->GetDoubleField(transferParams, gTransferParams_eFieldID); - p.fF = (float) env->GetDoubleField(transferParams, gTransferParams_fFieldID); - p.fG = (float) env->GetDoubleField(transferParams, gTransferParams_gFieldID); +skcms_TransferFunction GraphicsJNI::getNativeTransferParameters(JNIEnv* env, jobject transferParams) { + skcms_TransferFunction p; + p.a = (float) env->GetDoubleField(transferParams, gTransferParams_aFieldID); + p.b = (float) env->GetDoubleField(transferParams, gTransferParams_bFieldID); + p.c = (float) env->GetDoubleField(transferParams, gTransferParams_cFieldID); + p.d = (float) env->GetDoubleField(transferParams, gTransferParams_dFieldID); + p.e = (float) env->GetDoubleField(transferParams, gTransferParams_eFieldID); + p.f = (float) env->GetDoubleField(transferParams, gTransferParams_fFieldID); + p.g = (float) env->GetDoubleField(transferParams, gTransferParams_gFieldID); return p; } -SkMatrix44 GraphicsJNI::getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50) { - SkMatrix44 xyzMatrix(SkMatrix44::kIdentity_Constructor); +skcms_Matrix3x3 GraphicsJNI::getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50) { + skcms_Matrix3x3 xyzMatrix; jfloat* array = env->GetFloatArrayElements(xyzD50, NULL); - xyzMatrix.setFloat(0, 0, array[0]); - xyzMatrix.setFloat(1, 0, array[1]); - xyzMatrix.setFloat(2, 0, array[2]); - xyzMatrix.setFloat(0, 1, array[3]); - xyzMatrix.setFloat(1, 1, array[4]); - xyzMatrix.setFloat(2, 1, array[5]); - xyzMatrix.setFloat(0, 2, array[6]); - xyzMatrix.setFloat(1, 2, array[7]); - xyzMatrix.setFloat(2, 2, array[8]); + xyzMatrix.vals[0][0] = array[0]; + xyzMatrix.vals[1][0] = array[1]; + xyzMatrix.vals[2][0] = array[2]; + xyzMatrix.vals[0][1] = array[3]; + xyzMatrix.vals[1][1] = array[4]; + xyzMatrix.vals[2][1] = array[5]; + xyzMatrix.vals[0][2] = array[6]; + xyzMatrix.vals[1][2] = array[7]; + xyzMatrix.vals[2][2] = array[8]; env->ReleaseFloatArrayElements(xyzD50, array, 0); return xyzMatrix; } @@ -456,12 +456,14 @@ sk_sp<SkColorSpace> GraphicsJNI::getNativeColorSpace(JNIEnv* env, jobject colorS if (colorSpace == nullptr) return nullptr; if (!env->IsInstanceOf(colorSpace, gColorSpaceRGB_class)) { doThrowIAE(env, "The color space must be an RGB color space"); + return nullptr; } jobject transferParams = env->CallObjectMethod(colorSpace, gColorSpaceRGB_getTransferParametersMethodID); if (transferParams == nullptr) { doThrowIAE(env, "The color space must use an ICC parametric transfer function"); + return nullptr; } jfloatArray illuminantD50 = (jfloatArray) env->GetStaticObjectField(gColorSpace_class, @@ -472,8 +474,8 @@ sk_sp<SkColorSpace> GraphicsJNI::getNativeColorSpace(JNIEnv* env, jobject colorS jfloatArray xyzD50 = (jfloatArray) env->CallObjectMethod(colorSpaceD50, gColorSpaceRGB_getTransformMethodID); - SkMatrix44 xyzMatrix = getNativeXYZMatrix(env, xyzD50); - SkColorSpaceTransferFn transferFunction = getNativeTransferParameters(env, transferParams); + skcms_Matrix3x3 xyzMatrix = getNativeXYZMatrix(env, xyzD50); + skcms_TransferFunction transferFunction = getNativeTransferParameters(env, transferParams); return SkColorSpace::MakeRGB(transferFunction, xyzMatrix); } @@ -499,30 +501,30 @@ jobject GraphicsJNI::getColorSpace(JNIEnv* env, sk_sp<SkColorSpace>& decodeColor } else if (decodeColorSpace.get() != nullptr) { // Try to match against known RGB color spaces using the CIE XYZ D50 // conversion matrix and numerical transfer function parameters - SkMatrix44 xyzMatrix(SkMatrix44::kUninitialized_Constructor); + skcms_Matrix3x3 xyzMatrix; LOG_ALWAYS_FATAL_IF(!decodeColorSpace->toXYZD50(&xyzMatrix)); - SkColorSpaceTransferFn transferParams; + skcms_TransferFunction transferParams; // We can only handle numerical transfer functions at the moment LOG_ALWAYS_FATAL_IF(!decodeColorSpace->isNumericalTransferFn(&transferParams)); jobject params = env->NewObject(gTransferParameters_class, gTransferParameters_constructorMethodID, - transferParams.fA, transferParams.fB, transferParams.fC, - transferParams.fD, transferParams.fE, transferParams.fF, - transferParams.fG); + transferParams.a, transferParams.b, transferParams.c, + transferParams.d, transferParams.e, transferParams.f, + transferParams.g); jfloatArray xyzArray = env->NewFloatArray(9); jfloat xyz[9] = { - xyzMatrix.getFloat(0, 0), - xyzMatrix.getFloat(1, 0), - xyzMatrix.getFloat(2, 0), - xyzMatrix.getFloat(0, 1), - xyzMatrix.getFloat(1, 1), - xyzMatrix.getFloat(2, 1), - xyzMatrix.getFloat(0, 2), - xyzMatrix.getFloat(1, 2), - xyzMatrix.getFloat(2, 2) + xyzMatrix.vals[0][0], + xyzMatrix.vals[1][0], + xyzMatrix.vals[2][0], + xyzMatrix.vals[0][1], + xyzMatrix.vals[1][1], + xyzMatrix.vals[2][1], + xyzMatrix.vals[0][2], + xyzMatrix.vals[1][2], + xyzMatrix.vals[2][2] }; env->SetFloatArrayRegion(xyzArray, 0, 9, xyz); diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index b0bd68336e08..699d153874a2 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -10,7 +10,6 @@ #include "SkPoint.h" #include "SkRect.h" #include "SkColorSpace.h" -#include "SkMatrix44.h" #include <jni.h> #include <hwui/Canvas.h> #include <hwui/Bitmap.h> @@ -101,8 +100,8 @@ public: int srcStride, int x, int y, int width, int height, SkBitmap* dstBitmap); - static SkColorSpaceTransferFn getNativeTransferParameters(JNIEnv* env, jobject transferParams); - static SkMatrix44 getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50); + static skcms_TransferFunction getNativeTransferParameters(JNIEnv* env, jobject transferParams); + static skcms_Matrix3x3 getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50); static sk_sp<SkColorSpace> getNativeColorSpace(JNIEnv* env, jobject colorSpace); static jobject getColorSpace(JNIEnv* env, sk_sp<SkColorSpace>& decodeColorSpace, diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp index 7ef06dc54945..3b59321024fd 100644 --- a/core/jni/android_os_Parcel.cpp +++ b/core/jni/android_os_Parcel.cpp @@ -473,7 +473,7 @@ static jobject android_os_Parcel_readFileDescriptor(JNIEnv* env, jclass clazz, j if (parcel != NULL) { int fd = parcel->readFileDescriptor(); if (fd < 0) return NULL; - fd = dup(fd); + fd = fcntl(fd, F_DUPFD_CLOEXEC, 0); if (fd < 0) return NULL; return jniCreateFileDescriptor(env, fd); } diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index 7b564ae162ce..4101c04162af 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -44,6 +44,8 @@ #include "androidfw/MutexGuard.h" #include "androidfw/PosixUtils.h" #include "androidfw/ResourceTypes.h" +#include "androidfw/ResourceUtils.h" + #include "core_jni_helpers.h" #include "jni.h" #include "nativehelper/JNIHelp.h" @@ -975,34 +977,7 @@ static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, j return nullptr; } - std::string result; - if (name.package != nullptr) { - result.append(name.package, name.package_len); - } - - if (name.type != nullptr || name.type16 != nullptr) { - if (!result.empty()) { - result += ":"; - } - - if (name.type != nullptr) { - result.append(name.type, name.type_len); - } else { - result += util::Utf16ToUtf8(StringPiece16(name.type16, name.type_len)); - } - } - - if (name.entry != nullptr || name.entry16 != nullptr) { - if (!result.empty()) { - result += "/"; - } - - if (name.entry != nullptr) { - result.append(name.entry, name.entry_len); - } else { - result += util::Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len)); - } - } + std::string result = ToFormattedResourceString(&name); return env->NewStringUTF(result.c_str()); } @@ -1049,6 +1024,26 @@ static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong p return nullptr; } +static void NativeSetResourceResolutionLoggingEnabled(JNIEnv* /*env*/, + jclass /*clazz*/, + jlong ptr, + jboolean enabled) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + assetmanager->SetResourceResolutionLoggingEnabled(enabled); +} + +static jstring NativeGetLastResourceResolution(JNIEnv* env, + jclass /*clazz*/, + jlong ptr) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + std::string resolution = assetmanager->GetLastResourceResolution(); + if (resolution.empty()) { + return nullptr; + } else { + return env->NewStringUTF(resolution.c_str()); + } +} + static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr, jboolean exclude_system) { ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); @@ -1452,6 +1447,10 @@ static const JNINativeMethod gAssetManagerMethods[] = { {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName}, {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName}, {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName}, + {"nativeSetResourceResolutionLoggingEnabled", "(JZ)V", + (void*) NativeSetResourceResolutionLoggingEnabled}, + {"nativeGetLastResourceResolution", "(J)Ljava/lang/String;", + (void*) NativeGetLastResourceResolution}, {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales}, {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;", (void*)NativeGetSizeConfigurations}, diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp index bd87dcc325a8..41e00b9461c8 100644 --- a/core/jni/fd_utils.cpp +++ b/core/jni/fd_utils.cpp @@ -33,6 +33,7 @@ // Static whitelist of open paths that the zygote is allowed to keep open. static const char* kPathWhitelist[] = { + "/apex/com.android.conscrypt/javalib/conscrypt.jar", "/dev/null", "/dev/socket/zygote", "/dev/socket/zygote_secondary", diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index 3e1c5a334184..49ca378ef0e2 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -29,6 +29,12 @@ enum Action { // ACTION: Settings > Any preference is changed ACTION_SETTINGS_PREFERENCE_CHANGE = 853; + + // ACTION: Tap & Pay -> Default Application Setting -> Use Forground + ACTION_NFC_PAYMENT_FOREGROUND_SETTING = 1622; + + // ACTION: Tap & Pay -> Default Application Setting -> Use Default + ACTION_NFC_PAYMENT_ALWAYS_SETTING = 1623; } /** @@ -85,4 +91,10 @@ enum PageId { // OPEN: Settings > Apps & Notifications -> Special app access -> Financial Apps Sms Access SETTINGS_FINANCIAL_APPS_SMS_ACCESS = 1597; + + // OPEN: Settings > System > Input & Gesture > Skip songs + SETTINGS_GESTURE_SKIP = 1624; + + // OPEN: Settings > System > Input & Gesture > Silence alerts + SETTINGS_GESTURE_SILENCE = 1625; } diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index 0e052fe8d4b1..9f4345d4668f 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -525,7 +525,10 @@ message SecureSettingsProto { } optional Zen zen = 71; + optional SettingProto skip_gesture_enabled = 74 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto silence_gesture_enabled = 75 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Please insert fields in alphabetical order and group them into messages // if possible (to avoid reaching the method limit). - // Next tag = 74; + // Next tag = 76; } diff --git a/core/proto/android/service/package.proto b/core/proto/android/service/package.proto index 4ecf52ce5012..7f96d701cdbf 100644 --- a/core/proto/android/service/package.proto +++ b/core/proto/android/service/package.proto @@ -111,6 +111,7 @@ message PackageProto { optional EnabledState enabled_state = 7; optional string last_disabled_app_caller = 8; optional string suspending_package = 9; + optional int32 distraction_flags = 10; } // Name of package. e.g. "com.android.providers.telephony". diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto index 71ebcc1e3659..da801ffcc99a 100644 --- a/core/proto/android/service/procstats.proto +++ b/core/proto/android/service/procstats.proto @@ -43,7 +43,7 @@ message ProcessStatsServiceDumpProto { * Data model from /frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java * This proto is defined based on the writeToParcel method. * - * Next Tag: 10 + * Next Tag: 11 */ message ProcessStatsSectionProto { option (android.msg_privacy).dest = DEST_AUTOMATIC; @@ -76,6 +76,9 @@ message ProcessStatsSectionProto { } repeated Status status = 7; + // Number of pages available of various types and sizes, representation fragmentation. + repeated ProcessStatsAvailablePagesProto available_pages = 10; + // Stats for each process. repeated ProcessStatsProto process_stats = 8; @@ -83,6 +86,24 @@ message ProcessStatsSectionProto { repeated ProcessStatsPackageProto package_stats = 9; } +// Next Tag: 5 +message ProcessStatsAvailablePagesProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + // Node these pages are in (as per /proc/pagetypeinfo) + optional int32 node = 1; + + // Zone these pages are in (as per /proc/pagetypeinfo) + optional string zone = 2; + + // Label for the type of these pages (as per /proc/pagetypeinfo) + optional string label = 3; + + // Distribution of number of pages available by order size. First entry in array is + // order 0, second is order 1, etc. Each order increase is a doubling of page size. + repeated int32 pages_per_order = 4; +} + // Next Tag: 10 message ProcessStatsStateProto { option (android.msg_privacy).dest = DEST_AUTOMATIC; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 8147b4a2a009..2f3a4910bf83 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -51,6 +51,7 @@ <protected-broadcast android:name="android.intent.action.PACKAGE_VERIFIED" /> <protected-broadcast android:name="android.intent.action.PACKAGES_SUSPENDED" /> <protected-broadcast android:name="android.intent.action.PACKAGES_UNSUSPENDED" /> + <protected-broadcast android:name="android.intent.action.DISTRACTING_PACKAGES_CHANGED" /> <protected-broadcast android:name="android.intent.action.ACTION_PREFERRED_ACTIVITY_CHANGED" /> <protected-broadcast android:name="android.intent.action.UID_REMOVED" /> <protected-broadcast android:name="android.intent.action.QUERY_PACKAGE_RESTART" /> @@ -2268,6 +2269,11 @@ <permission android:name="android.permission.START_ANY_ACTIVITY" android:protectionLevel="signature" /> + <!-- Allows an application to start activities from background + @hide --> + <permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" + android:protectionLevel="signature|privileged|vendorPrivileged|oem" /> + <!-- @SystemApi Must be required by activities that handle the intent action {@link Intent#ACTION_SEND_SHOW_SUSPENDED_APP_DETAILS}. This is for use by apps that hold {@link Manifest.permission#SUSPEND_APPS} to interact with the system. diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 1feb59a52ad1..2e3bd7cfded5 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -929,9 +929,6 @@ in hardware. --> <bool name="config_setColorTransformAccelerated">false</bool> - <!-- Boolean indicating whether display white balance is supported. --> - <bool name="config_displayWhiteBalanceAvailable">false</bool> - <!-- Control whether Night display is available. This should only be enabled on devices that have a HWC implementation that can apply the matrix passed to setColorTransform without impacting power, performance, and app compatibility (e.g. protected content). --> @@ -987,6 +984,44 @@ <!-- B y-intercept --> <item>-0.198650895</item> </string-array> + <!-- Boolean indicating whether display white balance is supported. --> + <bool name="config_displayWhiteBalanceAvailable">false</bool> + + <!-- Minimum color temperature, in Kelvin, supported by display white balance. --> + <integer name="config_displayWhiteBalanceColorTemperatureMin">4000</integer> + + <!-- Maximum color temperature, in Kelvin, supported by display white balance. --> + <integer name="config_displayWhiteBalanceColorTemperatureMax">8000</integer> + + <!-- Default color temperature, in Kelvin, used by display white balance. --> + <integer name="config_displayWhiteBalanceColorTemperatureDefault">6500</integer> + + <!-- The display primaries, in CIE1931 XYZ color space, for display + white balance to use in its calculations. --> + <string-array name="config_displayWhiteBalanceDisplayPrimaries"> + <!-- Red X --> <item>0.412315</item> + <!-- Red Y --> <item>0.212600</item> + <!-- Red Z --> <item>0.019327</item> + <!-- Green X --> <item>0.357600</item> + <!-- Green Y --> <item>0.715200</item> + <!-- Green Z --> <item>0.119200</item> + <!-- Blue X --> <item>0.180500</item> + <!-- Blue Y --> <item>0.072200</item> + <!-- Blue Z --> <item>0.950633</item> + <!-- White X --> <item>0.950456</item> + <!-- White Y --> <item>1.000000</item> + <!-- White Z --> <item>1.089058</item> + </string-array> + + <!-- The nominal white coordinates, in CIE1931 XYZ color space, for Display White Balance to + use in its calculations. AWB will adapt this white point to the target ambient white + point. --> + <string-array name="config_displayWhiteBalanceDisplayNominalWhite"> + <!-- Nominal White X --> <item>0.950456</item> + <!-- Nominal White Y --> <item>1.000000</item> + <!-- Nominal White Z --> <item>1.089058</item> + </string-array> + <!-- Indicate available ColorDisplayController.COLOR_MODE_xxx. --> <integer-array name="config_availableColorModes"> @@ -3673,4 +3708,10 @@ <!-- If device supports corner radius on windows. This should be turned off on low-end devices to improve animation performance. --> <bool name="config_supportsRoundedCornersOnWindows">true</bool> + + <!-- If the sensor that skips media is available or not. --> + <bool name="config_skipSensorAvailable">false</bool> + + <!-- If the sensor that silences alerts is available or not. --> + <bool name="config_silenceSensorAvailable">false</bool> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 010accf19d18..daf8b446c7bb 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3042,6 +3042,13 @@ <java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficientsNative" /> <java-symbol type="array" name="config_availableColorModes" /> + <java-symbol type="bool" name="config_displayWhiteBalanceAvailable" /> + <java-symbol type="integer" name="config_displayWhiteBalanceColorTemperatureMin" /> + <java-symbol type="integer" name="config_displayWhiteBalanceColorTemperatureMax" /> + <java-symbol type="integer" name="config_displayWhiteBalanceColorTemperatureDefault" /> + <java-symbol type="array" name="config_displayWhiteBalanceDisplayPrimaries" /> + <java-symbol type="array" name="config_displayWhiteBalanceDisplayNominalWhite" /> + <!-- Default first user restrictions --> <java-symbol type="array" name="config_defaultFirstUserRestrictions" /> @@ -3529,4 +3536,7 @@ <java-symbol type="string" name="dynamic_mode_notification_title" /> <java-symbol type="string" name="dynamic_mode_notification_summary" /> <java-symbol type="drawable" name="ic_battery" /> + + <java-symbol type="bool" name="config_skipSensorAvailable" /> + <java-symbol type="bool" name="config_silenceSensorAvailable" /> </resources> diff --git a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java new file mode 100644 index 000000000000..7619af23375f --- /dev/null +++ b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.view.autofill; + +import static com.google.common.truth.Truth.assertThat; + +import static org.testng.Assert.assertThrows; + +import android.os.Parcel; +import android.view.View; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class AutofillIdTest { + + @Test + public void testNonVirtual() { + final AutofillId id = new AutofillId(42); + assertThat(id.getViewId()).isEqualTo(42); + assertThat(id.isVirtual()).isFalse(); + assertThat(id.getVirtualChildId()).isEqualTo(View.NO_ID); + + final AutofillId clone = cloneThroughParcel(id); + assertThat(clone.getViewId()).isEqualTo(42); + assertThat(clone.isVirtual()).isFalse(); + assertThat(clone.getVirtualChildId()).isEqualTo(View.NO_ID); + } + + @Test + public void testVirtual() { + final AutofillId id = new AutofillId(42, 108); + assertThat(id.getViewId()).isEqualTo(42); + assertThat(id.isVirtual()).isTrue(); + assertThat(id.getVirtualChildId()).isEqualTo(108); + + final AutofillId clone = cloneThroughParcel(id); + assertThat(clone.getViewId()).isEqualTo(42); + assertThat(clone.isVirtual()).isTrue(); + assertThat(clone.getVirtualChildId()).isEqualTo(108); + } + + @Test + public void testVirtual_parentObjectConstructor() { + assertThrows(NullPointerException.class, () -> new AutofillId(null, 108)); + + final AutofillId id = new AutofillId(new AutofillId(42), 108); + assertThat(id.getViewId()).isEqualTo(42); + assertThat(id.isVirtual()).isTrue(); + assertThat(id.getVirtualChildId()).isEqualTo(108); + + final AutofillId clone = cloneThroughParcel(id); + assertThat(clone.getViewId()).isEqualTo(42); + assertThat(clone.isVirtual()).isTrue(); + assertThat(clone.getVirtualChildId()).isEqualTo(108); + } + + @Test + public void testVirtual_withSession() { + final AutofillId id = new AutofillId(new AutofillId(42), 108, 666); + assertThat(id.getViewId()).isEqualTo(42); + assertThat(id.isVirtual()).isTrue(); + assertThat(id.getVirtualChildId()).isEqualTo(108); + assertThat(id.getSessionId()).isEqualTo(666); + + final AutofillId clone = cloneThroughParcel(id); + assertThat(clone.getViewId()).isEqualTo(42); + assertThat(clone.isVirtual()).isTrue(); + assertThat(clone.getVirtualChildId()).isEqualTo(108); + assertThat(clone.getSessionId()).isEqualTo(666); + } + + @Test + public void testEqualsHashCode() { + final AutofillId realId = new AutofillId(42); + final AutofillId realIdSame = new AutofillId(42); + assertThat(realId).isEqualTo(realIdSame); + assertThat(realIdSame).isEqualTo(realId); + assertThat(realId.hashCode()).isEqualTo(realIdSame.hashCode()); + + final AutofillId realIdDifferent = new AutofillId(108); + assertThat(realId).isNotEqualTo(realIdDifferent); + assertThat(realIdDifferent).isNotEqualTo(realId); + + final AutofillId virtualId = new AutofillId(42, 1); + final AutofillId virtualIdSame = new AutofillId(42, 1); + assertThat(virtualId).isEqualTo(virtualIdSame); + assertThat(virtualIdSame).isEqualTo(virtualId); + assertThat(virtualId.hashCode()).isEqualTo(virtualIdSame.hashCode()); + assertThat(virtualId).isNotEqualTo(realId); + assertThat(realId).isNotEqualTo(virtualId); + + final AutofillId virtualIdDifferentChild = new AutofillId(42, 2); + assertThat(virtualIdDifferentChild).isNotEqualTo(virtualId); + assertThat(virtualId).isNotEqualTo(virtualIdDifferentChild); + assertThat(virtualIdDifferentChild).isNotEqualTo(realId); + assertThat(realId).isNotEqualTo(virtualIdDifferentChild); + + final AutofillId virtualIdDifferentParent = new AutofillId(108, 1); + assertThat(virtualIdDifferentParent).isNotEqualTo(virtualId); + assertThat(virtualId).isNotEqualTo(virtualIdDifferentParent); + assertThat(virtualIdDifferentParent).isNotEqualTo(virtualIdDifferentChild); + assertThat(virtualIdDifferentChild).isNotEqualTo(virtualIdDifferentParent); + + final AutofillId virtualIdDifferentSession = new AutofillId(new AutofillId(42), 1, 108); + assertThat(virtualIdDifferentSession).isNotEqualTo(virtualId); + assertThat(virtualId).isNotEqualTo(virtualIdDifferentSession); + assertThat(virtualIdDifferentSession).isNotEqualTo(realId); + assertThat(realId).isNotEqualTo(virtualIdDifferentSession); + + final AutofillId sameVirtualIdDifferentSession = new AutofillId(new AutofillId(42), 1, 108); + assertThat(sameVirtualIdDifferentSession).isEqualTo(virtualIdDifferentSession); + assertThat(virtualIdDifferentSession).isEqualTo(sameVirtualIdDifferentSession); + assertThat(sameVirtualIdDifferentSession.hashCode()) + .isEqualTo(virtualIdDifferentSession.hashCode()); + } + + private AutofillId cloneThroughParcel(AutofillId id) { + Parcel parcel = Parcel.obtain(); + + try { + // Write to parcel + parcel.setDataPosition(0); // Sanity / paranoid check + id.writeToParcel(parcel, 0); + + // Read from parcel + parcel.setDataPosition(0); + AutofillId clone = AutofillId.CREATOR.createFromParcel(parcel); + assertThat(clone).isNotNull(); + return clone; + } finally { + parcel.recycle(); + } + } +} diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java index 59f3a4ccc8a2..73cceaeee547 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java @@ -19,11 +19,14 @@ import static com.google.common.truth.Truth.assertThat; import static org.testng.Assert.assertThrows; +import android.view.View; +import android.view.ViewStructure; import android.view.autofill.AutofillId; +import android.view.contentcapture.ViewNode.ViewStructureImpl; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Spy; +import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** @@ -35,34 +38,104 @@ import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ContentCaptureSessionTest { - /** - * Uses a spy as ContentCaptureSession is abstract but (so far) we're testing its concrete - * methods. - */ - @Spy - private ContentCaptureSession mMockSession; + private ContentCaptureSession mSession1 = new MyContentCaptureSession("111"); + + private ContentCaptureSession mSession2 = new MyContentCaptureSession("2222"); + + @Mock + private View mMockView; @Test public void testNewAutofillId_invalid() { - assertThrows(NullPointerException.class, () -> mMockSession.newAutofillId(null, 42)); + assertThrows(NullPointerException.class, () -> mSession1.newAutofillId(null, 42)); assertThrows(IllegalArgumentException.class, - () -> mMockSession.newAutofillId(new AutofillId(42, 42), 42)); + () -> mSession1.newAutofillId(new AutofillId(42, 42), 42)); } @Test public void testNewAutofillId_valid() { final AutofillId parentId = new AutofillId(42); - final AutofillId childId = mMockSession.newAutofillId(parentId, 108); + final AutofillId childId = mSession1.newAutofillId(parentId, 108); assertThat(childId.getViewId()).isEqualTo(42); assertThat(childId.getVirtualChildId()).isEqualTo(108); - // TODO(b/121197119): assert session id + assertThat(childId.getSessionId()).isEqualTo(mSession1.getIdAsInt()); + } + + @Test + public void testNewAutofillId_differentSessions() { + assertThat(mSession1.getIdAsInt()).isNotSameAs(mSession2.getIdAsInt()); //sanity check + final AutofillId parentId = new AutofillId(42); + final AutofillId childId1 = mSession1.newAutofillId(parentId, 108); + final AutofillId childId2 = mSession2.newAutofillId(parentId, 108); + assertThat(childId1).isNotEqualTo(childId2); + assertThat(childId2).isNotEqualTo(childId1); } @Test public void testNotifyXXX_null() { - assertThrows(NullPointerException.class, () -> mMockSession.notifyViewAppeared(null)); - assertThrows(NullPointerException.class, () -> mMockSession.notifyViewDisappeared(null)); + assertThrows(NullPointerException.class, () -> mSession1.notifyViewAppeared(null)); + assertThrows(NullPointerException.class, () -> mSession1.notifyViewDisappeared(null)); assertThrows(NullPointerException.class, - () -> mMockSession.notifyViewTextChanged(null, "whatever", 0)); + () -> mSession1.notifyViewTextChanged(null, "whatever", 0)); + } + + @Test + public void testNewViewStructure() { + assertThat(mMockView.getAutofillId()).isNotNull(); // sanity check + final ViewStructure structure = mSession1.newViewStructure(mMockView); + assertThat(structure).isNotNull(); + assertThat(structure.getAutofillId()).isEqualTo(mMockView.getAutofillId()); + } + + @Test + public void testNewVirtualViewStructure() { + final AutofillId parentId = new AutofillId(42); + final ViewStructure structure = mSession1.newVirtualViewStructure(parentId, 108); + assertThat(structure).isNotNull(); + final AutofillId childId = mSession1.newAutofillId(parentId, 108); + assertThat(structure.getAutofillId()).isEqualTo(childId); + } + + // Cannot use @Spy because we need to pass the session id on constructor + private class MyContentCaptureSession extends ContentCaptureSession { + + private MyContentCaptureSession(String id) { + super(id); + } + + @Override + MainContentCaptureSession getMainCaptureSession() { + throw new UnsupportedOperationException("should not have been called"); + } + + @Override + ContentCaptureSession newChild(ContentCaptureContext context) { + throw new UnsupportedOperationException("should not have been called"); + } + + @Override + void flush() { + throw new UnsupportedOperationException("should not have been called"); + } + + @Override + void onDestroy() { + throw new UnsupportedOperationException("should not have been called"); + } + + @Override + void internalNotifyViewAppeared(ViewStructureImpl node) { + throw new UnsupportedOperationException("should not have been called"); + } + + @Override + void internalNotifyViewDisappeared(AutofillId id) { + throw new UnsupportedOperationException("should not have been called"); + } + + @Override + void internalNotifyViewTextChanged(AutofillId id, CharSequence text, int flags) { + throw new UnsupportedOperationException("should not have been called"); + } } } diff --git a/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java b/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java index 995946b940f8..bbfe01c379f1 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java @@ -110,10 +110,10 @@ public class ViewNodeTest { @Test public void testAutofillIdMethods_explicitIdsConstructor() { AutofillId initialParentId = new AutofillId(42); - ViewStructureImpl structure = new ViewStructureImpl(initialParentId, 108); + ViewStructureImpl structure = new ViewStructureImpl(initialParentId, 108, 666); ViewNode node = structure.getNode(); - assertThat(node.getAutofillId()).isEqualTo(new AutofillId(initialParentId, 108)); + assertThat(node.getAutofillId()).isEqualTo(new AutofillId(initialParentId, 108, 666)); assertThat(node.getParentAutofillId()).isEqualTo(initialParentId); AutofillId newChildId = new AutofillId(108); diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java index 82eaf88fb07a..ec6101c09b7b 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java @@ -377,10 +377,10 @@ public class TextClassifierTest { ConversationActions.Message.PERSON_USER_REMOTE) .setText("Where are you?") .build(); - ConversationActions.TypeConfig typeConfig = - new ConversationActions.TypeConfig.Builder().includeTypesFromTextClassifier(false) + TextClassifier.EntityConfig typeConfig = + new TextClassifier.EntityConfig.Builder().includeTypesFromTextClassifier(false) .setIncludedTypes( - Collections.singletonList(ConversationActions.TYPE_TEXT_REPLY)) + Collections.singletonList(ConversationAction.TYPE_TEXT_REPLY)) .build(); ConversationActions.Request request = new ConversationActions.Request.Builder(Collections.singletonList(message)) @@ -391,10 +391,10 @@ public class TextClassifierTest { ConversationActions conversationActions = mClassifier.suggestConversationActions(request); assertTrue(conversationActions.getConversationActions().size() > 0); assertTrue(conversationActions.getConversationActions().size() == 1); - for (ConversationActions.ConversationAction conversationAction : + for (ConversationAction conversationAction : conversationActions.getConversationActions()) { assertThat(conversationAction, - isConversationAction(ConversationActions.TYPE_TEXT_REPLY)); + isConversationAction(ConversationAction.TYPE_TEXT_REPLY)); } }*/ @@ -406,10 +406,10 @@ public class TextClassifierTest { ConversationActions.Message.PERSON_USER_REMOTE) .setText("Where are you?") .build(); - ConversationActions.TypeConfig typeConfig = - new ConversationActions.TypeConfig.Builder().includeTypesFromTextClassifier(false) + TextClassifier.EntityConfig typeConfig = + new TextClassifier.EntityConfig.Builder().includeTypesFromTextClassifier(false) .setIncludedTypes( - Collections.singletonList(ConversationActions.TYPE_TEXT_REPLY)) + Collections.singletonList(ConversationAction.TYPE_TEXT_REPLY)) .build(); ConversationActions.Request request = new ConversationActions.Request.Builder(Collections.singletonList(message)) @@ -418,10 +418,10 @@ public class TextClassifierTest { ConversationActions conversationActions = mClassifier.suggestConversationActions(request); assertTrue(conversationActions.getConversationActions().size() > 1); - for (ConversationActions.ConversationAction conversationAction : + for (ConversationAction conversationAction : conversationActions.getConversationActions()) { assertThat(conversationAction, - isConversationAction(ConversationActions.TYPE_TEXT_REPLY)); + isConversationAction(ConversationAction.TYPE_TEXT_REPLY)); } } @@ -524,20 +524,19 @@ public class TextClassifierTest { }; } - private static Matcher<ConversationActions.ConversationAction> isConversationAction( - String actionType) { - return new BaseMatcher<ConversationActions.ConversationAction>() { + private static Matcher<ConversationAction> isConversationAction(String actionType) { + return new BaseMatcher<ConversationAction>() { @Override public boolean matches(Object o) { - if (!(o instanceof ConversationActions.ConversationAction)) { + if (!(o instanceof ConversationAction)) { return false; } - ConversationActions.ConversationAction conversationAction = - (ConversationActions.ConversationAction) o; + ConversationAction conversationAction = + (ConversationAction) o; if (!actionType.equals(conversationAction.getType())) { return false; } - if (ConversationActions.TYPE_TEXT_REPLY.equals(actionType)) { + if (ConversationAction.TYPE_TEXT_REPLY.equals(actionType)) { if (conversationAction.getTextReply() == null) { return false; } diff --git a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java index 344f79dcc48e..0597a89e87d4 100644 --- a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java @@ -26,7 +26,7 @@ import static com.google.common.truth.Truth.assertThat; import android.metrics.LogMaker; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; -import android.view.textclassifier.ConversationActions; +import android.view.textclassifier.ConversationAction; import android.view.textclassifier.TextClassificationContext; import android.view.textclassifier.TextClassifierEvent; import android.view.textclassifier.TextClassifierEventTronLogger; @@ -69,7 +69,7 @@ public class TextClassifierEventTronLoggerTest { new TextClassifierEvent.Builder( TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS, TextClassifierEvent.TYPE_SMART_ACTION) - .setEntityType(ConversationActions.TYPE_CALL_PHONE) + .setEntityType(ConversationAction.TYPE_CALL_PHONE) .setEventTime(EVENT_TIME) .setEventContext(textClassificationContext) .build(); @@ -84,7 +84,7 @@ public class TextClassifierEventTronLoggerTest { assertThat(logMaker.getType()).isEqualTo( ACTION_TEXT_SELECTION_SMART_SHARE); assertThat(logMaker.getTaggedData(FIELD_SELECTION_ENTITY_TYPE)) - .isEqualTo(ConversationActions.TYPE_CALL_PHONE); + .isEqualTo(ConversationAction.TYPE_CALL_PHONE); assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_EVENT_TIME)) .isEqualTo(EVENT_TIME); assertThat(logMaker.getPackageName()).isEqualTo(PACKAGE_NAME); diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 0bffa387489b..6ce81481c1d7 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -41,7 +41,39 @@ prebuilt_etc { src: "privapp-permissions-platform.xml", required: [ "privapp_whitelist_com.android.settings.intelligence", - ] + ], +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.carrierconfig", + product_specific: true, + sub_dir: "permissions", + src: "com.android.carrierconfig.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.contacts", + product_specific: true, + sub_dir: "permissions", + src: "com.android.contacts.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.launcher3", + product_specific: true, + sub_dir: "permissions", + src: "com.android.launcher3.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.provision", + product_specific: true, + sub_dir: "permissions", + src: "com.android.provision.xml", + filename_from_src: true, } prebuilt_etc { @@ -60,6 +92,14 @@ prebuilt_etc { } prebuilt_etc { + name: "privapp_whitelist_com.android.storagemanager", + product_specific: true, + sub_dir: "permissions", + src: "com.android.storagemanager.xml", + filename_from_src: true, +} + +prebuilt_etc { name: "privapp_whitelist_com.android.systemui", product_specific: true, sub_dir: "permissions", diff --git a/data/etc/com.android.carrierconfig.xml b/data/etc/com.android.carrierconfig.xml new file mode 100644 index 000000000000..17efb0315040 --- /dev/null +++ b/data/etc/com.android.carrierconfig.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.carrierconfig"> + <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/com.android.contacts.xml b/data/etc/com.android.contacts.xml new file mode 100644 index 000000000000..78eae40348e6 --- /dev/null +++ b/data/etc/com.android.contacts.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.contacts"> + <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/> + <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/com.android.launcher3.xml b/data/etc/com.android.launcher3.xml new file mode 100644 index 000000000000..337e153722ac --- /dev/null +++ b/data/etc/com.android.launcher3.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.launcher3"> + <permission name="android.permission.BIND_APPWIDGET"/> + <permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/> + <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/com.android.provision.xml b/data/etc/com.android.provision.xml new file mode 100644 index 000000000000..05404ef73732 --- /dev/null +++ b/data/etc/com.android.provision.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.provision"> + <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/com.android.storagemanager.xml b/data/etc/com.android.storagemanager.xml new file mode 100644 index 000000000000..e85a82c983df --- /dev/null +++ b/data/etc/com.android.storagemanager.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.storagemanager"> + <permission name="android.permission.DELETE_PACKAGES"/> + <permission name="android.permission.INTERACT_ACROSS_USERS"/> + <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.PACKAGE_USAGE_STATS"/> + <permission name="android.permission.USE_RESERVED_DISK"/> + <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml index 221708b8c94d..3562a8f81408 100644 --- a/data/etc/com.android.systemui.xml +++ b/data/etc/com.android.systemui.xml @@ -44,6 +44,7 @@ <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> <permission name="android.permission.REAL_GET_TASKS"/> <permission name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE"/> + <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" /> <permission name="android.permission.START_ACTIVITY_AS_CALLER"/> <permission name="android.permission.START_TASKS_FROM_RECENTS"/> <permission name="android.permission.STATUS_BAR"/> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 393a2a6fee03..597d14ac286e 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -33,10 +33,6 @@ applications that come with the platform <permission name="android.permission.CRYPT_KEEPER"/> </privapp-permissions> - <privapp-permissions package="com.android.carrierconfig"> - <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> - </privapp-permissions> - <privapp-permissions package="com.android.cellbroadcastreceiver"> <permission name="android.permission.INTERACT_ACROSS_USERS"/> <permission name="android.permission.MANAGE_USERS"/> @@ -45,11 +41,6 @@ applications that come with the platform <permission name="android.permission.RECEIVE_EMERGENCY_BROADCAST"/> </privapp-permissions> - <privapp-permissions package="com.android.contacts"> - <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/> - <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/> - </privapp-permissions> - <privapp-permissions package="com.android.defcontainer"> <permission name="android.permission.ACCESS_CACHE_FILESYSTEM"/> <permission name="android.permission.ALLOCATE_AGGRESSIVE"/> @@ -79,12 +70,6 @@ applications that come with the platform <permission name="android.permission.WRITE_MEDIA_STORAGE"/> </privapp-permissions> - <privapp-permissions package="com.android.launcher3"> - <permission name="android.permission.BIND_APPWIDGET"/> - <permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/> - <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/> - </privapp-permissions> - <privapp-permissions package="com.android.location.fused"> <permission name="android.permission.INSTALL_LOCATION_PROVIDER"/> </privapp-permissions> @@ -238,10 +223,6 @@ applications that come with the platform <permission name="android.permission.USE_RESERVED_DISK"/> </privapp-permissions> - <privapp-permissions package="com.android.provision"> - <permission name="android.permission.WRITE_SECURE_SETTINGS"/> - </privapp-permissions> - <privapp-permissions package="com.android.mainline.networkstack"> <permission name="android.permission.ACCESS_NETWORK_CONDITIONS"/> <permission name="android.permission.CHANGE_BACKGROUND_DATA_SETTING"/> @@ -335,6 +316,7 @@ applications that come with the platform <permission name="android.permission.SET_TIME"/> <permission name="android.permission.SET_TIME_ZONE"/> <permission name="android.permission.SIGNAL_PERSISTENT_PROCESSES"/> + <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" /> <permission name="android.permission.START_TASKS_FROM_RECENTS" /> <permission name="android.permission.STOP_APP_SWITCHES"/> <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/> @@ -349,15 +331,6 @@ applications that come with the platform <permission name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"/> </privapp-permissions> - <privapp-permissions package="com.android.storagemanager"> - <permission name="android.permission.DELETE_PACKAGES"/> - <permission name="android.permission.INTERACT_ACROSS_USERS"/> - <permission name="android.permission.MANAGE_USERS"/> - <permission name="android.permission.PACKAGE_USAGE_STATS"/> - <permission name="android.permission.USE_RESERVED_DISK"/> - <permission name="android.permission.WRITE_SECURE_SETTINGS"/> - </privapp-permissions> - <privapp-permissions package="com.android.tv"> <permission name="android.permission.CHANGE_HDMI_CEC_ACTIVE_SOURCE"/> <permission name="android.permission.DVB_DEVICE"/> diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java index ca9dc475f7a1..65aaba17a55d 100644 --- a/graphics/java/android/graphics/BaseCanvas.java +++ b/graphics/java/android/graphics/BaseCanvas.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.annotation.Size; import android.annotation.UnsupportedAppUsage; import android.graphics.Canvas.VertexMode; +import android.graphics.text.MeasuredText; import android.text.GraphicsOperations; import android.text.MeasuredParagraph; import android.text.PrecomputedText; @@ -554,14 +555,12 @@ public abstract class BaseCanvas { final int paraStart = pt.getParagraphStart(paraIndex); final MeasuredParagraph mp = pt.getMeasuredParagraph(paraIndex); // Only support the text in the same paragraph. - nDrawTextRun(mNativeCanvasWrapper, - mp.getChars(), - start - paraStart, - end - start, - contextStart - paraStart, - contextEnd - contextStart, - x, y, isRtl, paint.getNativeInstance(), - mp.getMeasuredText().getNativePtr()); + drawTextRun(mp.getMeasuredText(), + start - paraStart, + end - paraStart, + contextStart - paraStart, + contextEnd - paraStart, + x, y, isRtl, paint); return; } } @@ -576,6 +575,14 @@ public abstract class BaseCanvas { } } + public void drawTextRun(@NonNull MeasuredText measuredText, int start, int end, + int contextStart, int contextEnd, float x, float y, boolean isRtl, + @NonNull Paint paint) { + nDrawTextRun(mNativeCanvasWrapper, measuredText.getChars(), start, end - start, + contextStart, contextEnd - contextStart, x, y, isRtl, paint.getNativeInstance(), + measuredText.getNativePtr()); + } + public void drawVertices(@NonNull VertexMode mode, int vertexCount, @NonNull float[] verts, int vertOffset, @Nullable float[] texs, int texOffset, @Nullable int[] colors, int colorOffset, @Nullable short[] indices, int indexOffset, int indexCount, diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java index 901c2116884b..4f6093500cad 100644 --- a/graphics/java/android/graphics/BaseRecordingCanvas.java +++ b/graphics/java/android/graphics/BaseRecordingCanvas.java @@ -20,6 +20,7 @@ import android.annotation.ColorInt; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Size; +import android.graphics.text.MeasuredText; import android.text.GraphicsOperations; import android.text.MeasuredParagraph; import android.text.PrecomputedText; @@ -522,14 +523,12 @@ public class BaseRecordingCanvas extends Canvas { final int paraStart = pt.getParagraphStart(paraIndex); final MeasuredParagraph mp = pt.getMeasuredParagraph(paraIndex); // Only support if the target is in the same paragraph. - nDrawTextRun(mNativeCanvasWrapper, - mp.getChars(), + drawTextRun(mp.getMeasuredText(), start - paraStart, - end - start, + end - paraStart, contextStart - paraStart, - contextEnd - contextStart, - x, y, isRtl, paint.getNativeInstance(), - mp.getMeasuredText().getNativePtr()); + contextEnd - paraStart, + x, y, isRtl, paint); return; } } @@ -545,6 +544,15 @@ public class BaseRecordingCanvas extends Canvas { } @Override + public void drawTextRun(@NonNull MeasuredText measuredText, int start, int end, + int contextStart, int contextEnd, float x, float y, boolean isRtl, + @NonNull Paint paint) { + nDrawTextRun(mNativeCanvasWrapper, measuredText.getChars(), start, end - start, + contextStart, contextEnd - contextStart, x, y, isRtl, paint.getNativeInstance(), + measuredText.getNativePtr()); + } + + @Override public final void drawVertices(@NonNull VertexMode mode, int vertexCount, @NonNull float[] verts, int vertOffset, @Nullable float[] texs, int texOffset, @Nullable int[] colors, int colorOffset, @Nullable short[] indices, int indexOffset, diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 790b37eec4c5..30f0bfa68b4a 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -18,9 +18,11 @@ package android.graphics; import android.annotation.CheckResult; import android.annotation.ColorInt; +import android.annotation.ColorLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Size; +import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.annotation.WorkerThread; import android.content.res.ResourcesImpl; @@ -1780,6 +1782,30 @@ public final class Bitmap implements Parcelable { } /** + * Fills the bitmap's pixels with the specified {@link Color}. + * + * @throws IllegalStateException if the bitmap is not mutable. + * @throws IllegalArgumentException if the color space encoded in the long + * is invalid or unknown. + * + * @hide pending API approval + */ + @TestApi + public void eraseColor(@ColorLong long c) { + checkRecycled("Can't erase a recycled bitmap"); + if (!isMutable()) { + throw new IllegalStateException("cannot erase immutable bitmaps"); + } + + ColorSpace cs = Color.colorSpace(c); + float r = Color.red(c); + float g = Color.green(c); + float b = Color.blue(c); + float a = Color.alpha(c); + nativeErase(mNativePtr, cs, r, g, b, a); + } + + /** * Returns the {@link Color} at the specified location. Throws an exception * if x or y are out of bounds (negative or >= to the width or height * respectively). The returned color is a non-premultiplied ARGB value in @@ -2123,6 +2149,8 @@ public final class Bitmap implements Parcelable { int quality, OutputStream stream, byte[] tempStorage); private static native void nativeErase(long nativeBitmap, int color); + private static native void nativeErase(long nativeBitmap, ColorSpace cs, + float r, float g, float b, float a); private static native int nativeRowBytes(long nativeBitmap); private static native int nativeConfig(long nativeBitmap); diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index 63a806e53556..8c1bae2a0527 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Size; import android.annotation.UnsupportedAppUsage; +import android.graphics.text.MeasuredText; import android.os.Build; import dalvik.annotation.optimization.CriticalNative; @@ -2122,7 +2123,8 @@ public class Canvas extends BaseCanvas { * the text next to it. * <p> * All text outside the range {@code contextStart..contextEnd} is ignored. The text between - * {@code start} and {@code end} will be laid out and drawn. + * {@code start} and {@code end} will be laid out and drawn. The context range is useful for + * contextual shaping, e.g. Kerning, Arabic contextural form. * <p> * The direction of the run is explicitly specified by {@code isRtl}. Thus, this method is * suitable only for runs of a single direction. Alignment of the text is as determined by the @@ -2151,6 +2153,31 @@ public class Canvas extends BaseCanvas { } /** + * Draw a run of text, all in a single direction, with optional context for complex text + * shaping. + * <p> + * See {@link #drawTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)} for + * more details. This method uses a {@link MeasuredText} rather than CharSequence to represent + * the string. + * + * @param text the text to render + * @param start the start of the text to render. Data before this position can be used for + * shaping context. + * @param end the end of the text to render. Data at or after this position can be used for + * shaping context. + * @param contextStart the index of the start of the shaping context + * @param contextEnd the index of the end of the shaping context + * @param x the x position at which to draw the text + * @param y the y position at which to draw the text + * @param isRtl whether the run is in RTL direction + * @param paint the paint + */ + public void drawTextRun(@NonNull MeasuredText text, int start, int end, int contextStart, + int contextEnd, float x, float y, boolean isRtl, @NonNull Paint paint) { + super.drawTextRun(text, start, end, contextStart, contextEnd, x, y, isRtl, paint); + } + + /** * Draw the array of vertices, interpreted as triangles (based on mode). The verts array is * required, and specifies the x,y pairs for each vertex. If texs is non-null, then it is used * to specify the coordinate in shader coordinates to use at each vertex (the paint must have a diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java index 95317a4d0bf2..9fa70a5ab19c 100644 --- a/graphics/java/android/graphics/ColorSpace.java +++ b/graphics/java/android/graphics/ColorSpace.java @@ -1661,10 +1661,12 @@ public abstract class ColorSpace { * @param rhs 3x3 matrix, as a non-null array of 9 floats * @return A new array of 9 floats containing the result of the multiplication * of rhs by lhs + * + * @hide */ @NonNull @Size(9) - private static float[] mul3x3(@NonNull @Size(9) float[] lhs, @NonNull @Size(9) float[] rhs) { + public static float[] mul3x3(@NonNull @Size(9) float[] lhs, @NonNull @Size(9) float[] rhs) { float[] r = new float[9]; r[0] = lhs[0] * rhs[0] + lhs[3] * rhs[1] + lhs[6] * rhs[2]; r[1] = lhs[1] * rhs[0] + lhs[4] * rhs[1] + lhs[7] * rhs[2]; diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index ad9ec02648b1..3c35d9b33fc8 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -20,8 +20,9 @@ #include <algorithm> #include <iterator> -#include <set> #include <map> +#include <set> +#include <sstream> #include "android-base/logging.h" #include "android-base/stringprintf.h" @@ -372,6 +373,9 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri uint32_t best_offset = 0u; uint32_t type_flags = 0u; + Resolution::Step::Type resolution_type; + std::vector<Resolution::Step> resolution_steps; + // If desired_config is the same as the set configuration, then we can use our filtered list // and we don't need to match the configurations, since they already matched. const bool use_fast_path = desired_config == &configuration_; @@ -403,8 +407,8 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri // If the package is an overlay, then even configurations that are the same MUST be chosen. const bool package_is_overlay = loaded_package->IsOverlay(); - const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx]; if (use_fast_path) { + const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx]; const std::vector<ResTable_config>& candidate_configs = filtered_group.configurations; const size_t type_count = candidate_configs.size(); for (uint32_t i = 0; i < type_count; i++) { @@ -412,21 +416,34 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri // We can skip calling ResTable_config::match() because we know that all candidate // configurations that do NOT match have been filtered-out. - if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || - (package_is_overlay && this_config.compare(*best_config) == 0)) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const ResTable_type* type_chunk = filtered_group.types[i]; - const uint32_t offset = LoadedPackage::GetEntryOffset(type_chunk, local_entry_idx); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } + if (best_config == nullptr) { + resolution_type = Resolution::Step::Type::INITIAL; + } else if (this_config.isBetterThan(*best_config, desired_config)) { + resolution_type = Resolution::Step::Type::BETTER_MATCH; + } else if (package_is_overlay && this_config.compare(*best_config) == 0) { + resolution_type = Resolution::Step::Type::OVERLAID; + } else { + continue; + } + + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const ResTable_type* type = filtered_group.types[i]; + const uint32_t offset = LoadedPackage::GetEntryOffset(type, local_entry_idx); + if (offset == ResTable_type::NO_ENTRY) { + continue; + } - best_cookie = cookie; - best_package = loaded_package; - best_type = type_chunk; - best_config = &this_config; - best_offset = offset; + best_cookie = cookie; + best_package = loaded_package; + best_type = type; + best_config = &this_config; + best_offset = offset; + + if (resource_resolution_logging_enabled_) { + resolution_steps.push_back(Resolution::Step{resolution_type, + this_config.toString(), + &loaded_package->GetPackageName()}); } } } else { @@ -440,23 +457,38 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri ResTable_config this_config; this_config.copyFromDtoH((*iter)->config); - if (this_config.match(*desired_config)) { - if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || - (package_is_overlay && this_config.compare(*best_config) == 0)) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } + if (!this_config.match(*desired_config)) { + continue; + } - best_cookie = cookie; - best_package = loaded_package; - best_type = *iter; - best_config_copy = this_config; - best_config = &best_config_copy; - best_offset = offset; - } + if (best_config == nullptr) { + resolution_type = Resolution::Step::Type::INITIAL; + } else if (this_config.isBetterThan(*best_config, desired_config)) { + resolution_type = Resolution::Step::Type::BETTER_MATCH; + } else if (package_is_overlay && this_config.compare(*best_config) == 0) { + resolution_type = Resolution::Step::Type::OVERLAID; + } else { + continue; + } + + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx); + if (offset == ResTable_type::NO_ENTRY) { + continue; + } + + best_cookie = cookie; + best_package = loaded_package; + best_type = *iter; + best_config_copy = this_config; + best_config = &best_config_copy; + best_offset = offset; + + if (resource_resolution_logging_enabled_) { + resolution_steps.push_back(Resolution::Step{resolution_type, + this_config.toString(), + &loaded_package->GetPackageName()}); } } } @@ -478,9 +510,95 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri out_entry->entry_string_ref = StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); out_entry->dynamic_ref_table = &package_group.dynamic_ref_table; + + if (resource_resolution_logging_enabled_) { + last_resolution.resid = resid; + last_resolution.cookie = best_cookie; + last_resolution.steps = resolution_steps; + + // Cache only the type/entry refs since that's all that's needed to build name + last_resolution.type_string_ref = + StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1); + last_resolution.entry_string_ref = + StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); + } + return best_cookie; } +void AssetManager2::SetResourceResolutionLoggingEnabled(bool enabled) { + resource_resolution_logging_enabled_ = enabled; + + if (!enabled) { + last_resolution.cookie = kInvalidCookie; + last_resolution.resid = 0; + last_resolution.steps.clear(); + last_resolution.type_string_ref = StringPoolRef(); + last_resolution.entry_string_ref = StringPoolRef(); + } +} + +std::string AssetManager2::GetLastResourceResolution() const { + if (!resource_resolution_logging_enabled_) { + LOG(ERROR) << "Must enable resource resolution logging before getting path."; + return std::string(); + } + + auto cookie = last_resolution.cookie; + if (cookie == kInvalidCookie) { + LOG(ERROR) << "AssetManager hasn't resolved a resource to read resolution path."; + return std::string(); + } + + uint32_t resid = last_resolution.resid; + std::vector<Resolution::Step>& steps = last_resolution.steps; + + ResourceName resource_name; + std::string resource_name_string; + + const LoadedPackage* package = + apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid)); + + if (package != nullptr) { + ToResourceName(last_resolution.type_string_ref, + last_resolution.entry_string_ref, + package, + &resource_name); + resource_name_string = ToFormattedResourceString(&resource_name); + } + + std::stringstream log_stream; + log_stream << base::StringPrintf("Resolution for 0x%08x ", resid) + << resource_name_string + << "\n\tFor config -" + << configuration_.toString(); + + std::string prefix; + for (Resolution::Step step : steps) { + switch (step.type) { + case Resolution::Step::Type::INITIAL: + prefix = "Found initial"; + break; + case Resolution::Step::Type::BETTER_MATCH: + prefix = "Found better"; + break; + case Resolution::Step::Type::OVERLAID: + prefix = "Overlaid"; + break; + } + + if (!prefix.empty()) { + log_stream << "\n\t" << prefix << ": " << *step.package_name; + + if (!step.config_name.isEmpty()) { + log_stream << " -" << step.config_name; + } + } + } + + return log_stream.str(); +} + bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const { FindEntryResult entry; ApkAssetsCookie cookie = @@ -495,27 +613,10 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) cons return false; } - out_name->package = package->GetPackageName().data(); - out_name->package_len = package->GetPackageName().size(); - - out_name->type = entry.type_string_ref.string8(&out_name->type_len); - out_name->type16 = nullptr; - if (out_name->type == nullptr) { - out_name->type16 = entry.type_string_ref.string16(&out_name->type_len); - if (out_name->type16 == nullptr) { - return false; - } - } - - out_name->entry = entry.entry_string_ref.string8(&out_name->entry_len); - out_name->entry16 = nullptr; - if (out_name->entry == nullptr) { - out_name->entry16 = entry.entry_string_ref.string16(&out_name->entry_len); - if (out_name->entry16 == nullptr) { - return false; - } - } - return true; + return ToResourceName(entry.type_string_ref, + entry.entry_string_ref, + package, + out_name); } bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const { diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp index 57e3491895e6..3dc1f2cd56c5 100644 --- a/libs/androidfw/AttributeResolution.cpp +++ b/libs/androidfw/AttributeResolution.cpp @@ -51,7 +51,7 @@ class XmlAttributeFinder class BagAttributeFinder : public BackTrackingAttributeFinder<BagAttributeFinder, const ResolvedBag::Entry*> { public: - BagAttributeFinder(const ResolvedBag* bag) + explicit BagAttributeFinder(const ResolvedBag* bag) : BackTrackingAttributeFinder(bag != nullptr ? bag->entries : nullptr, bag != nullptr ? bag->entries + bag->entry_count : nullptr) { } diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp index 5694115f61aa..a99e77f8dbb9 100644 --- a/libs/androidfw/CursorWindow.cpp +++ b/libs/androidfw/CursorWindow.cpp @@ -94,7 +94,7 @@ status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursor if (size < 0) { result = UNKNOWN_ERROR; } else { - int dupAshmemFd = ::dup(ashmemFd); + int dupAshmemFd = ::fcntl(ashmemFd, F_DUPFD_CLOEXEC, 0); if (dupAshmemFd < 0) { result = -errno; } else { diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 5a267804ddf1..70ce9bc705ef 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -593,7 +593,12 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, return {}; } - // Iterate over the overlayable policy chunks + std::string name; + util::ReadUtf16StringFromDevice(header->name, arraysize(header->name), &name); + std::string actor; + util::ReadUtf16StringFromDevice(header->actor, arraysize(header->actor), &actor); + + // Iterate over the overlayable policy chunks contained within the overlayable chunk data ChunkIterator overlayable_iter(child_chunk.data_ptr(), child_chunk.data_size()); while (overlayable_iter.HasNext()) { const Chunk overlayable_child_chunk = overlayable_iter.Next(); @@ -613,7 +618,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, return {}; } - // Retrieve all the ids belonging to this policy + // Retrieve all the resource ids belonging to this policy chunk std::unordered_set<uint32_t> ids; const auto ids_begin = reinterpret_cast<const ResTable_ref*>(overlayable_child_chunk.data_ptr()); @@ -622,8 +627,10 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, ids.insert(dtohl(id_iter->ident)); } - // Add the pairing of overlayable properties to resource ids to the package + // Add the pairing of overlayable properties and resource ids to the package OverlayableInfo overlayable_info{}; + overlayable_info.name = name; + overlayable_info.actor = actor; overlayable_info.policy_flags = policy_header->policy_flags; loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids)); break; @@ -636,7 +643,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, } if (overlayable_iter.HadError()) { - LOG(ERROR) << StringPrintf("Error parsing RES_TABLE_OVERLAYABLE_POLICY_TYPE: %s", + LOG(ERROR) << StringPrintf("Error parsing RES_TABLE_OVERLAYABLE_TYPE: %s", overlayable_iter.GetLastError().c_str()); if (overlayable_iter.HadFatalError()) { return {}; diff --git a/libs/androidfw/ResourceUtils.cpp b/libs/androidfw/ResourceUtils.cpp index d63feb01ef83..645984d85c34 100644 --- a/libs/androidfw/ResourceUtils.cpp +++ b/libs/androidfw/ResourceUtils.cpp @@ -48,4 +48,65 @@ bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, Strin !(has_type_separator && out_type->empty()); } +bool ToResourceName(StringPoolRef& type_string_ref, + StringPoolRef& entry_string_ref, + const LoadedPackage* package, + AssetManager2::ResourceName* out_name) { + out_name->package = package->GetPackageName().data(); + out_name->package_len = package->GetPackageName().size(); + + out_name->type = type_string_ref.string8(&out_name->type_len); + out_name->type16 = nullptr; + if (out_name->type == nullptr) { + out_name->type16 = type_string_ref.string16(&out_name->type_len); + if (out_name->type16 == nullptr) { + return false; + } + } + + out_name->entry = entry_string_ref.string8(&out_name->entry_len); + out_name->entry16 = nullptr; + if (out_name->entry == nullptr) { + out_name->entry16 = entry_string_ref.string16(&out_name->entry_len); + if (out_name->entry16 == nullptr) { + return false; + } + } + + return true; +} + +std::string ToFormattedResourceString(AssetManager2::ResourceName* resource_name) { + std::string result; + if (resource_name->package != nullptr) { + result.append(resource_name->package, resource_name->package_len); + } + + if (resource_name->type != nullptr || resource_name->type16 != nullptr) { + if (!result.empty()) { + result += ":"; + } + + if (resource_name->type != nullptr) { + result.append(resource_name->type, resource_name->type_len); + } else { + result += util::Utf16ToUtf8(StringPiece16(resource_name->type16, resource_name->type_len)); + } + } + + if (resource_name->entry != nullptr || resource_name->entry16 != nullptr) { + if (!result.empty()) { + result += "/"; + } + + if (resource_name->entry != nullptr) { + result.append(resource_name->entry, resource_name->entry_len); + } else { + result += util::Utf16ToUtf8(StringPiece16(resource_name->entry16, resource_name->entry_len)); + } + } + + return result; +} + } // namespace android diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp index 5d243da2097c..5be2105fe404 100644 --- a/libs/androidfw/ZipUtils.cpp +++ b/libs/androidfw/ZipUtils.cpp @@ -37,7 +37,7 @@ using namespace android; // TODO: This can go away once the only remaining usage in aapt goes away. class FileReader : public zip_archive::Reader { public: - FileReader(FILE* fp) : Reader(), mFp(fp), mCurrentOffset(0) { + explicit FileReader(FILE* fp) : Reader(), mFp(fp), mCurrentOffset(0) { } bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const { diff --git a/libs/androidfw/include/androidfw/AssetDir.h b/libs/androidfw/include/androidfw/AssetDir.h index 7aef02dc4133..ce6e066d6a16 100644 --- a/libs/androidfw/include/androidfw/AssetDir.h +++ b/libs/androidfw/include/androidfw/AssetDir.h @@ -78,7 +78,7 @@ private: class FileInfo { public: FileInfo(void) {} - FileInfo(const String8& path) // useful for e.g. svect.indexOf + explicit FileInfo(const String8& path) // useful for e.g. svect.indexOf : mFileName(path), mFileType(kFileTypeUnknown) {} ~FileInfo(void) {} diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 0d492984d41d..f29769b834d1 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -229,6 +229,14 @@ class AssetManager2 { ResTable_config* in_out_selected_config, uint32_t* in_out_flags, uint32_t* out_last_reference) const; + // Enables or disables resource resolution logging. Clears stored steps when + // disabled. + void SetResourceResolutionLoggingEnabled(bool enabled); + + // Returns formatted log of last resource resolution path, or empty if no + // resource has been resolved yet. + std::string GetLastResourceResolution() const; + // Retrieves the best matching bag/map resource with ID `resid`. // This method will resolve all parent references for this bag and merge keys with the child. // To iterate over the keys, use the following idiom: @@ -346,6 +354,48 @@ class AssetManager2 { // Cached set of bags. These are cached because they can inherit keys from parent bags, // which involves some calculation. std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_; + + // Whether or not to save resource resolution steps + bool resource_resolution_logging_enabled_ = false; + + struct Resolution { + + struct Step { + + enum class Type { + INITIAL, + BETTER_MATCH, + OVERLAID + }; + + // Marks what kind of override this step was. + Type type; + + // Built name of configuration for this step. + String8 config_name; + + // Marks the package name of the better resource found in this step. + const std::string* package_name; + }; + + // Last resolved resource ID. + uint32_t resid; + + // Last resolved resource result cookie. + ApkAssetsCookie cookie = kInvalidCookie; + + // Last resolved resource type. + StringPoolRef type_string_ref; + + // Last resolved resource entry. + StringPoolRef entry_string_ref; + + // Steps taken to resolve last resource. + std::vector<Step> steps; + }; + + // Record of the last resolved resource's resolution path. + mutable Resolution last_resolution; }; class Theme { diff --git a/libs/androidfw/include/androidfw/BackupHelpers.h b/libs/androidfw/include/androidfw/BackupHelpers.h index fc1ad4717c16..2da247b77c0a 100644 --- a/libs/androidfw/include/androidfw/BackupHelpers.h +++ b/libs/androidfw/include/androidfw/BackupHelpers.h @@ -67,7 +67,7 @@ struct FileRec { class BackupDataWriter { public: - BackupDataWriter(int fd); + explicit BackupDataWriter(int fd); // does not close fd ~BackupDataWriter(); @@ -104,7 +104,7 @@ private: class BackupDataReader { public: - BackupDataReader(int fd); + explicit BackupDataReader(int fd); // does not close fd ~BackupDataReader(); diff --git a/libs/androidfw/include/androidfw/ConfigDescription.h b/libs/androidfw/include/androidfw/ConfigDescription.h index 29424c4462aa..6fa089aeb12c 100644 --- a/libs/androidfw/include/androidfw/ConfigDescription.h +++ b/libs/androidfw/include/androidfw/ConfigDescription.h @@ -82,7 +82,7 @@ struct ConfigDescription : public ResTable_config { static void ApplyVersionForCompatibility(ConfigDescription* config); ConfigDescription(); - ConfigDescription(const android::ResTable_config& o); // NOLINT(implicit) + ConfigDescription(const android::ResTable_config& o); // NOLINT(google-explicit-constructor) ConfigDescription(const ConfigDescription& o); ConfigDescription(ConfigDescription&& o) noexcept; diff --git a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h index e1dfb9490c60..bf35aa3c15bb 100644 --- a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h +++ b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h @@ -22,7 +22,7 @@ namespace android { class DisplayEventDispatcher : public LooperCallback { public: - DisplayEventDispatcher(const sp<Looper>& looper, + explicit DisplayEventDispatcher(const sp<Looper>& looper, ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp); status_t initialize(); diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 8c5c3b7d3858..be62f30617bf 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -78,6 +78,8 @@ struct TypeSpec { using TypeSpecPtr = util::unique_cptr<TypeSpec>; struct OverlayableInfo { + std::string name; + std::string actor; uint32_t policy_flags; }; diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index cf2d8fb3251c..6b9ebd3e8d12 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -693,7 +693,7 @@ class ResXMLTree; class ResXMLParser { public: - ResXMLParser(const ResXMLTree& tree); + explicit ResXMLParser(const ResXMLTree& tree); enum event_code_t { BAD_DOCUMENT = -1, @@ -806,7 +806,7 @@ public: * The tree stores a clone of the specified DynamicRefTable, so any changes to the original * DynamicRefTable will not affect this tree after instantiation. **/ - ResXMLTree(const DynamicRefTable* dynamicRefTable); + explicit ResXMLTree(const DynamicRefTable* dynamicRefTable); ResXMLTree(); ~ResXMLTree(); @@ -1611,6 +1611,12 @@ struct ResTable_lib_entry struct ResTable_overlayable_header { struct ResChunk_header header; + + // The name of the overlayable set of resources that overlays target. + uint16_t name[256]; + + // The component responsible for enabling and disabling overlays targeting this chunk. + uint16_t actor[256]; }; /** @@ -1844,7 +1850,7 @@ public: class Theme { public: - Theme(const ResTable& table); + explicit Theme(const ResTable& table); ~Theme(); inline const ResTable& getResTable() const { return mTable; } diff --git a/libs/androidfw/include/androidfw/ResourceUtils.h b/libs/androidfw/include/androidfw/ResourceUtils.h index d94779bf5225..eb6eb8e66175 100644 --- a/libs/androidfw/include/androidfw/ResourceUtils.h +++ b/libs/androidfw/include/androidfw/ResourceUtils.h @@ -17,6 +17,7 @@ #ifndef ANDROIDFW_RESOURCEUTILS_H #define ANDROIDFW_RESOURCEUTILS_H +#include "androidfw/AssetManager2.h" #include "androidfw/StringPiece.h" namespace android { @@ -27,6 +28,17 @@ namespace android { bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, StringPiece* out_type, StringPiece* out_entry); +// Convert a type_string_ref, entry_string_ref, and package +// to AssetManager2::ResourceName. Useful for getting +// resource name without re-running AssetManager2::FindEntry searches. +bool ToResourceName(StringPoolRef& type_string_ref, + StringPoolRef& entry_string_ref, + const LoadedPackage* package, + AssetManager2::ResourceName* out_name); + +// Formats a ResourceName to "package:type/entry_name". +std::string ToFormattedResourceString(AssetManager2::ResourceName* resource_name); + inline uint32_t fix_package_id(uint32_t resid, uint8_t package_id) { return (resid & 0x00ffffffu) | (static_cast<uint32_t>(package_id) << 24); } diff --git a/libs/androidfw/include/androidfw/StringPiece.h b/libs/androidfw/include/androidfw/StringPiece.h index a33865f4d34f..921877dc4982 100644 --- a/libs/androidfw/include/androidfw/StringPiece.h +++ b/libs/androidfw/include/androidfw/StringPiece.h @@ -52,8 +52,8 @@ class BasicStringPiece { BasicStringPiece(); BasicStringPiece(const BasicStringPiece<TChar>& str); - BasicStringPiece(const std::basic_string<TChar>& str); // NOLINT(implicit) - BasicStringPiece(const TChar* str); // NOLINT(implicit) + BasicStringPiece(const std::basic_string<TChar>& str); // NOLINT(google-explicit-constructor) + BasicStringPiece(const TChar* str); // NOLINT(google-explicit-constructor) BasicStringPiece(const TChar* str, size_t len); BasicStringPiece<TChar>& operator=(const BasicStringPiece<TChar>& rhs); diff --git a/libs/androidfw/include/androidfw/TypeWrappers.h b/libs/androidfw/include/androidfw/TypeWrappers.h index 5cfe54e5759d..fb2fad619011 100644 --- a/libs/androidfw/include/androidfw/TypeWrappers.h +++ b/libs/androidfw/include/androidfw/TypeWrappers.h @@ -23,7 +23,7 @@ namespace android { struct TypeVariant { - TypeVariant(const ResTable_type* data); + explicit TypeVariant(const ResTable_type* data); class iterator { public: diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h index 10d088e02829..aa1466fde778 100644 --- a/libs/androidfw/include/androidfw/Util.h +++ b/libs/androidfw/include/androidfw/Util.h @@ -46,7 +46,7 @@ class unique_cptr { using pointer = typename std::add_pointer<T>::type; constexpr unique_cptr() : ptr_(nullptr) {} - constexpr unique_cptr(std::nullptr_t) : ptr_(nullptr) {} + constexpr explicit unique_cptr(std::nullptr_t) : ptr_(nullptr) {} explicit unique_cptr(pointer ptr) : ptr_(ptr) {} unique_cptr(unique_cptr&& o) noexcept : ptr_(o.ptr_) { o.ptr_ = nullptr; } diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 5449a54d08de..105dcd209bf7 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -586,4 +586,111 @@ TEST_F(AssetManager2Test, OpenDirFromManyApks) { EXPECT_THAT(asset_dir->getFileType(2), Eq(FileType::kFileTypeDirectory)); } +TEST_F(AssetManager2Test, GetLastPathWithoutEnablingReturnsEmpty) { + ResTable_config desired_config; + + AssetManager2 assetmanager; + assetmanager.SetConfiguration(desired_config); + assetmanager.SetApkAssets({basic_assets_.get()}); + assetmanager.SetResourceResolutionLoggingEnabled(false); + + Res_value value; + ResTable_config selected_config; + uint32_t flags; + + ApkAssetsCookie cookie = + assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/, + 0 /*density_override*/, &value, &selected_config, &flags); + ASSERT_NE(kInvalidCookie, cookie); + + auto result = assetmanager.GetLastResourceResolution(); + EXPECT_EQ("", result); +} + +TEST_F(AssetManager2Test, GetLastPathWithoutResolutionReturnsEmpty) { + ResTable_config desired_config; + + AssetManager2 assetmanager; + assetmanager.SetConfiguration(desired_config); + assetmanager.SetApkAssets({basic_assets_.get()}); + + auto result = assetmanager.GetLastResourceResolution(); + EXPECT_EQ("", result); +} + +TEST_F(AssetManager2Test, GetLastPathWithSingleApkAssets) { + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); + desired_config.language[0] = 'd'; + desired_config.language[1] = 'e'; + + AssetManager2 assetmanager; + assetmanager.SetResourceResolutionLoggingEnabled(true); + assetmanager.SetConfiguration(desired_config); + assetmanager.SetApkAssets({basic_assets_.get()}); + + Res_value value; + ResTable_config selected_config; + uint32_t flags; + + ApkAssetsCookie cookie = + assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/, + 0 /*density_override*/, &value, &selected_config, &flags); + ASSERT_NE(kInvalidCookie, cookie); + + auto result = assetmanager.GetLastResourceResolution(); + EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n\tFor config -de\n\tFound initial: com.android.basic", result); +} + +TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) { + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); + desired_config.language[0] = 'd'; + desired_config.language[1] = 'e'; + + AssetManager2 assetmanager; + assetmanager.SetResourceResolutionLoggingEnabled(true); + assetmanager.SetConfiguration(desired_config); + assetmanager.SetApkAssets({basic_assets_.get(), basic_de_fr_assets_.get()}); + + Res_value value = Res_value(); + ResTable_config selected_config; + uint32_t flags; + + ApkAssetsCookie cookie = + assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/, + 0 /*density_override*/, &value, &selected_config, &flags); + ASSERT_NE(kInvalidCookie, cookie); + + auto result = assetmanager.GetLastResourceResolution(); + EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n\tFor config -de\n\tFound initial: com.android.basic\n\tFound better: com.android.basic -de", result); +} + +TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) { + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); + + AssetManager2 assetmanager; + assetmanager.SetResourceResolutionLoggingEnabled(true); + assetmanager.SetConfiguration(desired_config); + assetmanager.SetApkAssets({basic_assets_.get()}); + + Res_value value = Res_value(); + ResTable_config selected_config; + uint32_t flags; + + ApkAssetsCookie cookie = + assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/, + 0 /*density_override*/, &value, &selected_config, &flags); + ASSERT_NE(kInvalidCookie, cookie); + + auto resultEnabled = assetmanager.GetLastResourceResolution(); + ASSERT_NE("", resultEnabled); + + assetmanager.SetResourceResolutionLoggingEnabled(false); + + auto resultDisabled = assetmanager.GetLastResourceResolution(); + EXPECT_EQ("", resultDisabled); +} + } // namespace android diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index 22d587a7f5c4..2e386a083185 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -294,22 +294,30 @@ TEST(LoadedArscTest, LoadOverlayable) { info = package->GetOverlayableInfo(overlayable::R::string::overlayable1); ASSERT_THAT(info, NotNull()); + EXPECT_THAT(info->name, Eq("OverlayableResources1")); + EXPECT_THAT(info->actor, Eq("overlay://theme")); EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC)); info = package->GetOverlayableInfo(overlayable::R::string::overlayable2); ASSERT_THAT(info, NotNull()); + EXPECT_THAT(info->name, Eq("OverlayableResources1")); + EXPECT_THAT(info->actor, Eq("overlay://theme")); EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION)); info = package->GetOverlayableInfo(overlayable::R::string::overlayable3); ASSERT_THAT(info, NotNull()); + EXPECT_THAT(info->name, Eq("OverlayableResources2")); + EXPECT_THAT(info->actor, Eq("overlay://com.android.overlayable")); EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION | ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION)); info = package->GetOverlayableInfo(overlayable::R::string::overlayable4); + EXPECT_THAT(info->name, Eq("OverlayableResources1")); + EXPECT_THAT(info->actor, Eq("overlay://theme")); ASSERT_THAT(info, NotNull()); EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC)); } diff --git a/libs/androidfw/tests/data/overlayable/overlayable.apk b/libs/androidfw/tests/data/overlayable/overlayable.apk Binary files differindex 85ab4be7a2e5..863474794d00 100644 --- a/libs/androidfw/tests/data/overlayable/overlayable.apk +++ b/libs/androidfw/tests/data/overlayable/overlayable.apk diff --git a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml index 11aa7354901d..dba7b08628f1 100644 --- a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml +++ b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml @@ -15,7 +15,7 @@ --> <resources> -<overlayable> +<overlayable name="OverlayableResources1" actor="overlay://theme"> <!-- Any overlay can overlay the value of @string/overlayable1 --> <item type="string" name="overlayable1" /> @@ -31,9 +31,9 @@ </policy> </overlayable> -<overlayable> +<overlayable name="OverlayableResources2" actor="overlay://com.android.overlayable"> <!-- Any overlay on the product_services, vendor, or product partition can overlay the value of - @string/overlayable3 --> + @string/overlayable3 --> <policy type="product_services|vendor|product"> <item type="string" name="overlayable3" /> </policy> diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp index ed167e57158e..56b1885de820 100644 --- a/libs/hwui/DeviceInfo.cpp +++ b/libs/hwui/DeviceInfo.cpp @@ -79,8 +79,7 @@ static void queryWideColorGamutPreference(SkColorSpace::Gamut* colorGamut, switch (wcgDataspace) { case ui::Dataspace::DISPLAY_P3: *colorGamut = SkColorSpace::Gamut::kDCIP3_D65_Gamut; - *colorSpace = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, - SkColorSpace::Gamut::kDCIP3_D65_Gamut); + *colorSpace = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3); break; case ui::Dataspace::V0_SCRGB: *colorGamut = SkColorSpace::Gamut::kSRGB_Gamut; diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp index 562a3b225e36..1661905eff57 100644 --- a/libs/hwui/pipeline/skia/ShaderCache.cpp +++ b/libs/hwui/pipeline/skia/ShaderCache.cpp @@ -23,6 +23,7 @@ #include "FileBlobCache.h" #include "Properties.h" #include "utils/TraceUtils.h" +#include <GrContext.h> namespace android { namespace uirenderer { @@ -168,6 +169,24 @@ void ShaderCache::store(const SkData& key, const SkData& data) { const void* value = data.data(); BlobCache* bc = getBlobCacheLocked(); + if (mInStoreVkPipelineInProgress) { + if (mOldPipelineCacheSize == -1) { + // Record the initial pipeline cache size stored in the file. + mOldPipelineCacheSize = bc->get(key.data(), keySize, nullptr, 0); + } + if (mNewPipelineCacheSize != -1 && mNewPipelineCacheSize == valueSize) { + // There has not been change in pipeline cache size. Stop trying to save. + mTryToStorePipelineCache = false; + return; + } + mNewPipelineCacheSize = valueSize; + } else { + mCacheDirty = true; + // If there are new shaders compiled, we probably have new pipeline state too. + // Store pipeline cache on the next flush. + mNewPipelineCacheSize = -1; + mTryToStorePipelineCache = true; + } bc->set(key.data(), keySize, value, valueSize); if (!mSavePending && mDeferredSaveDelay > 0) { @@ -175,12 +194,31 @@ void ShaderCache::store(const SkData& key, const SkData& data) { std::thread deferredSaveThread([this]() { sleep(mDeferredSaveDelay); std::lock_guard<std::mutex> lock(mMutex); - saveToDiskLocked(); + // Store file on disk if there a new shader or Vulkan pipeline cache size changed. + if (mCacheDirty || mNewPipelineCacheSize != mOldPipelineCacheSize) { + saveToDiskLocked(); + mOldPipelineCacheSize = mNewPipelineCacheSize; + mTryToStorePipelineCache = false; + mCacheDirty = false; + } }); deferredSaveThread.detach(); } } +void ShaderCache::onVkFrameFlushed(GrContext* context) { + { + std::lock_guard<std::mutex> lock(mMutex); + + if (!mInitialized || !mTryToStorePipelineCache) { + return; + } + } + mInStoreVkPipelineInProgress = true; + context->storeVkPipelineCacheData(); + mInStoreVkPipelineInProgress = false; +} + } /* namespace skiapipeline */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h index d41aadb269dd..0898017d52a1 100644 --- a/libs/hwui/pipeline/skia/ShaderCache.h +++ b/libs/hwui/pipeline/skia/ShaderCache.h @@ -75,6 +75,13 @@ public: */ void store(const SkData& key, const SkData& data) override; + /** + * "onVkFrameFlushed" tries to store Vulkan pipeline cache state. + * Pipeline cache is saved on disk only if the size of the data has changed or there was + * a new shader compiled. + */ + void onVkFrameFlushed(GrContext* context); + private: // Creation and (the lack of) destruction is handled internally. ShaderCache(); @@ -167,6 +174,33 @@ private: mutable std::mutex mMutex; /** + * If set to "true", the next call to onVkFrameFlushed, will invoke + * GrCanvas::storeVkPipelineCacheData. This does not guarantee that data will be stored on disk. + */ + bool mTryToStorePipelineCache = true; + + /** + * This flag is used by "ShaderCache::store" to distinguish between shader data and + * Vulkan pipeline data. + */ + bool mInStoreVkPipelineInProgress = false; + + /** + * "mNewPipelineCacheSize" has the size of the new Vulkan pipeline cache data. It is used + * to prevent unnecessary disk writes, if the pipeline cache size has not changed. + */ + size_t mNewPipelineCacheSize = -1; + /** + * "mOldPipelineCacheSize" has the size of the Vulkan pipeline cache data stored on disk. + */ + size_t mOldPipelineCacheSize = -1; + + /** + * "mCacheDirty" is true when there is new shader cache data, which is not saved to disk. + */ + bool mCacheDirty = false; + + /** * "sCache" is the singleton ShaderCache object. */ static ShaderCache sCache; diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index 1d3a24463057..b9aae9839bdc 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -23,6 +23,7 @@ #include "VkInteropFunctorDrawable.h" #include "renderstate/RenderState.h" #include "renderthread/Frame.h" +#include "ShaderCache.h" #include <SkSurface.h> #include <SkTypes.h> @@ -73,6 +74,7 @@ bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, con } SkiaPipeline::updateLighting(lightGeometry, lightInfo); renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, backBuffer); + ShaderCache::get().onVkFrameFlushed(mRenderThread.getGrContext()); layerUpdateQueue->clear(); // Draw visual debugging features diff --git a/libs/hwui/private/hwui/DrawVkInfo.h b/libs/hwui/private/hwui/DrawVkInfo.h index b2351fc026de..fd824bd957fd 100644 --- a/libs/hwui/private/hwui/DrawVkInfo.h +++ b/libs/hwui/private/hwui/DrawVkInfo.h @@ -52,17 +52,8 @@ struct DrawVkInfo { // Input: Format of the destination surface. VkFormat format; - // Input: Color space transfer params - float g; - float a; - float b; - float c; - float d; - float e; - float f; - - // Input: Color space transformation from linear RGB to D50-adapted XYZ - float colorSpaceTransform[9]; + // Input: Color space + const SkColorSpace* colorSpaceInfo; // Input: current clip rect int clipLeft; diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index c06faddf7fa6..8bef35915c4d 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -171,6 +171,9 @@ void RenderThread::initThreadLocals() { mRenderState = new RenderState(*this); mVkManager = new VulkanManager(*this); mCacheManager = new CacheManager(mDisplayInfo); + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { + mVkManager->initialize(); + } } void RenderThread::requireGlContext() { diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp index f3a764874e2b..f6178aff0c2e 100644 --- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp +++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp @@ -44,8 +44,8 @@ TEST(SkiaCanvas, drawShadowLayer) { } TEST(SkiaCanvas, colorSpaceXform) { - sk_sp<SkColorSpace> adobe = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, - SkColorSpace::kAdobeRGB_Gamut); + sk_sp<SkColorSpace> adobe = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, + SkNamedGamut::kAdobeRGB); SkImageInfo adobeInfo = SkImageInfo::Make(1, 1, kN32_SkColorType, kOpaque_SkAlphaType, adobe); sk_sp<Bitmap> adobeBitmap = Bitmap::allocateHeapBitmap(adobeInfo); diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp index dc347f615d98..4415a593f6ee 100644 --- a/libs/hwui/utils/Color.cpp +++ b/libs/hwui/utils/Color.cpp @@ -25,38 +25,6 @@ namespace android { namespace uirenderer { -static inline bool almostEqual(float a, float b) { - return std::abs(a - b) < 1e-2f; -} - -bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace) { - if (colorSpace == nullptr) return true; - if (colorSpace->isSRGB()) return true; - - SkColorSpaceTransferFn transferFunction; - if (colorSpace->isNumericalTransferFn(&transferFunction)) { - // sRGB transfer function params: - const float sRGBParamA = 1 / 1.055f; - const float sRGBParamB = 0.055f / 1.055f; - const float sRGBParamC = 1 / 12.92f; - const float sRGBParamD = 0.04045f; - const float sRGBParamE = 0.0f; - const float sRGBParamF = 0.0f; - const float sRGBParamG = 2.4f; - - // This comparison will catch Display P3 - return almostEqual(sRGBParamA, transferFunction.fA) && - almostEqual(sRGBParamB, transferFunction.fB) && - almostEqual(sRGBParamC, transferFunction.fC) && - almostEqual(sRGBParamD, transferFunction.fD) && - almostEqual(sRGBParamE, transferFunction.fE) && - almostEqual(sRGBParamF, transferFunction.fF) && - almostEqual(sRGBParamG, transferFunction.fG); - } - - return false; -} - android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType) { switch (colorType) { case kRGBA_8888_SkColorType: @@ -79,19 +47,19 @@ android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType) { sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) { - SkColorSpace::Gamut gamut; + skcms_Matrix3x3 gamut; switch (dataspace & HAL_DATASPACE_STANDARD_MASK) { case HAL_DATASPACE_STANDARD_BT709: - gamut = SkColorSpace::kSRGB_Gamut; + gamut = SkNamedGamut::kSRGB; break; case HAL_DATASPACE_STANDARD_BT2020: - gamut = SkColorSpace::kRec2020_Gamut; + gamut = SkNamedGamut::kRec2020; break; case HAL_DATASPACE_STANDARD_DCI_P3: - gamut = SkColorSpace::kDCIP3_D65_Gamut; + gamut = SkNamedGamut::kDCIP3; break; case HAL_DATASPACE_STANDARD_ADOBE_RGB: - gamut = SkColorSpace::kAdobeRGB_Gamut; + gamut = SkNamedGamut::kAdobeRGB; break; case HAL_DATASPACE_STANDARD_UNSPECIFIED: return nullptr; @@ -109,9 +77,9 @@ sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) { switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) { case HAL_DATASPACE_TRANSFER_LINEAR: - return SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, gamut); + return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut); case HAL_DATASPACE_TRANSFER_SRGB: - return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, gamut); + return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut); case HAL_DATASPACE_TRANSFER_GAMMA2_2: return SkColorSpace::MakeRGB({2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut); case HAL_DATASPACE_TRANSFER_GAMMA2_6: diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h index 4473ce632b1b..388025207ed6 100644 --- a/libs/hwui/utils/Color.h +++ b/libs/hwui/utils/Color.h @@ -111,11 +111,6 @@ static constexpr float EOCF(float srgb) { #endif } -// Returns whether the specified color space's transfer function can be -// approximated with the native sRGB transfer function. This method -// returns true for sRGB, gamma 2.2 and Display P3 for instance -bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace); - android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType); ANDROID_API sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace); diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java index f179bc3f9d3d..602cc3e6d0fd 100644 --- a/location/java/android/location/GnssMeasurement.java +++ b/location/java/android/location/GnssMeasurement.java @@ -48,6 +48,7 @@ public final class GnssMeasurement implements Parcelable { private int mMultipathIndicator; private double mSnrInDb; private double mAutomaticGainControlLevelInDb; + private int mCodeType; // The following enumerations must be in sync with the values declared in gps.h @@ -58,6 +59,7 @@ public final class GnssMeasurement implements Parcelable { private static final int HAS_CARRIER_PHASE = (1<<11); private static final int HAS_CARRIER_PHASE_UNCERTAINTY = (1<<12); private static final int HAS_AUTOMATIC_GAIN_CONTROL = (1<<13); + private static final int HAS_CODE_TYPE = (1 << 14); /** * The status of the multipath indicator. @@ -202,6 +204,104 @@ public final class GnssMeasurement implements Parcelable { public static final int ADR_STATE_HALF_CYCLE_REPORTED = (1<<4); /** + * GNSS measurement code type. + * @hide + */ + @IntDef(prefix = { "CODE_TYPE_" }, value = { + CODE_TYPE_UNKNOWN, CODE_TYPE_A, CODE_TYPE_B, CODE_TYPE_C, CODE_TYPE_I, CODE_TYPE_L, + CODE_TYPE_M, CODE_TYPE_P, CODE_TYPE_Q, CODE_TYPE_S, CODE_TYPE_W, CODE_TYPE_X, + CODE_TYPE_Y, CODE_TYPE_Z, CODE_TYPE_CODELESS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CodeType {} + + /** The GNSS Measurement's code type is unknown. */ + public static final int CODE_TYPE_UNKNOWN = -1; + + /** + * The GNSS Measurement's code type is one of the following: GALILEO E1A, GALILEO E6A, IRNSS + * L5A, IRNSS SA. + */ + public static final int CODE_TYPE_A = 0; + + /** + * The GNSS Measurement's code type is one of the following: GALILEO E1B, GALILEO E6B, IRNSS + * L5B, IRNSS SB. + */ + public static final int CODE_TYPE_B = 1; + + /** + * The GNSS Measurement's code type is one of the following: GPS L1 C/A, GPS L2 C/A, GLONASS G1 + * C/A, GLONASS G2 C/A, GALILEO E1C, GALILEO E6C, SBAS L1 C/A, QZSS L1 C/A, IRNSS L5C. + */ + public static final int CODE_TYPE_C = 2; + + /** + * The GNSS Measurement's code type is one of the following: GPS L5 I, GLONASS G3 I, GALILEO E5a + * I, GALILEO E5b I, GALILEO E5a+b I, SBAS L5 I, QZSS L5 I, BDS B1 I, BDS B2 I, BDS B3 I. + */ + public static final int CODE_TYPE_I = 3; + + /** + * The GNSS Measurement's code type is one of the following: GPS L1C (P), GPS L2C (L), QZSS L1C + * (P), QZSS L2C (L), LEX(6) L. + */ + public static final int CODE_TYPE_L = 4; + + /** + * The GNSS Measurement's code type is one of the following: GPS L1M, GPS L2M. + */ + public static final int CODE_TYPE_M = 5; + + /** + * The GNSS Measurement's code type is one of the following: GPS L1P, GPS L2P, GLONASS G1P, + * GLONASS G2P. + */ + public static final int CODE_TYPE_P = 6; + + /** + * The GNSS Measurement's code type is one of the following: GPS L5 Q, GLONASS G3 Q, GALILEO E5a + * Q, GALILEO E5b Q, GALILEO E5a+b Q, SBAS L5 Q, QZSS L5 Q, BDS B1 Q, BDS B2 Q, BDS B3 Q. + */ + public static final int CODE_TYPE_Q = 7; + + /** + * The GNSS Measurement's code type is one of the following: GPS L1C (D), GPS L2C (M), QZSS L1C + * (D), QZSS L2C (M), LEX(6) S. + */ + public static final int CODE_TYPE_S = 8; + + /** + * The GNSS Measurement's code type is one of the following: GPS L1 Z-tracking, GPS L2 + * Z-tracking. + */ + public static final int CODE_TYPE_W = 9; + + /** + * The GNSS Measurement's code type is one of the following: GPS L1C (D+P), GPS L2C (M+L), GPS + * L5 (I+Q), GLONASS G3 (I+Q), GALILEO E1 (B+C), GALILEO E5a (I+Q), GALILEO E5b (I+Q), GALILEO + * E5a+b(I+Q), GALILEO E6 (B+C), SBAS L5 (I+Q), QZSS L1C (D+P), QZSS L2C (M+L), QZSS L5 (I+Q), + * LEX(6) (S+L), BDS B1 (I+Q), BDS B2 (I+Q), BDS B3 (I+Q), IRNSS L5 (B+C). + */ + public static final int CODE_TYPE_X = 10; + + /** + * The GNSS Measurement's code type is one of the following: GPS L1Y, GPS L2Y. + */ + public static final int CODE_TYPE_Y = 11; + + /** + * The GNSS Measurement's code type is one of the following: GALILEO E1 (A+B+C), GALILEO E6 + * (A+B+C), QZSS L1-SAIF. + */ + public static final int CODE_TYPE_Z = 12; + + /** + * The GNSS Measurement's code type is one of the following: GPS L1 codeless, GPS L2 codeless. + */ + public static final int CODE_TYPE_CODELESS = 13; + + /** * All the 'Accumulated Delta Range' flags. * @hide */ @@ -248,6 +348,7 @@ public final class GnssMeasurement implements Parcelable { mMultipathIndicator = measurement.mMultipathIndicator; mSnrInDb = measurement.mSnrInDb; mAutomaticGainControlLevelInDb = measurement.mAutomaticGainControlLevelInDb; + mCodeType = measurement.mCodeType; } /** @@ -967,7 +1068,7 @@ public final class GnssMeasurement implements Parcelable { * <p>For internal and logging use only. */ private String getMultipathIndicatorString() { - switch(mMultipathIndicator) { + switch (mMultipathIndicator) { case MULTIPATH_INDICATOR_UNKNOWN: return "Unknown"; case MULTIPATH_INDICATOR_DETECTED: @@ -1063,6 +1164,89 @@ public final class GnssMeasurement implements Parcelable { mAutomaticGainControlLevelInDb = Double.NaN; } + /** + * Returns {@code true} if {@link #getCodeType()} is available, + * {@code false} otherwise. + */ + public boolean hasCodeType() { + return isFlagSet(HAS_CODE_TYPE); + } + + /** + * Gets the GNSS measurement's code type. + * + * <p>Similar to the Attribute field described in Rinex 3.03, e.g., in Tables 4-10, and Table + * A2 at the Rinex 3.03 Update 1 Document. + */ + @CodeType + public int getCodeType() { + return mCodeType; + } + + /** + * Sets the GNSS measurement's code type. + * + * @hide + */ + @TestApi + public void setCodeType(@CodeType int codeType) { + setFlag(HAS_CODE_TYPE); + mCodeType = codeType; + } + + /** + * Resets the GNSS measurement's code type. + * + * @hide + */ + @TestApi + public void resetCodeType() { + resetFlag(HAS_CODE_TYPE); + mCodeType = CODE_TYPE_UNKNOWN; + } + + /** + * Gets a string representation of the 'code type'. + * + * <p>For internal and logging use only. + */ + private String getCodeTypeString() { + switch (mCodeType) { + case CODE_TYPE_UNKNOWN: + return "CODE_TYPE_UNKNOWN"; + case CODE_TYPE_A: + return "CODE_TYPE_A"; + case CODE_TYPE_B: + return "CODE_TYPE_B"; + case CODE_TYPE_C: + return "CODE_TYPE_C"; + case CODE_TYPE_I: + return "CODE_TYPE_I"; + case CODE_TYPE_L: + return "CODE_TYPE_L"; + case CODE_TYPE_M: + return "CODE_TYPE_M"; + case CODE_TYPE_P: + return "CODE_TYPE_P"; + case CODE_TYPE_Q: + return "CODE_TYPE_Q"; + case CODE_TYPE_S: + return "CODE_TYPE_S"; + case CODE_TYPE_W: + return "CODE_TYPE_W"; + case CODE_TYPE_X: + return "CODE_TYPE_X"; + case CODE_TYPE_Y: + return "CODE_TYPE_Y"; + case CODE_TYPE_Z: + return "CODE_TYPE_Z"; + case CODE_TYPE_CODELESS: + return "CODE_TYPE_CODELESS"; + default: + return "<Invalid: " + mCodeType + ">"; + } + } + public static final Creator<GnssMeasurement> CREATOR = new Creator<GnssMeasurement>() { @Override public GnssMeasurement createFromParcel(Parcel parcel) { @@ -1088,6 +1272,7 @@ public final class GnssMeasurement implements Parcelable { gnssMeasurement.mMultipathIndicator = parcel.readInt(); gnssMeasurement.mSnrInDb = parcel.readDouble(); gnssMeasurement.mAutomaticGainControlLevelInDb = parcel.readDouble(); + gnssMeasurement.mCodeType = parcel.readInt(); return gnssMeasurement; } @@ -1120,6 +1305,7 @@ public final class GnssMeasurement implements Parcelable { parcel.writeInt(mMultipathIndicator); parcel.writeDouble(mSnrInDb); parcel.writeDouble(mAutomaticGainControlLevelInDb); + parcel.writeInt(mCodeType); } @Override @@ -1191,9 +1377,13 @@ public final class GnssMeasurement implements Parcelable { "SnrInDb", hasSnrInDb() ? mSnrInDb : null)); builder.append(String.format( - format, - "AgcLevelDb", - hasAutomaticGainControlLevelDb() ? mAutomaticGainControlLevelInDb : null)); + format, + "AgcLevelDb", + hasAutomaticGainControlLevelDb() ? mAutomaticGainControlLevelInDb : null)); + builder.append(String.format( + format, + "CodeType", + hasCodeType() ? getCodeTypeString() : null)); return builder.toString(); } @@ -1218,6 +1408,7 @@ public final class GnssMeasurement implements Parcelable { setMultipathIndicator(MULTIPATH_INDICATOR_UNKNOWN); resetSnrInDb(); resetAutomaticGainControlLevel(); + resetCodeType(); } private void setFlag(int flag) { diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index 0375de3cc496..bc9500ddb3b7 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -2289,6 +2289,30 @@ final public class MediaCodec { */ public static final int ERROR_UNSUPPORTED_OPERATION = 6; + /** + * This indicates that the security level of the device is not + * sufficient to meet the requirements set by the content owner + * in the license policy. + */ + public static final int ERROR_INSUFFICIENT_SECURITY = 7; + + /** + * This indicates that the video frame being decrypted exceeds + * the size of the device's protected output buffers. When + * encountering this error the app should try playing content + * of a lower resolution. + */ + public static final int ERROR_FRAME_TOO_LARGE = 8; + + /** + * This error indicates that session state has been + * invalidated. It can occur on devices that are not capable + * of retaining crypto session state across device + * suspend/resume. The session must be closed and a new + * session opened to resume operation. + */ + public static final int ERROR_LOST_STATE = 9; + /** @hide */ @IntDef({ ERROR_NO_KEY, @@ -2296,7 +2320,10 @@ final public class MediaCodec { ERROR_RESOURCE_BUSY, ERROR_INSUFFICIENT_OUTPUT_PROTECTION, ERROR_SESSION_NOT_OPENED, - ERROR_UNSUPPORTED_OPERATION + ERROR_UNSUPPORTED_OPERATION, + ERROR_INSUFFICIENT_SECURITY, + ERROR_FRAME_TOO_LARGE, + ERROR_LOST_STATE }) @Retention(RetentionPolicy.SOURCE) public @interface CryptoErrorCode {} diff --git a/media/java/android/media/MediaConstants.java b/media/java/android/media/MediaConstants.java index 275b0acd8ad6..5a5747ae4bab 100644 --- a/media/java/android/media/MediaConstants.java +++ b/media/java/android/media/MediaConstants.java @@ -24,7 +24,7 @@ class MediaConstants { static final String KEY_PACKAGE_NAME = "android.media.key.PACKAGE_NAME"; // Bundle key for Parcelable - static final String KEY_SESSION2_STUB = "android.media.key.SESSION2_STUB"; + static final String KEY_SESSION2LINK = "android.media.key.SESSION2LINK"; static final String KEY_ALLOWED_COMMANDS = "android.media.key.ALLOWED_COMMANDS"; private MediaConstants() { diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java index b8381a7249be..774ea185f57c 100644 --- a/media/java/android/media/MediaController2.java +++ b/media/java/android/media/MediaController2.java @@ -19,7 +19,7 @@ package android.media; import static android.media.MediaConstants.KEY_ALLOWED_COMMANDS; import static android.media.MediaConstants.KEY_PACKAGE_NAME; import static android.media.MediaConstants.KEY_PID; -import static android.media.MediaConstants.KEY_SESSION2_STUB; +import static android.media.MediaConstants.KEY_SESSION2LINK; import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR; import static android.media.Session2Command.RESULT_INFO_SKIPPED; import static android.media.Session2Token.TYPE_SESSION; @@ -41,15 +41,15 @@ import java.util.concurrent.Executor; /** * Allows an app to interact with an active {@link MediaSession2} or a - * {@link MediaSession2Service} which would provide {@link MediaSession2}. Media buttons and other + * MediaSession2Service which would provide {@link MediaSession2}. Media buttons and other * commands can be sent to the session. * <p> * This API is not generally intended for third party application developers. * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a> * for consistent behavior across all devices. - * @hide */ +// TODO: use @link for MediaSession2Service public class MediaController2 implements AutoCloseable { static final String TAG = "MediaController2"; static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -130,8 +130,8 @@ public class MediaController2 implements AutoCloseable { synchronized (mLock) { if (mSessionBinder != null) { try { - mSessionBinder.unlinkToDeath(mDeathRecipient, 0); mSessionBinder.disconnect(mControllerStub, getNextSeqNumber()); + mSessionBinder.unlinkToDeath(mDeathRecipient, 0); } catch (RuntimeException e) { // No-op } @@ -153,6 +153,7 @@ public class MediaController2 implements AutoCloseable { * @return a token which will be sent together in {@link ControllerCallback#onCommandResult} * when its result is received. */ + @NonNull public Object sendSessionCommand(@NonNull Session2Command command, @Nullable Bundle args) { if (command == null) { throw new IllegalArgumentException("command shouldn't be null"); @@ -208,7 +209,7 @@ public class MediaController2 implements AutoCloseable { void onConnected(int seq, Bundle connectionResult) { final long token = Binder.clearCallingIdentity(); try { - Session2Link sessionBinder = connectionResult.getParcelable(KEY_SESSION2_STUB); + Session2Link sessionBinder = connectionResult.getParcelable(KEY_SESSION2LINK); Session2CommandGroup allowedCommands = connectionResult.getParcelable(KEY_ALLOWED_COMMANDS); if (DEBUG) { @@ -349,7 +350,7 @@ public class MediaController2 implements AutoCloseable { * @return the result for the session command. A runtime exception will be thrown if null * is returned. */ - @NonNull + @Nullable public Session2Command.Result onSessionCommand(@NonNull MediaController2 controller, @NonNull Session2Command command, @Nullable Bundle args) { return null; diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java index cdbc7b44f905..75b391534dd4 100644 --- a/media/java/android/media/MediaDrm.java +++ b/media/java/android/media/MediaDrm.java @@ -132,11 +132,19 @@ public final class MediaDrm implements AutoCloseable { private static final String PERMISSION = android.Manifest.permission.ACCESS_DRM_CERTIFICATES; private EventHandler mEventHandler; - private EventHandler mOnKeyStatusChangeEventHandler; - private EventHandler mOnExpirationUpdateEventHandler; + private EventHandler mKeyStatusChangeHandler; + private EventHandler mExpirationUpdateHandler; + private EventHandler mSessionLostStateHandler; + private OnEventListener mOnEventListener; private OnKeyStatusChangeListener mOnKeyStatusChangeListener; private OnExpirationUpdateListener mOnExpirationUpdateListener; + private OnSessionLostStateListener mOnSessionLostStateListener; + + private final Object mEventLock = new Object(); + private final Object mKeyStatusChangeLock = new Object(); + private final Object mExpirationUpdateLock = new Object(); + private final Object mSessionLostStateLock = new Object(); private long mNativeContext; @@ -200,6 +208,35 @@ public final class MediaDrm implements AutoCloseable { private static final native boolean isCryptoSchemeSupportedNative( @NonNull byte[] uuid, @Nullable String mimeType); + private EventHandler createHandler() { + Looper looper; + EventHandler handler; + if ((looper = Looper.myLooper()) != null) { + handler = new EventHandler(this, looper); + } else if ((looper = Looper.getMainLooper()) != null) { + handler = new EventHandler(this, looper); + } else { + handler = null; + } + return handler; + } + + private EventHandler updateHandler(Handler handler) { + Looper looper; + EventHandler newHandler = null; + if (handler != null) { + looper = handler.getLooper(); + } else { + looper = Looper.myLooper(); + } + if (looper != null) { + if (handler == null || handler.getLooper() != looper) { + newHandler = new EventHandler(this, looper); + } + } + return newHandler; + } + /** * Instantiate a MediaDrm object * @@ -209,14 +246,10 @@ public final class MediaDrm implements AutoCloseable { * specified scheme UUID */ public MediaDrm(@NonNull UUID uuid) throws UnsupportedSchemeException { - Looper looper; - if ((looper = Looper.myLooper()) != null) { - mEventHandler = new EventHandler(this, looper); - } else if ((looper = Looper.getMainLooper()) != null) { - mEventHandler = new EventHandler(this, looper); - } else { - mEventHandler = null; - } + mEventHandler = createHandler(); + mKeyStatusChangeHandler = createHandler(); + mExpirationUpdateHandler = createHandler(); + mSessionLostStateHandler = createHandler(); /* Native setup requires a weak reference to our object. * It's easier to create it here than in C++. @@ -272,6 +305,40 @@ public final class MediaDrm implements AutoCloseable { } /** + * Thrown when an error occurs in any method that has a session context. + */ + public static final class SessionException extends RuntimeException { + public SessionException(int errorCode, @Nullable String detailMessage) { + super(detailMessage); + mErrorCode = errorCode; + } + + /** + * This indicates that apps using MediaDrm sessions are + * temporarily exceeding the capacity of available crypto + * resources. The app should retry the operation later. + */ + public static final int ERROR_RESOURCE_CONTENTION = 1; + + /** @hide */ + @IntDef({ + ERROR_RESOURCE_CONTENTION, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SessionErrorCode {} + + /** + * Retrieve the error code associated with the SessionException + */ + @SessionErrorCode + public int getErrorCode() { + return mErrorCode; + } + + private final int mErrorCode; + } + + /** * Register a callback to be invoked when a session expiration update * occurs. The app's OnExpirationUpdateListener will be notified * when the expiration time of the keys in the session have changed. @@ -282,15 +349,12 @@ public final class MediaDrm implements AutoCloseable { */ public void setOnExpirationUpdateListener( @Nullable OnExpirationUpdateListener listener, @Nullable Handler handler) { - if (listener != null) { - Looper looper = handler != null ? handler.getLooper() : Looper.myLooper(); - if (looper != null) { - if (mEventHandler == null || mEventHandler.getLooper() != looper) { - mEventHandler = new EventHandler(this, looper); - } + synchronized(mExpirationUpdateLock) { + if (listener != null) { + mExpirationUpdateHandler = updateHandler(handler); } + mOnExpirationUpdateListener = listener; } - mOnExpirationUpdateListener = listener; } /** @@ -324,15 +388,12 @@ public final class MediaDrm implements AutoCloseable { */ public void setOnKeyStatusChangeListener( @Nullable OnKeyStatusChangeListener listener, @Nullable Handler handler) { - if (listener != null) { - Looper looper = handler != null ? handler.getLooper() : Looper.myLooper(); - if (looper != null) { - if (mEventHandler == null || mEventHandler.getLooper() != looper) { - mEventHandler = new EventHandler(this, looper); - } + synchronized(mKeyStatusChangeLock) { + if (listener != null) { + mKeyStatusChangeHandler = updateHandler(handler); } + mOnKeyStatusChangeListener = listener; } - mOnKeyStatusChangeListener = listener; } /** @@ -360,6 +421,46 @@ public final class MediaDrm implements AutoCloseable { } /** + * Register a callback to be invoked when session state has been + * lost. This event can occur on devices that are not capable of + * retaining crypto session state across device suspend/resume + * cycles. When this event occurs, the session must be closed and + * a new session opened to resume operation. + * + * @param listener the callback that will be run, or {@code null} to unregister the + * previously registered callback. + * @param handler the handler on which the listener should be invoked, or + * {@code null} if the listener should be invoked on the calling thread's looper. + */ + public void setOnSessionLostStateListener( + @Nullable OnSessionLostStateListener listener, @Nullable Handler handler) { + synchronized(mSessionLostStateLock) { + if (listener != null) { + mSessionLostStateHandler = updateHandler(handler); + } + mOnSessionLostStateListener = listener; + } + } + + /** + * Interface definition for a callback to be invoked when the + * session state has been lost and is now invalid + */ + public interface OnSessionLostStateListener + { + /** + * Called when session state has lost state, to inform the app + * about the condition so it can close the session and open a new + * one to resume operation. + * + * @param md the MediaDrm object on which the event occurred + * @param sessionId the DRM session ID on which the event occurred + */ + void onSessionLostState( + @NonNull MediaDrm md, @NonNull byte[] sessionId); + } + + /** * Defines the status of a key. * A KeyStatus for each key in a session is provided to the * {@link OnKeyStatusChangeListener#onKeyStatusChange} @@ -437,7 +538,9 @@ public final class MediaDrm implements AutoCloseable { */ public void setOnEventListener(@Nullable OnEventListener listener) { - mOnEventListener = listener; + synchronized(mEventLock) { + mOnEventListener = listener; + } } /** @@ -513,6 +616,7 @@ public final class MediaDrm implements AutoCloseable { private static final int DRM_EVENT = 200; private static final int EXPIRATION_UPDATE = 201; private static final int KEY_STATUS_CHANGE = 202; + private static final int SESSION_LOST_STATE = 203; private class EventHandler extends Handler { @@ -532,52 +636,72 @@ public final class MediaDrm implements AutoCloseable { switch(msg.what) { case DRM_EVENT: - if (mOnEventListener != null) { - if (msg.obj != null && msg.obj instanceof Parcel) { - Parcel parcel = (Parcel)msg.obj; - byte[] sessionId = parcel.createByteArray(); - if (sessionId.length == 0) { - sessionId = null; - } - byte[] data = parcel.createByteArray(); - if (data.length == 0) { - data = null; + synchronized(mEventLock) { + if (mOnEventListener != null) { + if (msg.obj != null && msg.obj instanceof Parcel) { + Parcel parcel = (Parcel)msg.obj; + byte[] sessionId = parcel.createByteArray(); + if (sessionId.length == 0) { + sessionId = null; + } + byte[] data = parcel.createByteArray(); + if (data.length == 0) { + data = null; + } + + Log.i(TAG, "Drm event (" + msg.arg1 + "," + msg.arg2 + ")"); + mOnEventListener.onEvent(mMediaDrm, sessionId, msg.arg1, msg.arg2, data); } - - Log.i(TAG, "Drm event (" + msg.arg1 + "," + msg.arg2 + ")"); - mOnEventListener.onEvent(mMediaDrm, sessionId, msg.arg1, msg.arg2, data); } } return; case KEY_STATUS_CHANGE: - if (mOnKeyStatusChangeListener != null) { - if (msg.obj != null && msg.obj instanceof Parcel) { - Parcel parcel = (Parcel)msg.obj; - byte[] sessionId = parcel.createByteArray(); - if (sessionId.length > 0) { - List<KeyStatus> keyStatusList = keyStatusListFromParcel(parcel); - boolean hasNewUsableKey = (parcel.readInt() != 0); - - Log.i(TAG, "Drm key status changed"); - mOnKeyStatusChangeListener.onKeyStatusChange(mMediaDrm, sessionId, - keyStatusList, hasNewUsableKey); + synchronized(mKeyStatusChangeLock) { + if (mOnKeyStatusChangeListener != null) { + if (msg.obj != null && msg.obj instanceof Parcel) { + Parcel parcel = (Parcel)msg.obj; + byte[] sessionId = parcel.createByteArray(); + if (sessionId.length > 0) { + List<KeyStatus> keyStatusList = keyStatusListFromParcel(parcel); + boolean hasNewUsableKey = (parcel.readInt() != 0); + + Log.i(TAG, "Drm key status changed"); + mOnKeyStatusChangeListener.onKeyStatusChange(mMediaDrm, sessionId, + keyStatusList, hasNewUsableKey); + } } } } return; case EXPIRATION_UPDATE: - if (mOnExpirationUpdateListener != null) { - if (msg.obj != null && msg.obj instanceof Parcel) { - Parcel parcel = (Parcel)msg.obj; - byte[] sessionId = parcel.createByteArray(); - if (sessionId.length > 0) { - long expirationTime = parcel.readLong(); - - Log.i(TAG, "Drm key expiration update: " + expirationTime); - mOnExpirationUpdateListener.onExpirationUpdate(mMediaDrm, sessionId, - expirationTime); + synchronized(mExpirationUpdateLock) { + if (mOnExpirationUpdateListener != null) { + if (msg.obj != null && msg.obj instanceof Parcel) { + Parcel parcel = (Parcel)msg.obj; + byte[] sessionId = parcel.createByteArray(); + if (sessionId.length > 0) { + long expirationTime = parcel.readLong(); + + Log.i(TAG, "Drm key expiration update: " + expirationTime); + mOnExpirationUpdateListener.onExpirationUpdate(mMediaDrm, sessionId, + expirationTime); + } + } + } + } + return; + + case SESSION_LOST_STATE: + synchronized(mSessionLostStateLock) { + if (mOnSessionLostStateListener != null) { + if (msg.obj != null && msg.obj instanceof Parcel) { + Parcel parcel = (Parcel)msg.obj; + byte[] sessionId = parcel.createByteArray(); + Log.i(TAG, "Drm session lost state event: "); + mOnSessionLostStateListener.onSessionLostState(mMediaDrm, + sessionId); } } } @@ -619,9 +743,42 @@ public final class MediaDrm implements AutoCloseable { if (md == null) { return; } - if (md.mEventHandler != null) { - Message m = md.mEventHandler.obtainMessage(what, eventType, extra, obj); - md.mEventHandler.sendMessage(m); + switch (what) { + case DRM_EVENT: + synchronized(md.mEventLock) { + if (md.mEventHandler != null) { + Message m = md.mEventHandler.obtainMessage(what, eventType, extra, obj); + md.mEventHandler.sendMessage(m); + } + } + break; + case EXPIRATION_UPDATE: + synchronized(md.mExpirationUpdateLock) { + if (md.mExpirationUpdateHandler != null) { + Message m = md.mExpirationUpdateHandler.obtainMessage(what, obj); + md.mExpirationUpdateHandler.sendMessage(m); + } + } + break; + case KEY_STATUS_CHANGE: + synchronized(md.mKeyStatusChangeLock) { + if (md.mKeyStatusChangeHandler != null) { + Message m = md.mKeyStatusChangeHandler.obtainMessage(what, obj); + md.mKeyStatusChangeHandler.sendMessage(m); + } + } + break; + case SESSION_LOST_STATE: + synchronized(md.mSessionLostStateLock) { + if (md.mSessionLostStateHandler != null) { + Message m = md.mSessionLostStateHandler.obtainMessage(what, obj); + md.mSessionLostStateHandler.sendMessage(m); + } + } + break; + default: + Log.e(TAG, "Unknown message type " + what); + break; } } diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java index 7b20f7ae5954..e008adf120ab 100644 --- a/media/java/android/media/MediaSession2.java +++ b/media/java/android/media/MediaSession2.java @@ -19,7 +19,7 @@ package android.media; import static android.media.MediaConstants.KEY_ALLOWED_COMMANDS; import static android.media.MediaConstants.KEY_PACKAGE_NAME; import static android.media.MediaConstants.KEY_PID; -import static android.media.MediaConstants.KEY_SESSION2_STUB; +import static android.media.MediaConstants.KEY_SESSION2LINK; import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR; import static android.media.Session2Command.RESULT_INFO_SKIPPED; import static android.media.Session2Token.TYPE_SESSION; @@ -56,10 +56,9 @@ import java.util.concurrent.Executor; * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a> * for consistent behavior across all devices. - * @hide */ public class MediaSession2 implements AutoCloseable { - static final String TAG = "MediaSession"; + static final String TAG = "MediaSession2"; static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); // Note: This checks the uniqueness of a session ID only in a single process. @@ -89,7 +88,6 @@ public class MediaSession2 implements AutoCloseable { private final Handler mResultHandler; //@GuardedBy("mLock") - @SuppressWarnings("WeakerAccess") /* synthetic access */ private boolean mClosed; MediaSession2(@NonNull Context context, @NonNull String id, PendingIntent sessionActivity, @@ -113,6 +111,7 @@ public class MediaSession2 implements AutoCloseable { Context.MEDIA_SESSION_SERVICE); // NOTE: mResultHandler uses main looper, so this MUST NOT be blocked. mResultHandler = new Handler(context.getMainLooper()); + mClosed = false; } @Override @@ -179,6 +178,7 @@ public class MediaSession2 implements AutoCloseable { * @return a token which will be sent together in {@link SessionCallback#onCommandResult} * when its result is received. */ + @NonNull public Object sendSessionCommand(@NonNull ControllerInfo controller, @NonNull Session2Command command, @Nullable Bundle args) { if (controller == null) { @@ -206,7 +206,10 @@ public class MediaSession2 implements AutoCloseable { * @param controller the controller to get the session command * @param token the token which is returned from {@link #sendSessionCommand}. */ - public void cancelSessionCommand(ControllerInfo controller, Object token) { + public void cancelSessionCommand(@NonNull ControllerInfo controller, @NonNull Object token) { + if (controller == null) { + throw new IllegalArgumentException("controller shouldn't be null"); + } if (token == null) { throw new IllegalArgumentException("token shouldn't be null"); } @@ -267,7 +270,7 @@ public class MediaSession2 implements AutoCloseable { // It's needed because we cannot call synchronous calls between // session/controller. Bundle connectionResult = new Bundle(); - connectionResult.putParcelable(KEY_SESSION2_STUB, mSessionStub); + connectionResult.putParcelable(KEY_SESSION2LINK, mSessionStub); connectionResult.putParcelable(KEY_ALLOWED_COMMANDS, controllerInfo.mAllowedCommands); @@ -558,7 +561,7 @@ public class MediaSession2 implements AutoCloseable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (!(obj instanceof ControllerInfo)) return false; if (this == obj) return true; @@ -570,6 +573,7 @@ public class MediaSession2 implements AutoCloseable { } @Override + @NonNull public String toString() { return "ControllerInfo {pkg=" + mRemoteUserInfo.getPackageName() + ", uid=" + mRemoteUserInfo.getUid() + ", allowedCommands=" + mAllowedCommands + "})"; @@ -693,7 +697,7 @@ public class MediaSession2 implements AutoCloseable { * @return the result for the session command. A runtime exception will be thrown if null * is returned. */ - @NonNull + @Nullable public Session2Command.Result onSessionCommand(@NonNull MediaSession2 session, @NonNull ControllerInfo controller, @NonNull Session2Command command, @Nullable Bundle args) { diff --git a/media/java/android/media/Session2CommandGroup.java b/media/java/android/media/Session2CommandGroup.java index 519888e86462..a189c264b029 100644 --- a/media/java/android/media/Session2CommandGroup.java +++ b/media/java/android/media/Session2CommandGroup.java @@ -59,6 +59,7 @@ public final class Session2CommandGroup implements Parcelable { * * @param commands The collection of commands to copy. */ + @SuppressWarnings("WeakerAccess") /* synthetic access */ Session2CommandGroup(@Nullable Collection<Session2Command> commands) { if (commands != null) { mCommands.addAll(commands); diff --git a/media/java/android/media/Session2Token.java b/media/java/android/media/Session2Token.java index e1fff38c6a79..95ee4c039f4b 100644 --- a/media/java/android/media/Session2Token.java +++ b/media/java/android/media/Session2Token.java @@ -34,7 +34,7 @@ import java.util.List; import java.util.Objects; /** - * Represents an ongoing {@link MediaSession2} or a {@link MediaSession2Service}. + * Represents an ongoing {@link MediaSession2} or a MediaSession2Service. * If it's representing a session service, it may not be ongoing. * <p> * This API is not generally intended for third party application developers. @@ -45,9 +45,8 @@ import java.util.Objects; * This may be passed to apps by the session owner to allow them to create a * {@link MediaController2} to communicate with the session. * <p> - * It can be also obtained by {@link MediaSessionManager}. + * It can be also obtained by {@link android.media.session.MediaSessionManager}. * - * @hide */ // New version of MediaSession2.Token for following reasons // - Stop implementing Parcelable for updatable support @@ -56,6 +55,7 @@ import java.util.Objects; // This helps controller apps to keep target of dispatching media key events in uniform way. // For details about the reason, see following. (Android O+) // android.media.session.MediaSessionManager.Callback#onAddressedPlayerChanged +// TODO: use @link for MediaSession2Service public final class Session2Token implements Parcelable { private static final String TAG = "Session2Token"; @@ -75,7 +75,7 @@ public final class Session2Token implements Parcelable { * @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = "TYPE_", value = {TYPE_SESSION, TYPE_SESSION_SERVICE, TYPE_LIBRARY_SERVICE}) + @IntDef(prefix = "TYPE_", value = {TYPE_SESSION, TYPE_SESSION_SERVICE}) public @interface TokenType { } @@ -85,15 +85,10 @@ public final class Session2Token implements Parcelable { public static final int TYPE_SESSION = 0; /** - * Type for {@link MediaSession2Service}. + * Type for MediaSession2Service. */ public static final int TYPE_SESSION_SERVICE = 1; - /** - * Type for {@link MediaLibrary2Service}. - */ - public static final int TYPE_LIBRARY_SERVICE = 2; - private final int mUid; private final @TokenType int mType; private final String mPackageName; @@ -102,8 +97,7 @@ public final class Session2Token implements Parcelable { private final ComponentName mComponentName; /** - * Constructor for the token with type {@link #TYPE_SESSION_SERVICE} or - * {@link #TYPE_LIBRARY_SERVICE}. + * Constructor for the token with type {@link #TYPE_SESSION_SERVICE}. * * @param context The context. * @param serviceComponent The component name of the service. @@ -239,7 +233,6 @@ public final class Session2Token implements Parcelable { * @return type of the token * @see #TYPE_SESSION * @see #TYPE_SESSION_SERVICE - * @see #TYPE_LIBRARY_SERVICE */ public @TokenType int getType() { return mType; diff --git a/media/jni/Android.bp b/media/jni/Android.bp index d6b6339b8f5f..452c6e1becca 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -34,7 +34,6 @@ cc_library_shared { "libutils", "libbinder", "libmedia", - "libmediaextractor", "libmedia_omx", "libmediametrics", "libmediadrm", @@ -125,12 +124,12 @@ cc_library_shared { "libcutils", "libmedia_helper", "libmedia_player2_util", - "libmediaextractor", "libmediaplayer2", "libmediaplayer2-protos", "libmediandk_utils", "libmediautils", "libprotobuf-cpp-lite", + "libstagefright", "libstagefright_esds", "libstagefright_foundation", "libstagefright_httplive", diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index 257860890437..7b07bea3cf1a 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -72,7 +72,10 @@ static struct CryptoErrorCodes { jint cryptoErrorResourceBusy; jint cryptoErrorInsufficientOutputProtection; jint cryptoErrorSessionNotOpened; + jint cryptoErrorInsufficientSecurity; jint cryptoErrorUnsupportedOperation; + jint cryptoErrorFrameTooLarge; + jint cryptoErrorLostState; } gCryptoErrorCodes; static struct CodecActionCodes { @@ -1005,10 +1008,22 @@ static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) { err = gCryptoErrorCodes.cryptoErrorSessionNotOpened; defaultMsg = "Attempted to use a closed session"; break; + case ERROR_DRM_INSUFFICIENT_SECURITY: + err = gCryptoErrorCodes.cryptoErrorInsufficientSecurity; + defaultMsg = "Required security level is not met"; + break; case ERROR_DRM_CANNOT_HANDLE: err = gCryptoErrorCodes.cryptoErrorUnsupportedOperation; defaultMsg = "Operation not supported in this configuration"; break; + case ERROR_DRM_FRAME_TOO_LARGE: + err = gCryptoErrorCodes.cryptoErrorFrameTooLarge; + defaultMsg = "Decrytped frame exceeds size of output buffer"; + break; + case ERROR_DRM_SESSION_LOST_STATE: + err = gCryptoErrorCodes.cryptoErrorLostState; + defaultMsg = "Session state was lost, open a new session and retry"; + break; default: /* Other negative DRM error codes go out as is. */ break; } @@ -1994,11 +2009,26 @@ static void android_media_MediaCodec_native_init(JNIEnv *env) { gCryptoErrorCodes.cryptoErrorSessionNotOpened = env->GetStaticIntField(clazz.get(), field); + field = env->GetStaticFieldID(clazz.get(), "ERROR_INSUFFICIENT_SECURITY", "I"); + CHECK(field != NULL); + gCryptoErrorCodes.cryptoErrorInsufficientSecurity = + env->GetStaticIntField(clazz.get(), field); + field = env->GetStaticFieldID(clazz.get(), "ERROR_UNSUPPORTED_OPERATION", "I"); CHECK(field != NULL); gCryptoErrorCodes.cryptoErrorUnsupportedOperation = env->GetStaticIntField(clazz.get(), field); + field = env->GetStaticFieldID(clazz.get(), "ERROR_FRAME_TOO_LARGE", "I"); + CHECK(field != NULL); + gCryptoErrorCodes.cryptoErrorFrameTooLarge = + env->GetStaticIntField(clazz.get(), field); + + field = env->GetStaticFieldID(clazz.get(), "ERROR_LOST_STATE", "I"); + CHECK(field != NULL); + gCryptoErrorCodes.cryptoErrorLostState = + env->GetStaticIntField(clazz.get(), field); + clazz.reset(env->FindClass("android/media/MediaCodec$CodecException")); CHECK(clazz.get() != NULL); field = env->GetStaticFieldID(clazz.get(), "ACTION_TRANSIENT", "I"); diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp index be71dad571bb..83364590039d 100644 --- a/media/jni/android_media_MediaDrm.cpp +++ b/media/jni/android_media_MediaDrm.cpp @@ -110,6 +110,7 @@ struct EventWhat { jint kWhatDrmEvent; jint kWhatExpirationUpdate; jint kWhatKeyStatusChange; + jint kWhatSessionLostState; } gEventWhat; struct KeyTypes { @@ -141,6 +142,16 @@ struct StateExceptionFields { jclass classId; }; +struct SessionExceptionFields { + jmethodID init; + jclass classId; + jfieldID errorCode; +}; + +struct SessionExceptionErrorCodes { + jint kResourceContention; +} gSessionExceptionErrorCodes; + struct HDCPLevels { jint kHdcpLevelUnknown; jint kHdcpNone; @@ -180,6 +191,7 @@ struct fields_t { EntryFields entry; CertificateFields certificate; StateExceptionFields stateException; + SessionExceptionFields sessionException; jclass certificateClassId; jclass hashmapClassId; jclass arraylistClassId; @@ -310,6 +322,9 @@ void JNIDrmListener::notify(DrmPlugin::EventType eventType, int extra, case DrmPlugin::kDrmPluginEventKeysChange: jwhat = gEventWhat.kWhatKeyStatusChange; break; + case DrmPlugin::kDrmPluginEventSessionLostState: + jwhat = gEventWhat.kWhatSessionLostState; + break; default: ALOGE("Invalid event DrmPlugin::EventType %d, ignored", (int)eventType); return; @@ -343,6 +358,30 @@ static void throwStateException(JNIEnv *env, const char *msg, status_t err) { env->Throw(static_cast<jthrowable>(exception)); } +static void throwSessionException(JNIEnv *env, const char *msg, status_t err) { + ALOGE("Session exception: %s (%d)", msg, err); + + jint jErrorCode = 0; + switch(err) { + case ERROR_DRM_RESOURCE_CONTENTION: + jErrorCode = gSessionExceptionErrorCodes.kResourceContention; + break; + default: + break; + } + + jobject exception = env->NewObject(gFields.sessionException.classId, + gFields.sessionException.init, static_cast<int>(err), + env->NewStringUTF(msg)); + + env->SetIntField(exception, gFields.sessionException.errorCode, jErrorCode); + env->Throw(static_cast<jthrowable>(exception)); +} + +static bool isSessionException(status_t err) { + return err == ERROR_DRM_RESOURCE_CONTENTION; +} + static bool throwExceptionAsNecessary( JNIEnv *env, status_t err, const char *msg = NULL) { @@ -370,7 +409,7 @@ static bool throwExceptionAsNecessary( case ERROR_DRM_CANNOT_HANDLE: drmMessage = "Invalid parameter or data format"; break; - case ERROR_DRM_TAMPER_DETECTED: + case ERROR_DRM_INVALID_STATE: drmMessage = "Invalid state"; break; default: @@ -399,6 +438,9 @@ static bool throwExceptionAsNecessary( jniThrowException(env, "android/media/MediaDrmResetException", "mediaserver died"); return true; + } else if (isSessionException(err)) { + throwSessionException(env, msg, err); + return true; } else if (err != OK) { String8 errbuf; if (drmMessage != NULL) { @@ -705,6 +747,8 @@ static void android_media_MediaDrm_native_init(JNIEnv *env) { gEventWhat.kWhatExpirationUpdate = env->GetStaticIntField(clazz, field); GET_STATIC_FIELD_ID(field, clazz, "KEY_STATUS_CHANGE", "I"); gEventWhat.kWhatKeyStatusChange = env->GetStaticIntField(clazz, field); + GET_STATIC_FIELD_ID(field, clazz, "SESSION_LOST_STATE", "I"); + gEventWhat.kWhatSessionLostState = env->GetStaticIntField(clazz, field); GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_STREAMING", "I"); gKeyTypes.kKeyTypeStreaming = env->GetStaticIntField(clazz, field); @@ -831,6 +875,14 @@ static void android_media_MediaDrm_native_init(JNIEnv *env) { FIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException"); GET_METHOD_ID(gFields.stateException.init, clazz, "<init>", "(ILjava/lang/String;)V"); gFields.stateException.classId = static_cast<jclass>(env->NewGlobalRef(clazz)); + + FIND_CLASS(clazz, "android/media/MediaDrm$SessionException"); + GET_METHOD_ID(gFields.sessionException.init, clazz, "<init>", "(ILjava/lang/String;)V"); + gFields.sessionException.classId = static_cast<jclass>(env->NewGlobalRef(clazz)); + GET_FIELD_ID(gFields.sessionException.errorCode, clazz, "mErrorCode", "I"); + + GET_STATIC_FIELD_ID(field, clazz, "ERROR_RESOURCE_CONTENTION", "I"); + gSessionExceptionErrorCodes.kResourceContention = env->GetStaticIntField(clazz, field); } static void android_media_MediaDrm_native_setup( diff --git a/native/android/Android.bp b/native/android/Android.bp index fdcfc446a448..73d4c4522010 100644 --- a/native/android/Android.bp +++ b/native/android/Android.bp @@ -49,6 +49,7 @@ cc_library_shared { "sharedmem.cpp", "storage_manager.cpp", "surface_texture.cpp", + "surface_control.cpp", "system_fonts.cpp", "trace.cpp", ], diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index 537aed498cd2..8be8eda06a59 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -205,6 +205,9 @@ LIBANDROID { AStorageManager_mountObb; AStorageManager_new; AStorageManager_unmountObb; + ASurfaceControl_create; # introduced=29 + ASurfaceControl_createFromWindow; # introduced=29 + ASurfaceControl_destroy; # introduced=29 ASurfaceTexture_acquireANativeWindow; # introduced=28 ASurfaceTexture_attachToGLContext; # introduced=28 ASurfaceTexture_detachFromGLContext; # introduced=28 @@ -213,6 +216,16 @@ LIBANDROID { ASurfaceTexture_getTransformMatrix; # introduced=28 ASurfaceTexture_release; # introduced=28 ASurfaceTexture_updateTexImage; # introduced=28 + ASurfaceTransaction_apply; # introduced=29 + ASurfaceTransaction_create; # introduced=29 + ASurfaceTransaction_delete; # introduced=29 + ASurfaceTransaction_setBuffer; # introduced=29 + ASurfaceTransaction_setBufferTransparency; # introduced=29 + ASurfaceTransaction_setDamageRegion; # introduced=29 + ASurfaceTransaction_setGeometry; # introduced=29 + ASurfaceTransaction_setOnComplete; # introduced=29 + ASurfaceTransaction_setVisibility; # introduced=29 + ASurfaceTransaction_setZOrder; # introduced=29 ASystemFontIterator_open; # introduced=29 ASystemFontIterator_close; # introduced=29 ASystemFontIterator_next; # introduced=29 diff --git a/native/android/sharedmem.cpp b/native/android/sharedmem.cpp index 757aaecab40d..4410bd6fbeed 100644 --- a/native/android/sharedmem.cpp +++ b/native/android/sharedmem.cpp @@ -71,7 +71,7 @@ int ASharedMemory_dupFromJava(JNIEnv* env, jobject javaSharedMemory) { } int fd = env->CallIntMethod(javaSharedMemory, sSharedMemory.getFd); if (fd != -1) { - fd = dup(fd); + fd = fcntl(fd, F_DUPFD_CLOEXEC, 0); } return fd; } diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp new file mode 100644 index 000000000000..ead5b0b37f4b --- /dev/null +++ b/native/android/surface_control.cpp @@ -0,0 +1,232 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/native_window.h> +#include <android/surface_control.h> + +#include <gui/Surface.h> +#include <gui/SurfaceComposerClient.h> +#include <gui/SurfaceControl.h> + +using namespace android; + +using Transaction = SurfaceComposerClient::Transaction; + +#define CHECK_NOT_NULL(name) \ + LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument"); + +#define CHECK_VALID_RECT(name) \ + LOG_ALWAYS_FATAL_IF(!static_cast<const Rect&>(name).isValid(), \ + "invalid arg passed as " #name " argument"); + +Transaction* ASurfaceTransaction_to_Transaction(ASurfaceTransaction* aSurfaceTransaction) { + return reinterpret_cast<Transaction*>(aSurfaceTransaction); +} + +SurfaceControl* ASurfaceControl_to_SurfaceControl(ASurfaceControl* aSurfaceControl) { + return reinterpret_cast<SurfaceControl*>(aSurfaceControl); +} + +void SurfaceControl_acquire(SurfaceControl* surfaceControl) { + // incStrong/decStrong token must be the same, doesn't matter what it is + surfaceControl->incStrong((void*)SurfaceControl_acquire); +} + +void SurfaceControl_release(SurfaceControl* surfaceControl) { + // incStrong/decStrong token must be the same, doesn't matter what it is + surfaceControl->decStrong((void*)SurfaceControl_acquire); +} + +ASurfaceControl* ASurfaceControl_createFromWindow(ANativeWindow* window, const char* debug_name) { + CHECK_NOT_NULL(window); + CHECK_NOT_NULL(debug_name); + + sp<SurfaceComposerClient> client = new SurfaceComposerClient(); + if (client->initCheck() != NO_ERROR) { + return nullptr; + } + + uint32_t flags = ISurfaceComposerClient::eFXSurfaceBufferState; + sp<SurfaceControl> surfaceControl = + client->createWithSurfaceParent(String8(debug_name), 0 /* width */, 0 /* height */, + // Format is only relevant for buffer queue layers. + PIXEL_FORMAT_UNKNOWN /* format */, flags, + static_cast<Surface*>(window)); + if (!surfaceControl) { + return nullptr; + } + + SurfaceControl_acquire(surfaceControl.get()); + return reinterpret_cast<ASurfaceControl*>(surfaceControl.get()); +} + +ASurfaceControl* ASurfaceControl_create(ASurfaceControl* parent, const char* debug_name) { + CHECK_NOT_NULL(parent); + CHECK_NOT_NULL(debug_name); + + SurfaceComposerClient* client = ASurfaceControl_to_SurfaceControl(parent)->getClient().get(); + + SurfaceControl* surfaceControlParent = ASurfaceControl_to_SurfaceControl(parent); + + uint32_t flags = ISurfaceComposerClient::eFXSurfaceBufferState; + sp<SurfaceControl> surfaceControl = + client->createSurface(String8(debug_name), 0 /* width */, 0 /* height */, + // Format is only relevant for buffer queue layers. + PIXEL_FORMAT_UNKNOWN /* format */, flags, + surfaceControlParent); + if (!surfaceControl) { + return nullptr; + } + + SurfaceControl_acquire(surfaceControl.get()); + return reinterpret_cast<ASurfaceControl*>(surfaceControl.get()); +} + +void ASurfaceControl_destroy(ASurfaceControl* aSurfaceControl) { + sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl); + + Transaction().reparent(surfaceControl, nullptr).apply(); + SurfaceControl_release(surfaceControl.get()); +} + +ASurfaceTransaction* ASurfaceTransaction_create() { + Transaction* transaction = new Transaction; + return reinterpret_cast<ASurfaceTransaction*>(transaction); +} + +void ASurfaceTransaction_delete(ASurfaceTransaction* aSurfaceTransaction) { + Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction); + delete transaction; +} + +void ASurfaceTransaction_apply(ASurfaceTransaction* aSurfaceTransaction) { + CHECK_NOT_NULL(aSurfaceTransaction); + + Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction); + + transaction->apply(); +} + +void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* aSurfaceTransaction, void* context, + ASurfaceTransaction_OnComplete func) { + CHECK_NOT_NULL(aSurfaceTransaction); + CHECK_NOT_NULL(context); + CHECK_NOT_NULL(func); + + TransactionCompletedCallbackTakesContext callback = [func](void* callback_context, + const TransactionStats& stats) { + int fence = (stats.presentFence) ? stats.presentFence->dup() : -1; + (*func)(callback_context, fence); + }; + + Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction); + + transaction->addTransactionCompletedCallback(callback, context); +} + +void ASurfaceTransaction_setVisibility(ASurfaceTransaction* aSurfaceTransaction, ASurfaceControl* aSurfaceControl, + int8_t visibility) { + CHECK_NOT_NULL(aSurfaceTransaction); + CHECK_NOT_NULL(aSurfaceControl); + + sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl); + Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction); + + switch (visibility) { + case ASURFACE_TRANSACTION_VISIBILITY_SHOW: + transaction->show(surfaceControl); + break; + case ASURFACE_TRANSACTION_VISIBILITY_HIDE: + transaction->hide(surfaceControl); + break; + default: + LOG_ALWAYS_FATAL("invalid visibility %d", visibility); + } +} + +void ASurfaceTransaction_setZOrder(ASurfaceTransaction* aSurfaceTransaction, ASurfaceControl* aSurfaceControl, + int32_t z_order) { + CHECK_NOT_NULL(aSurfaceTransaction); + CHECK_NOT_NULL(aSurfaceControl); + + sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl); + Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction); + + transaction->setLayer(surfaceControl, z_order); +} + +void ASurfaceTransaction_setBuffer(ASurfaceTransaction* aSurfaceTransaction, ASurfaceControl* aSurfaceControl, + AHardwareBuffer* buffer, int fence_fd) { + CHECK_NOT_NULL(aSurfaceTransaction); + CHECK_NOT_NULL(aSurfaceControl); + + sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl); + Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction); + + sp<GraphicBuffer> graphic_buffer(reinterpret_cast<GraphicBuffer*>(buffer)); + + transaction->setBuffer(surfaceControl, graphic_buffer); + if (fence_fd != -1) { + sp<Fence> fence = new Fence(fence_fd); + transaction->setAcquireFence(surfaceControl, fence); + } +} + +void ASurfaceTransaction_setGeometry(ASurfaceTransaction* aSurfaceTransaction, + ASurfaceControl* aSurfaceControl, const ARect& source, + const ARect& destination, int32_t transform) { + CHECK_NOT_NULL(aSurfaceTransaction); + CHECK_NOT_NULL(aSurfaceControl); + CHECK_VALID_RECT(source); + CHECK_VALID_RECT(destination); + + sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl); + Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction); + + transaction->setCrop(surfaceControl, static_cast<const Rect&>(source)); + transaction->setFrame(surfaceControl, static_cast<const Rect&>(destination)); + transaction->setTransform(surfaceControl, transform); +} + +void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* aSurfaceTransaction, + ASurfaceControl* aSurfaceControl, + int8_t transparency) { + CHECK_NOT_NULL(aSurfaceTransaction); + CHECK_NOT_NULL(aSurfaceControl); + + sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl); + Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction); + + uint32_t flags = (transparency == ASURFACE_TRANSACTION_TRANSPARENCY_OPAQUE) ? + layer_state_t::eLayerOpaque : 0; + transaction->setFlags(surfaceControl, flags, layer_state_t::eLayerOpaque); +} + +void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* aSurfaceTransaction, ASurfaceControl* aSurfaceControl, + const ARect rects[], uint32_t count) { + CHECK_NOT_NULL(aSurfaceTransaction); + CHECK_NOT_NULL(aSurfaceControl); + + sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl); + Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction); + + Region region; + for (uint32_t i = 0; i < count; ++i) { + region.merge(static_cast<const Rect&>(rects[i])); + } + + transaction->setSurfaceDamageRegion(surfaceControl, region); +} diff --git a/native/webview/plat_support/draw_fn.h b/native/webview/plat_support/draw_fn.h index 6afd8837594c..bb2ee9b5da04 100644 --- a/native/webview/plat_support/draw_fn.h +++ b/native/webview/plat_support/draw_fn.h @@ -109,8 +109,15 @@ struct AwDrawFn_DrawVkParams { // Input: Format of the destination surface. VkFormat format; - // Input: Color space transformation from linear RGB to D50-adapted XYZ - float matrix[9]; + // Input: Color space parameters. + float transfer_function_g; + float transfer_function_a; + float transfer_function_b; + float transfer_function_c; + float transfer_function_d; + float transfer_function_e; + float transfer_function_f; + float color_space_toXYZD50[9]; // Input: current clip rect int clip_left; diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java index a9d8f62d53f4..95df5f269467 100644 --- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java +++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java @@ -27,6 +27,7 @@ import android.os.Process; import android.service.notification.NotificationAssistantService; import android.text.TextUtils; import android.util.LruCache; +import android.view.textclassifier.ConversationAction; import android.view.textclassifier.ConversationActions; import android.view.textclassifier.TextClassification; import android.view.textclassifier.TextClassificationContext; @@ -65,13 +66,13 @@ public class SmartActionsHelper { 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( - Collections.singletonList(ConversationActions.TYPE_TEXT_REPLY)) + private static final TextClassifier.EntityConfig TYPE_CONFIG = + new TextClassifier.EntityConfig.Builder().setIncludedTypes( + Collections.singletonList(ConversationAction.TYPE_TEXT_REPLY)) .includeTypesFromTextClassifier(false) .build(); private static final List<String> HINTS = - Collections.singletonList(ConversationActions.HINT_FOR_NOTIFICATION); + Collections.singletonList(ConversationActions.Request.HINT_FOR_NOTIFICATION); private Context mContext; @Nullable @@ -137,7 +138,7 @@ public class SmartActionsHelper { ConversationActions conversationActionsResult = mTextClassifier.suggestConversationActions(request); - List<ConversationActions.ConversationAction> conversationActions = + List<ConversationAction> conversationActions = conversationActionsResult.getConversationActions(); ArrayList<CharSequence> replies = conversationActions.stream() .map(conversationAction -> conversationAction.getTextReply()) @@ -193,7 +194,7 @@ public class SmartActionsHelper { } TextClassifierEvent textClassifierEvent = createTextClassifierEventBuilder(TextClassifierEvent.TYPE_SMART_ACTION, resultId) - .setEntityType(ConversationActions.TYPE_TEXT_REPLY) + .setEntityType(ConversationAction.TYPE_TEXT_REPLY) .build(); mTextClassifier.onTextClassifierEvent(textClassifierEvent); } 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 7d74788e21c0..7b7ce3d87f31 100644 --- a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java +++ b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java @@ -32,6 +32,7 @@ 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.ConversationAction; import android.view.textclassifier.ConversationActions; import android.view.textclassifier.TextClassificationManager; import android.view.textclassifier.TextClassifier; @@ -65,9 +66,10 @@ public class SmartActionHelperTest { private static final String NOTIFICATION_KEY = "key"; private static final String RESULT_ID = "id"; - private static final ConversationActions.ConversationAction REPLY_ACTION = - new ConversationActions.ConversationAction.Builder( - ConversationActions.TYPE_TEXT_REPLY).setTextReply("Home").build(); + private static final ConversationAction REPLY_ACTION = + new ConversationAction.Builder(ConversationAction.TYPE_TEXT_REPLY) + .setTextReply("Home") + .build(); private SmartActionsHelper mSmartActionsHelper; private Context mContext; diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml index 0b0f1eca7aa5..7f8bb93ae023 100644 --- a/packages/NetworkStack/AndroidManifest.xml +++ b/packages/NetworkStack/AndroidManifest.xml @@ -20,6 +20,7 @@ package="com.android.mainline.networkstack" android:sharedUserId="android.uid.networkstack"> <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" /> diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java index 94ea1b931348..4077d93d700b 100644 --- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java @@ -545,7 +545,9 @@ public class NetworkMonitor extends StateMachine { return HANDLED; case CMD_FORCE_REEVALUATION: case CMD_CAPTIVE_PORTAL_RECHECK: - log("Forcing reevaluation for UID " + message.arg1); + final int dnsCount = mDnsStallDetector.getConsecutiveTimeoutCount(); + validationLog("Forcing reevaluation for UID " + message.arg1 + + ". Dns signal count: " + dnsCount); mUidResponsibleForReeval = message.arg1; transitionTo(mEvaluatingState); return HANDLED; diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 03c620580423..a29cf8449e51 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -99,6 +99,8 @@ <string name="connected_via_network_scorer_default">Automatically connected via network rating provider</string> <!-- Status message of Wi-Fi when it is connected by Passpoint configuration. [CHAR LIMIT=NONE] --> <string name="connected_via_passpoint">Connected via %1$s</string> + <!-- Status message of Wi-Fi when it is connected by Passpoint configuration. [CHAR LIMIT=NONE] --> + <string name="ssid_by_passpoint_provider"><xliff:g id="ssid" example="Cafe Wifi">%1$s</xliff:g> by <xliff:g id="passpointProvider" example="Passpoint Provider">%2$s</xliff:g></string> <!-- Status message of Wi-Fi when network has matching passpoint credentials. [CHAR LIMIT=NONE] --> <string name="available_via_passpoint">Available via %1$s</string> <!-- Package name for Settings app--> diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java index aed02a268aea..8090169a4245 100644 --- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java +++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java @@ -41,10 +41,6 @@ public class VisibilityLoggerMixin implements LifecycleObserver { private int mSourceMetricsCategory = MetricsProto.MetricsEvent.VIEW_UNKNOWN; private long mVisibleTimestamp; - private VisibilityLoggerMixin() { - mMetricsCategory = METRICS_CATEGORY_UNKNOWN; - } - public VisibilityLoggerMixin(int metricsCategory, MetricsFeatureProvider metricsFeature) { mMetricsCategory = metricsCategory; mMetricsFeature = metricsFeature; @@ -81,12 +77,4 @@ public class VisibilityLoggerMixin implements LifecycleObserver { MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, MetricsProto.MetricsEvent.VIEW_UNKNOWN); } - - /** Returns elapsed time since onResume() */ - public long elapsedTimeSinceVisible() { - if (mVisibleTimestamp == 0) { - return 0; - } - return SystemClock.elapsedRealtime() - mVisibleTimestamp; - } } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index 4f81dafe7305..1ae1d56aacc8 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -41,6 +41,7 @@ import android.net.wifi.WifiEnterpriseConfig; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiNetworkScoreCache; +import android.net.wifi.hotspot2.OsuProvider; import android.net.wifi.hotspot2.PasspointConfiguration; import android.os.Bundle; import android.os.Parcelable; @@ -182,6 +183,10 @@ public class AccessPoint implements Comparable<AccessPoint> { public static final int UNREACHABLE_RSSI = Integer.MIN_VALUE; + public static final String KEY_PREFIX_AP = "AP:"; + public static final String KEY_PREFIX_FQDN = "FQDN:"; + public static final String KEY_PREFIX_OSU = "OSU:"; + private final Context mContext; private String ssid; @@ -204,9 +209,6 @@ public class AccessPoint implements Comparable<AccessPoint> { @Speed private int mSpeed = Speed.NONE; private boolean mIsScoredNetworkMetered = false; - // used to co-relate internal vs returned accesspoint. - int mId; - /** * Information associated with the {@link PasspointConfiguration}. Only maintaining * the relevant info to preserve spaces. @@ -215,6 +217,8 @@ public class AccessPoint implements Comparable<AccessPoint> { private String mProviderFriendlyName; private boolean mIsCarrierAp = false; + + private OsuProvider mOsuProvider; /** * The EAP type {@link WifiEnterpriseConfig.Eap} associated with this AP if it is a carrier AP. */ @@ -280,14 +284,18 @@ public class AccessPoint implements Comparable<AccessPoint> { // Calculate required fields updateKey(); updateRssi(); - - mId = sLastId.incrementAndGet(); } + /** + * Creates an AccessPoint with only a WifiConfiguration. This is used for the saved networks + * page. + * + * Passpoint Credential AccessPoints should be created with this. + * Make sure to call setScanResults after constructing with this. + */ public AccessPoint(Context context, WifiConfiguration config) { mContext = context; loadConfig(config); - mId = sLastId.incrementAndGet(); } /** @@ -298,7 +306,19 @@ public class AccessPoint implements Comparable<AccessPoint> { mContext = context; mFqdn = config.getHomeSp().getFqdn(); mProviderFriendlyName = config.getHomeSp().getFriendlyName(); - mId = sLastId.incrementAndGet(); + } + + /** + * Initialize an AccessPoint object for a Passpoint OSU Provider. + * Make sure to call setScanResults after constructing with this. + */ + public AccessPoint(Context context, OsuProvider provider) { + mContext = context; + mOsuProvider = provider; + mRssi = 1; + // TODO: This placeholder SSID is here to avoid null pointer exceptions. + ssid = "<OsuProvider AP SSID goes here>"; + updateKey(); } AccessPoint(Context context, Collection<ScanResult> results) { @@ -324,17 +344,15 @@ public class AccessPoint implements Comparable<AccessPoint> { mIsCarrierAp = firstResult.isCarrierAp; mCarrierApEapType = firstResult.carrierApEapType; mCarrierName = firstResult.carrierName; - - mId = sLastId.incrementAndGet(); } @VisibleForTesting void loadConfig(WifiConfiguration config) { ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID)); bssid = config.BSSID; security = getSecurity(config); - updateKey(); networkId = config.networkId; mConfig = config; + updateKey(); } /** Updates {@link #mKey} and should only called upon object creation/initialization. */ @@ -343,13 +361,20 @@ public class AccessPoint implements Comparable<AccessPoint> { StringBuilder builder = new StringBuilder(); - if (TextUtils.isEmpty(getSsidStr())) { - builder.append(getBssid()); - } else { - builder.append(getSsidStr()); + if (isPasspoint()) { + builder.append(KEY_PREFIX_FQDN).append(mConfig.FQDN); + } else if (isOsuProvider()) { + builder.append(KEY_PREFIX_OSU).append(mOsuProvider.getOsuSsid()); + builder.append(',').append(mOsuProvider.getServerUri()); + } else { // Non-Passpoint AP + builder.append(KEY_PREFIX_AP); + if (TextUtils.isEmpty(getSsidStr())) { + builder.append(getBssid()); + } else { + builder.append(getSsidStr()); + } + builder.append(',').append(getSecurity()); } - - builder.append(',').append(getSecurity()); mKey = builder.toString(); } @@ -394,8 +419,8 @@ public class AccessPoint implements Comparable<AccessPoint> { return difference; } - // Sort by ssid. - difference = getSsidStr().compareToIgnoreCase(other.getSsidStr()); + // Sort by title. + difference = getTitle().compareToIgnoreCase(other.getTitle()); if (difference != 0) { return difference; } @@ -593,6 +618,7 @@ public class AccessPoint implements Comparable<AccessPoint> { public static String getKey(ScanResult result) { StringBuilder builder = new StringBuilder(); + builder.append(KEY_PREFIX_AP); if (TextUtils.isEmpty(result.SSID)) { builder.append(result.BSSID); } else { @@ -606,13 +632,18 @@ public class AccessPoint implements Comparable<AccessPoint> { public static String getKey(WifiConfiguration config) { StringBuilder builder = new StringBuilder(); - if (TextUtils.isEmpty(config.SSID)) { - builder.append(config.BSSID); + if (config.isPasspoint()) { + builder.append(KEY_PREFIX_FQDN).append(config.FQDN); } else { - builder.append(removeDoubleQuotes(config.SSID)); + builder.append(KEY_PREFIX_AP); + if (TextUtils.isEmpty(config.SSID)) { + builder.append(config.BSSID); + } else { + builder.append(removeDoubleQuotes(config.SSID)); + } + builder.append(',').append(getSecurity(config)); } - builder.append(',').append(getSecurity(config)); return builder.toString(); } @@ -621,9 +652,10 @@ public class AccessPoint implements Comparable<AccessPoint> { } public boolean matches(WifiConfiguration config) { - if (config.isPasspoint() && mConfig != null && mConfig.isPasspoint()) { - return ssid.equals(removeDoubleQuotes(config.SSID)) && config.FQDN.equals(mConfig.FQDN); + if (config.isPasspoint()) { + return (isPasspoint() && config.FQDN.equals(mConfig.FQDN)); } else { + // Normal non-Passpoint network return ssid.equals(removeDoubleQuotes(config.SSID)) && security == getSecurity(config) && (mConfig == null || mConfig.shared == config.shared); @@ -828,86 +860,101 @@ public class AccessPoint implements Comparable<AccessPoint> { return ""; } - public String getSummary() { - return getSettingsSummary(mConfig); + /** + * Returns the display title for the AccessPoint, such as for an AccessPointPreference's title. + */ + public String getTitle() { + if (isPasspoint()) { + return mConfig.providerFriendlyName; + } else if (isOsuProvider()) { + return mOsuProvider.getFriendlyName(); + } else { + return getSsidStr(); + } } - public String getSettingsSummary() { - return getSettingsSummary(mConfig); + public String getSummary() { + return getSettingsSummary(); } - private String getSettingsSummary(WifiConfiguration config) { + public String getSettingsSummary() { // Update to new summary StringBuilder summary = new StringBuilder(); - if (isActive() && config != null && config.isPasspoint()) { - // This is the active connection on passpoint - summary.append(getSummary(mContext, getDetailedState(), - false, config.providerFriendlyName)); - } else if (isActive() && config != null && getDetailedState() == DetailedState.CONNECTED - && mIsCarrierAp) { - summary.append(String.format(mContext.getString(R.string.connected_via_carrier), mCarrierName)); - } else if (isActive()) { - // This is the active connection on non-passpoint network - summary.append(getSummary(mContext, getDetailedState(), - mInfo != null && mInfo.isEphemeral())); - } else if (config != null && config.isPasspoint() - && config.getNetworkSelectionStatus().isNetworkEnabled()) { - String format = mContext.getString(R.string.available_via_passpoint); - summary.append(String.format(format, config.providerFriendlyName)); - } else if (config != null && config.hasNoInternetAccess()) { - int messageID = config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled() - ? R.string.wifi_no_internet_no_reconnect - : R.string.wifi_no_internet; - summary.append(mContext.getString(messageID)); - } else if (config != null && !config.getNetworkSelectionStatus().isNetworkEnabled()) { - WifiConfiguration.NetworkSelectionStatus networkStatus = - config.getNetworkSelectionStatus(); - switch (networkStatus.getNetworkSelectionDisableReason()) { - case WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE: - summary.append(mContext.getString(R.string.wifi_disabled_password_failure)); - break; - case WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD: - summary.append(mContext.getString(R.string.wifi_check_password_try_again)); - break; - case WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE: - case WifiConfiguration.NetworkSelectionStatus.DISABLED_DNS_FAILURE: - summary.append(mContext.getString(R.string.wifi_disabled_network_failure)); - break; - case WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION: - summary.append(mContext.getString(R.string.wifi_disabled_generic)); - break; + if (isActive()) { + if (isPasspoint()) { + // This is the active connection on passpoint + summary.append(getSummary(mContext, ssid, getDetailedState(), + false, mConfig.providerFriendlyName)); + } else if (mConfig != null && getDetailedState() == DetailedState.CONNECTED + && mIsCarrierAp) { + // This is the active connection on a carrier AP + summary.append(String.format(mContext.getString(R.string.connected_via_carrier), + mCarrierName)); + } else { + // This is the active connection on non-passpoint network + summary.append(getSummary(mContext, getDetailedState(), + mInfo != null && mInfo.isEphemeral())); } - } else if (config != null && config.getNetworkSelectionStatus().isNotRecommended()) { - summary.append(mContext.getString(R.string.wifi_disabled_by_recommendation_provider)); - } else if (mIsCarrierAp) { - summary.append(String.format(mContext.getString(R.string.available_via_carrier), mCarrierName)); - } else if (!isReachable()) { // Wifi out of range - summary.append(mContext.getString(R.string.wifi_not_in_range)); - } else { // In range, not disabled. - if (config != null) { // Is saved network - // Last attempt to connect to this failed. Show reason why - switch (config.recentFailure.getAssociationStatus()) { - case WifiConfiguration.RecentFailure.STATUS_AP_UNABLE_TO_HANDLE_NEW_STA: - summary.append(mContext.getString( - R.string.wifi_ap_unable_to_handle_new_sta)); + } else { // not active + if (mConfig != null && mConfig.hasNoInternetAccess()) { + int messageID = mConfig.getNetworkSelectionStatus().isNetworkPermanentlyDisabled() + ? R.string.wifi_no_internet_no_reconnect + : R.string.wifi_no_internet; + summary.append(mContext.getString(messageID)); + } else if (mConfig != null && !mConfig.getNetworkSelectionStatus().isNetworkEnabled()) { + WifiConfiguration.NetworkSelectionStatus networkStatus = + mConfig.getNetworkSelectionStatus(); + switch (networkStatus.getNetworkSelectionDisableReason()) { + case WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE: + summary.append(mContext.getString(R.string.wifi_disabled_password_failure)); break; - default: - // "Saved" - summary.append(mContext.getString(R.string.wifi_remembered)); + case WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD: + summary.append(mContext.getString(R.string.wifi_check_password_try_again)); break; + case WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE: + case WifiConfiguration.NetworkSelectionStatus.DISABLED_DNS_FAILURE: + summary.append(mContext.getString(R.string.wifi_disabled_network_failure)); + break; + case WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION: + summary.append(mContext.getString(R.string.wifi_disabled_generic)); + break; + } + } else if (mConfig != null && mConfig.getNetworkSelectionStatus().isNotRecommended()) { + summary.append(mContext.getString( + R.string.wifi_disabled_by_recommendation_provider)); + } else if (mIsCarrierAp) { + summary.append(String.format(mContext.getString( + R.string.available_via_carrier), mCarrierName)); + } else if (!isReachable()) { // Wifi out of range + summary.append(mContext.getString(R.string.wifi_not_in_range)); + } else { // In range, not disabled. + if (mConfig != null) { // Is saved network + // Last attempt to connect to this failed. Show reason why + switch (mConfig.recentFailure.getAssociationStatus()) { + case WifiConfiguration.RecentFailure.STATUS_AP_UNABLE_TO_HANDLE_NEW_STA: + summary.append(mContext.getString( + R.string.wifi_ap_unable_to_handle_new_sta)); + break; + default: + // "Saved" + summary.append(mContext.getString(R.string.wifi_remembered)); + break; + } } } } + + if (isVerboseLoggingEnabled()) { - summary.append(WifiUtils.buildLoggingSummary(this, config)); + summary.append(WifiUtils.buildLoggingSummary(this, mConfig)); } - if (config != null && (WifiUtils.isMeteredOverridden(config) || config.meteredHint)) { + if (mConfig != null && (WifiUtils.isMeteredOverridden(mConfig) || mConfig.meteredHint)) { return mContext.getResources().getString( R.string.preference_summary_default_combination, - WifiUtils.getMeteredLabel(mContext, config), + WifiUtils.getMeteredLabel(mContext, mConfig), summary.toString()); } @@ -960,6 +1007,13 @@ public class AccessPoint implements Comparable<AccessPoint> { } /** + * Return true if this AccessPoint represents an OSU Provider. + */ + public boolean isOsuProvider() { + return mOsuProvider != null; + } + + /** * Return whether the given {@link WifiInfo} is for this access point. * If the current AP does not have a network Id then the config is used to * match based on SSID and security. @@ -1048,18 +1102,21 @@ public class AccessPoint implements Comparable<AccessPoint> { */ void setScanResults(Collection<ScanResult> scanResults) { - // Validate scan results are for current AP only - String key = getKey(); - for (ScanResult result : scanResults) { - String scanResultKey = AccessPoint.getKey(result); - if (!mKey.equals(scanResultKey)) { - throw new IllegalArgumentException( - String.format("ScanResult %s\nkey of %s did not match current AP key %s", - result, scanResultKey, key)); + // Validate scan results are for current AP only by matching SSID/BSSID + // Passpoint networks are not bound to a specific SSID/BSSID, so skip this for passpoint. + if (!isPasspoint() && !isOsuProvider()) { + String key = getKey(); + for (ScanResult result : scanResults) { + String scanResultKey = AccessPoint.getKey(result); + if (!mKey.equals(scanResultKey)) { + throw new IllegalArgumentException( + String.format( + "ScanResult %s\nkey of %s did not match current AP key %s", + result, scanResultKey, key)); + } } } - int oldLevel = getLevel(); mScanResults.clear(); mScanResults.addAll(scanResults); @@ -1100,7 +1157,17 @@ public class AccessPoint implements Comparable<AccessPoint> { } } - /** Attempt to update the AccessPoint and return true if an update occurred. */ + /** + * Attempt to update the AccessPoint with the current connection info. + * This is used to set an AccessPoint to the active one if the connection info matches, or + * conversely to set an AccessPoint to inactive if the connection info does not match. The RSSI + * is also updated upon a match. Listeners will be notified if an update occurred. + * + * This is called in {@link WifiTracker#updateAccessPoints} as well as in callbacks for handling + * NETWORK_STATE_CHANGED_ACTION, RSSI_CHANGED_ACTION, and onCapabilitiesChanged in WifiTracker. + * + * Returns true if an update occurred. + */ public boolean update( @Nullable WifiConfiguration config, WifiInfo info, NetworkInfo networkInfo) { @@ -1149,6 +1216,9 @@ public class AccessPoint implements Comparable<AccessPoint> { void update(@Nullable WifiConfiguration config) { mConfig = config; + if (mConfig != null) { + ssid = removeDoubleQuotes(mConfig.SSID); + } networkId = config != null ? config.networkId : WifiConfiguration.INVALID_NETWORK_ID; ThreadUtils.postOnMainThread(() -> { if (mAccessPointListener != null) { @@ -1224,11 +1294,11 @@ public class AccessPoint implements Comparable<AccessPoint> { public static String getSummary(Context context, String ssid, DetailedState state, boolean isEphemeral, String passpointProvider) { - if (state == DetailedState.CONNECTED && ssid == null) { - if (TextUtils.isEmpty(passpointProvider) == false) { + if (state == DetailedState.CONNECTED) { + if (!TextUtils.isEmpty(passpointProvider)) { // Special case for connected + passpoint networks. - String format = context.getString(R.string.connected_via_passpoint); - return String.format(format, passpointProvider); + String format = context.getString(R.string.ssid_by_passpoint_provider); + return String.format(format, ssid, passpointProvider); } else if (isEphemeral) { // Special case for connected + ephemeral networks. final NetworkScoreManager networkScoreManager = context.getSystemService( diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java index db364a3b75e5..1fa7083498e1 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java @@ -266,7 +266,7 @@ public class AccessPointPreference extends Preference { if (savedNetworks) { preference.setTitle(ap.getConfigName()); } else { - preference.setTitle(ap.getSsidStr()); + preference.setTitle(ap.getTitle()); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java index e47ca3226cfb..6d2889121c64 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java @@ -35,6 +35,7 @@ import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiNetworkScoreCache; import android.net.wifi.WifiNetworkScoreCache.CacheListener; +import android.net.wifi.hotspot2.OsuProvider; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; @@ -45,6 +46,7 @@ import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; +import android.util.Pair; import android.widget.Toast; import androidx.annotation.GuardedBy; @@ -573,9 +575,69 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro accessPoints.add(accessPoint); } + List<ScanResult> cachedScanResults = new ArrayList<>(mScanResultCache.values()); + + // Add a unique Passpoint R1 AccessPoint for each Passpoint profile's FQDN. + List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> passpointConfigsAndScans = + mWifiManager.getAllMatchingWifiConfigs(cachedScanResults); + Set<String> seenFQDNs = new ArraySet<>(); + for (Pair<WifiConfiguration, + Map<Integer, List<ScanResult>>> pairing : passpointConfigsAndScans) { + WifiConfiguration config = pairing.first; + + // TODO(b/118705403): Prioritize home networks before roaming networks + List<ScanResult> scanResults = new ArrayList<>(); + + List<ScanResult> homeScans = + pairing.second.get(WifiManager.PASSPOINT_HOME_NETWORK); + List<ScanResult> roamingScans = + pairing.second.get(WifiManager.PASSPOINT_ROAMING_NETWORK); + + if (homeScans == null) { + homeScans = new ArrayList<>(); + } + if (roamingScans == null) { + roamingScans = new ArrayList<>(); + } + + scanResults.addAll(homeScans); + scanResults.addAll(roamingScans); + + if (seenFQDNs.add(config.FQDN)) { + int bestRssi = Integer.MIN_VALUE; + for (ScanResult result : scanResults) { + if (result.level >= bestRssi) { + bestRssi = result.level; + config.SSID = AccessPoint.convertToQuotedString(result.SSID); + } + } + + AccessPoint accessPoint = new AccessPoint(mContext, config); + accessPoint.setScanResults(scanResults); + accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo); + accessPoints.add(accessPoint); + } + } + + // Add Passpoint OSU Provider AccessPoints + // TODO(b/118705403): filter out OSU Providers which we already have credentials from. + Map<OsuProvider, List<ScanResult>> providersAndScans = + mWifiManager.getMatchingOsuProviders(cachedScanResults); + for (OsuProvider provider : providersAndScans.keySet()) { + AccessPoint accessPointOsu = new AccessPoint(mContext, provider); + // TODO(b/118705403): accessPointOsu.setScanResults(Matching ScanResult with best + // RSSI) + // TODO(b/118705403): Figure out if we would need to update an OSU AP (this will be + // used if we need to display it at the top of the picker as the "active" AP). + // Otherwise, OSU APs should ignore attempts to update the active connection + // info. + // accessPointOsu.update(connectionConfig, mLastInfo, mLastNetworkInfo); + accessPoints.add(accessPointOsu); + } + + // If there were no scan results, create an AP for the currently connected network (if // it exists). - // TODO(b/b/73076869): Add support for passpoint (ephemeral) networks if (accessPoints.isEmpty() && connectionConfig != null) { AccessPoint activeAp = new AccessPoint(mContext, connectionConfig); activeAp.update(connectionConfig, mLastInfo, mLastNetworkInfo); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index e853399133aa..ef90dc981870 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -28,8 +28,8 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteStatement; -import android.media.AudioSystem; import android.media.AudioManager; +import android.media.AudioSystem; import android.net.ConnectivityManager; import android.os.Build; import android.os.Environment; @@ -1104,9 +1104,7 @@ class DatabaseHelper extends SQLiteOpenHelper { } if (upgradeVersion == 77) { - // Introduce "vibrate when ringing" setting - loadVibrateWhenRingingSetting(db); - + // "vibrate when ringing" setting moved to SettingsProvider version 168 upgradeVersion = 78; } @@ -2223,8 +2221,6 @@ class DatabaseHelper extends SQLiteOpenHelper { } finally { if (stmt != null) stmt.close(); } - - loadVibrateWhenRingingSetting(db); } private void loadVibrateSetting(SQLiteDatabase db, boolean deleteOld) { @@ -2250,24 +2246,6 @@ class DatabaseHelper extends SQLiteOpenHelper { } } - private void loadVibrateWhenRingingSetting(SQLiteDatabase db) { - // The default should be off. VIBRATE_SETTING_ONLY_SILENT should also be ignored here. - // Phone app should separately check whether AudioManager#getRingerMode() returns - // RINGER_MODE_VIBRATE, with which the device should vibrate anyway. - int vibrateSetting = getIntValueFromSystem(db, Settings.System.VIBRATE_ON, - AudioManager.VIBRATE_SETTING_OFF); - boolean vibrateWhenRinging = ((vibrateSetting & 3) == AudioManager.VIBRATE_SETTING_ON); - - SQLiteStatement stmt = null; - try { - stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)" - + " VALUES(?,?);"); - loadSetting(stmt, Settings.System.VIBRATE_WHEN_RINGING, vibrateWhenRinging ? 1 : 0); - } finally { - if (stmt != null) stmt.close(); - } - } - private void loadSettings(SQLiteDatabase db) { loadSystemSettings(db); loadSecureSettings(db); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index bcf37ffef262..18bdb20ad0a6 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -2363,6 +2363,14 @@ class SettingsProtoDumpUtil { SecureSettingsProto.Zen.SETTINGS_SUGGESTION_VIEWED); p.end(zenToken); + dumpSetting(s, p, + Settings.Secure.SKIP_GESTURE, + SecureSettingsProto.SKIP_GESTURE_ENABLED); + + dumpSetting(s, p, + Settings.Secure.SILENCE_GESTURE, + SecureSettingsProto.SILENCE_GESTURE_ENABLED); + // Please insert new settings using the same order as in SecureSettingsProto. p.end(token); diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index fa95bf2ee302..9b775e058ea8 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -129,6 +129,7 @@ <uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" /> <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" /> + <uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" /> <uses-permission android:name="android.permission.ACTIVITY_EMBEDDING" /> <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" /> <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" /> diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index 2530abc765da..afb978174784 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -56,6 +56,7 @@ import com.google.android.collect.Lists; import android.accounts.Account; import android.accounts.AccountManager; import android.annotation.MainThread; +import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.AlertDialog; import android.app.Notification; @@ -799,6 +800,18 @@ public class BugreportProgressService extends Service { Log.wtf(TAG, "Missing " + EXTRA_BUGREPORT + " on intent " + intent); return; } + final int max = intent.getIntExtra(EXTRA_MAX, -1); + final File screenshotFile = getFileExtra(intent, EXTRA_SCREENSHOT); + final String shareTitle = intent.getStringExtra(EXTRA_TITLE); + final String shareDescription = intent.getStringExtra(EXTRA_DESCRIPTION); + onBugreportFinished(id, bugreportFile, screenshotFile, shareTitle, shareDescription, max); + } + + /** + * Wraps up bugreport generation and triggers a notification to share the bugreport. + */ + private void onBugreportFinished(int id, File bugreportFile, @Nullable File screenshotFile, + String shareTitle, String shareDescription, int max) { mInfoDialog.onBugreportFinished(); BugreportInfo info = getInfo(id); if (info == null) { @@ -809,22 +822,17 @@ public class BugreportProgressService extends Service { } info.renameScreenshots(mScreenshotsDir); info.bugreportFile = bugreportFile; + if (screenshotFile != null) { + info.addScreenshot(screenshotFile); + } - final int max = intent.getIntExtra(EXTRA_MAX, -1); if (max != -1) { MetricsLogger.histogram(this, "dumpstate_duration", max); info.max = max; } - final File screenshot = getFileExtra(intent, EXTRA_SCREENSHOT); - if (screenshot != null) { - info.addScreenshot(screenshot); - } - - final String shareTitle = intent.getStringExtra(EXTRA_TITLE); if (!TextUtils.isEmpty(shareTitle)) { info.title = shareTitle; - final String shareDescription = intent.getStringExtra(EXTRA_DESCRIPTION); if (!TextUtils.isEmpty(shareDescription)) { info.shareDescription= shareDescription; } @@ -1944,6 +1952,23 @@ public class BugreportProgressService extends Service { } @Override + public void onProgress(int progress) throws RemoteException { + // TODO(b/111441001): change max argument? + updateProgressInfo(progress, CAPPED_MAX); + } + + @Override + public void onError(int errorCode) throws RemoteException { + // TODO(b/111441001): implement + } + + @Override + public void onFinished(long durationMs, String title, String description) + throws RemoteException { + // TODO(b/111441001): implement + } + + @Override public void onProgressUpdated(int progress) throws RemoteException { /* * Checks whether the progress changed in a way that should be displayed to the user: @@ -1964,21 +1989,7 @@ public class BugreportProgressService extends Service { } if (newPercentage > oldPercentage) { - if (DEBUG) { - if (progress != info.progress) { - Log.v(TAG, "Updating progress for PID " + info.pid + "(id: " + info.id - + ") from " + info.progress + " to " + progress); - } - if (max != info.max) { - Log.v(TAG, "Updating max progress for PID " + info.pid + "(id: " + info.id - + ") from " + info.max + " to " + max); - } - } - info.progress = progress; - info.max = max; - info.lastUpdate = System.currentTimeMillis(); - - updateProgress(info); + updateProgressInfo(progress, max); } } @@ -2000,5 +2011,23 @@ public class BugreportProgressService extends Service { public void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("token: "); pw.println(token); } + + private void updateProgressInfo(int progress, int max) { + if (DEBUG) { + if (progress != info.progress) { + Log.v(TAG, "Updating progress for PID " + info.pid + "(id: " + info.id + + ") from " + info.progress + " to " + progress); + } + if (max != info.max) { + Log.v(TAG, "Updating max progress for PID " + info.pid + "(id: " + info.id + + ") from " + info.max + " to " + max); + } + } + info.progress = progress; + info.max = max; + info.lastUpdate = System.currentTimeMillis(); + + updateProgress(info); + } } } diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 1c1a1404eacd..b4f2711ef9d2 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -87,6 +87,7 @@ <uses-permission android:name="android.permission.STOP_APP_SWITCHES" /> <uses-permission android:name="android.permission.SET_SCREEN_COMPATIBILITY" /> <uses-permission android:name="android.permission.START_ANY_ACTIVITY" /> + <uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" /> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> <uses-permission android:name="android.permission.GET_TOP_ACTIVITY_INFO" /> diff --git a/packages/SystemUI/res-keyguard/drawable/bubble_hour_hand.xml b/packages/SystemUI/res-keyguard/drawable/bubble_hour_hand.xml new file mode 100644 index 000000000000..d3c3a518f6ac --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/bubble_hour_hand.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="200dp" + android:width="200dp" + android:viewportHeight="100" + android:viewportWidth="100"> + <path + android:fillColor="#000000" + android:pathData="M50.082,14.199m-13.985,0a13.985,13.985 0,1 1,27.97 0a13.985,13.985 0,1 1,-27.97 0"/> +</vector> diff --git a/packages/SystemUI/res-keyguard/drawable/bubble_minute_hand.xml b/packages/SystemUI/res-keyguard/drawable/bubble_minute_hand.xml new file mode 100644 index 000000000000..a4417fb65da4 --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/bubble_minute_hand.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="200dp" + android:width="200dp" + android:viewportHeight="100" + android:viewportWidth="100" > + <path + android:fillColor="#000000" + android:pathData="M50.082,0.025L50.082,0.025A13.985,15.63 0,0 1,64.067 15.656L64.067,49.029A13.985,15.63 0,0 1,50.082 64.659L50.082,64.659A13.985,15.63 0,0 1,36.097 49.029L36.097,15.656A13.985,15.63 0,0 1,50.082 0.025z"/> +</vector> diff --git a/packages/SystemUI/res-keyguard/layout/bubble_clock.xml b/packages/SystemUI/res-keyguard/layout/bubble_clock.xml new file mode 100644 index 000000000000..0d72657bb2df --- /dev/null +++ b/packages/SystemUI/res-keyguard/layout/bubble_clock.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<com.android.keyguard.clock.ClockLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + > + <TextClock + android:id="@+id/digital_clock" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:letterSpacing="0.03" + android:singleLine="true" + style="@style/widget_big" + android:format12Hour="@string/keyguard_widget_12_hours_format" + android:format24Hour="@string/keyguard_widget_24_hours_format" + /> + <com.android.keyguard.clock.ImageClock + android:id="@+id/analog_clock" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + > + <ImageView + android:id="@+id/minute_hand" + android:layout_width="300dp" + android:layout_height="300dp" + android:src="@drawable/bubble_minute_hand" + android:tint="@color/bubbleMinuteHandColor" + /> + <ImageView + android:id="@+id/hour_hand" + android:layout_width="300dp" + android:layout_height="300dp" + android:src="@drawable/bubble_hour_hand" + android:tint="@color/bubbleHourHandColor" + /> + </com.android.keyguard.clock.ImageClock> +</com.android.keyguard.clock.ClockLayout> diff --git a/packages/SystemUI/res-keyguard/layout/digital_clock.xml b/packages/SystemUI/res-keyguard/layout/digital_clock.xml new file mode 100644 index 000000000000..cf4a573834dd --- /dev/null +++ b/packages/SystemUI/res-keyguard/layout/digital_clock.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:layout_alignParentTop="true"> + <TextClock + android:id="@+id/lock_screen_clock" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:letterSpacing="0.03" + android:singleLine="true" + style="@style/widget_big" + android:format12Hour="@string/keyguard_widget_12_hours_format" + android:format24Hour="@string/keyguard_widget_24_hours_format" /> + /> +</FrameLayout> + diff --git a/packages/SystemUI/res-keyguard/values/colors.xml b/packages/SystemUI/res-keyguard/values/colors.xml new file mode 100644 index 000000000000..7a849ebd481d --- /dev/null +++ b/packages/SystemUI/res-keyguard/values/colors.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<resources> + <!-- Default color for hour hand of Bubble clock. --> + <color name="bubbleHourHandColor">#C97343</color> + <!-- Default color for minute hand of Bubble clock. --> + <color name="bubbleMinuteHandColor">#F5C983</color> +</resources> diff --git a/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml b/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml index ecfbfb434800..c8e0845a9144 100644 --- a/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml +++ b/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml @@ -25,12 +25,18 @@ android:focusable="true" android:layout_gravity="center_vertical"> - <ImageView - android:id="@+id/app_icon" + <FrameLayout android:layout_height="@dimen/ongoing_appops_dialog_app_icon_size" android:layout_width="@dimen/ongoing_appops_dialog_app_icon_size" - android:layout_gravity="start|center_vertical" - /> + android:layout_gravity="start|center_vertical"> + + <ImageView + android:id="@+id/app_icon" + android:layout_height="@dimen/ongoing_appops_dialog_app_icon_size" + android:layout_width="@dimen/ongoing_appops_dialog_app_icon_size" + android:layout_gravity="center" + /> + </FrameLayout> <TextView android:id="@+id/app_name" diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index ef16bcaf0fd2..5a00b4526c32 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -952,6 +952,8 @@ <dimen name="ongoing_appops_dialog_icon_margin">8dp</dimen> <!-- Height and width of Application icons in Ongoing App Ops dialog --> <dimen name="ongoing_appops_dialog_app_icon_size">32dp</dimen> + <!-- Height and width of Plus sign in Ongoing App Ops dialog --> + <dimen name="ongoing_appops_dialog_app_plus_size">24dp</dimen> <!-- Height of line in Ongoing App Ops dialog--> <dimen name="ongoing_appops_dialog_line_height">48dp</dimen> <!-- Side margin of title in Ongoing App Ops dialog --> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 3cfd6a9ba7cb..0ec0bf08c317 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -1,9 +1,15 @@ package com.android.keyguard; +import android.content.ContentResolver; import android.content.Context; +import android.database.ContentObserver; import android.graphics.Paint; import android.graphics.Paint.Style; +import android.os.Handler; +import android.os.Looper; +import android.provider.Settings; import android.util.AttributeSet; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; @@ -12,6 +18,7 @@ import android.widget.TextClock; import androidx.annotation.VisibleForTesting; +import com.android.keyguard.clock.BubbleClockController; import com.android.systemui.Dependency; import com.android.systemui.plugins.ClockPlugin; import com.android.systemui.statusbar.StatusBarState; @@ -19,13 +26,19 @@ import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.policy.ExtensionController; import com.android.systemui.statusbar.policy.ExtensionController.Extension; +import java.util.Objects; import java.util.TimeZone; import java.util.function.Consumer; +import java.util.function.Supplier; /** * Switch to show plugin clock when plugin is connected, otherwise it will show default clock. */ public class KeyguardClockSwitch extends RelativeLayout { + + private LayoutInflater mLayoutInflater; + + private final ContentResolver mContentResolver; /** * Optional/alternative clock injected via plugin. */ @@ -79,12 +92,25 @@ public class KeyguardClockSwitch extends RelativeLayout { } }; + private final ContentObserver mContentObserver = + new ContentObserver(new Handler(Looper.getMainLooper())) { + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + if (mClockExtension != null) { + mClockExtension.reload(); + } + } + }; + public KeyguardClockSwitch(Context context) { this(context, null); } public KeyguardClockSwitch(Context context, AttributeSet attrs) { super(context, attrs); + mLayoutInflater = LayoutInflater.from(context); + mContentResolver = context.getContentResolver(); } /** @@ -108,7 +134,22 @@ public class KeyguardClockSwitch extends RelativeLayout { mClockExtension = Dependency.get(ExtensionController.class).newExtension(ClockPlugin.class) .withPlugin(ClockPlugin.class) .withCallback(mClockPluginConsumer) + // Using withDefault even though this isn't the default as a workaround. + // ExtensionBulider doesn't provide the ability to supply a ClockPlugin + // instance based off of the value of a setting. Since multiple "default" + // can be provided, using a supplier that changes the settings value. + // A null return will cause Extension#reload to look at the next "default" + // supplier. + .withDefault( + new SettingsGattedSupplier( + mContentResolver, + Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, + BubbleClockController.class.getName(), + () -> BubbleClockController.build(mLayoutInflater))) .build(); + mContentResolver.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE), + false, mContentObserver); Dependency.get(StatusBarStateController.class).addCallback(mStateListener); } @@ -116,6 +157,7 @@ public class KeyguardClockSwitch extends RelativeLayout { protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mClockExtension.destroy(); + mContentResolver.unregisterContentObserver(mContentObserver); Dependency.get(StatusBarStateController.class).removeCallback(mStateListener); } @@ -269,4 +311,44 @@ public class KeyguardClockSwitch extends RelativeLayout { StatusBarStateController.StateListener getStateListener() { return mStateListener; } + + /** + * Supplier that only gets an instance when a settings value matches expected value. + */ + private static class SettingsGattedSupplier implements Supplier<ClockPlugin> { + + private final ContentResolver mContentResolver; + private final String mKey; + private final String mValue; + private final Supplier<ClockPlugin> mSupplier; + + /** + * Constructs a supplier that changes secure setting key against value. + * + * @param contentResolver Used to look up settings value. + * @param key Settings key. + * @param value If the setting matches this values that get supplies a ClockPlugin + * instance. + * @param supplier Supplier of ClockPlugin instance, only used if the setting + * matches value. + */ + SettingsGattedSupplier(ContentResolver contentResolver, String key, String value, + Supplier<ClockPlugin> supplier) { + mContentResolver = contentResolver; + mKey = key; + mValue = value; + mSupplier = supplier; + } + + /** + * Returns null if the settings value doesn't match the expected value. + * + * A null return causes Extension#reload to skip this supplier and move to the next. + */ + @Override + public ClockPlugin get() { + final String currentValue = Settings.Secure.getString(mContentResolver, mKey); + return Objects.equals(currentValue, mValue) ? mSupplier.get() : null; + } + } } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java new file mode 100644 index 000000000000..db6127f1d573 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.keyguard.clock; + +import android.graphics.Paint.Style; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.TextClock; + +import com.android.keyguard.R; +import com.android.systemui.plugins.ClockPlugin; + +import java.util.TimeZone; + +/** + * Controller for Bubble clock that can appear on lock screen and AOD. + */ +public class BubbleClockController implements ClockPlugin { + + /** + * Custom clock shown on AOD screen and behind stack scroller on lock. + */ + private View mView; + private TextClock mDigitalClock; + private ImageClock mAnalogClock; + + /** + * Small clock shown on lock screen above stack scroller. + */ + private View mLockClockContainer; + private TextClock mLockClock; + + /** + * Controller for transition to dark state. + */ + private CrossFadeDarkController mDarkController; + + private BubbleClockController() { } + + /** + * Create a BubbleClockController instance. + * + * @param layoutInflater Inflater used to inflate custom clock views. + */ + public static BubbleClockController build(LayoutInflater layoutInflater) { + BubbleClockController controller = new BubbleClockController(); + controller.createViews(layoutInflater); + return controller; + } + + private void createViews(LayoutInflater layoutInflater) { + mView = layoutInflater.inflate(R.layout.bubble_clock, null); + mDigitalClock = (TextClock) mView.findViewById(R.id.digital_clock); + mAnalogClock = (ImageClock) mView.findViewById(R.id.analog_clock); + + mLockClockContainer = layoutInflater.inflate(R.layout.digital_clock, null); + mLockClock = (TextClock) mLockClockContainer.findViewById(R.id.lock_screen_clock); + mLockClock.setVisibility(View.GONE); + + mDarkController = new CrossFadeDarkController(mDigitalClock, mLockClock); + } + + @Override + public View getView() { + return mLockClockContainer; + } + + @Override + public View getBigClockView() { + return mView; + } + + @Override + public void setStyle(Style style) {} + + @Override + public void setTextColor(int color) { + mLockClock.setTextColor(color); + mDigitalClock.setTextColor(color); + } + + @Override + public void dozeTimeTick() { + mAnalogClock.onTimeChanged(); + } + + @Override + public void setDarkAmount(float darkAmount) { + mDarkController.setDarkAmount(darkAmount); + } + + @Override + public void onTimeZoneChanged(TimeZone timeZone) { + mAnalogClock.onTimeZoneChanged(timeZone); + } + + @Override + public boolean shouldShowStatusArea() { + return false; + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java new file mode 100644 index 000000000000..5aa566848732 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.keyguard.clock; + +import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; + +import android.content.Context; +import android.content.res.Resources; +import android.util.AttributeSet; +import android.view.View; +import android.widget.FrameLayout; + +import com.android.keyguard.R; + +/** + * Positions clock faces (analog, digital, typographic) and handles pixel shifting + * to prevent screen burn-in. + */ +public class ClockLayout extends FrameLayout { + + /** + * Clock face views. + */ + private View mDigitalClock; + private View mAnalogClock; + + /** + * Pixel shifting amplitidues used to prevent screen burn-in. + */ + private int mBurnInPreventionOffsetX; + private int mBurnInPreventionOffsetY; + + public ClockLayout(Context context) { + this(context, null); + } + + public ClockLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ClockLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mDigitalClock = findViewById(R.id.digital_clock); + mAnalogClock = findViewById(R.id.analog_clock); + + // Get pixel shifting X, Y amplitudes from resources. + Resources resources = getResources(); + mBurnInPreventionOffsetX = resources.getDimensionPixelSize( + R.dimen.burn_in_prevention_offset_x); + mBurnInPreventionOffsetY = resources.getDimensionPixelSize( + R.dimen.burn_in_prevention_offset_y); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + + final float offsetX = getBurnInOffset(mBurnInPreventionOffsetX, true); + final float offsetY = getBurnInOffset(mBurnInPreventionOffsetY, false); + + // Put digital clock in two left corner of the screen. + if (mDigitalClock != null) { + mDigitalClock.setX(0.1f * getWidth() + offsetX); + mDigitalClock.setY(0.1f * getHeight() + offsetY); + } + + // Put the analog clock in the middle of the screen. + if (mAnalogClock != null) { + mAnalogClock.setX(Math.max(0f, 0.5f * (getWidth() - mAnalogClock.getWidth())) + + offsetX); + mAnalogClock.setY(Math.max(0f, 0.5f * (getHeight() - mAnalogClock.getHeight())) + + offsetY); + } + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/CrossFadeDarkController.java b/packages/SystemUI/src/com/android/keyguard/clock/CrossFadeDarkController.java new file mode 100644 index 000000000000..3c3f4759614b --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/clock/CrossFadeDarkController.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.keyguard.clock; + +import android.view.View; + +/** + * Controls transition to dark state by cross fading between views. + */ +final class CrossFadeDarkController { + + private final View mFadeInView; + private final View mFadeOutView; + + /** + * Creates a new controller that fades between views. + * + * @param fadeInView View to fade in when transitioning to AOD. + * @param fadeOutView View to fade out when transitioning to AOD. + */ + CrossFadeDarkController(View fadeInView, View fadeOutView) { + mFadeInView = fadeInView; + mFadeOutView = fadeOutView; + } + + /** + * Sets the amount the system has transitioned to the dark state. + * + * @param darkAmount Amount of transition to dark state: 1f for AOD and 0f for lock screen. + */ + void setDarkAmount(float darkAmount) { + mFadeInView.setAlpha(Math.max(0f, 2f * darkAmount - 1f)); + if (darkAmount == 0f) { + mFadeInView.setVisibility(View.GONE); + } else { + if (mFadeInView.getVisibility() == View.GONE) { + mFadeInView.setVisibility(View.VISIBLE); + } + } + mFadeOutView.setAlpha(Math.max(0f, 1f - 2f * darkAmount)); + if (darkAmount == 1f) { + mFadeOutView.setVisibility(View.GONE); + } else { + if (mFadeOutView.getVisibility() == View.GONE) { + mFadeOutView.setVisibility(View.VISIBLE); + } + } + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java b/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java new file mode 100644 index 000000000000..2c709e0393bd --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.keyguard.clock; + +import android.content.Context; +import android.text.format.DateFormat; +import android.util.AttributeSet; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import com.android.keyguard.R; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.TimeZone; + +/** + * Clock composed of two images that rotate with the time. + * + * The images are the clock hands. ImageClock expects two child ImageViews + * with ids hour_hand and minute_hand. + */ +public class ImageClock extends FrameLayout { + + private ImageView mHourHand; + private ImageView mMinuteHand; + private Calendar mTime; + private String mDescFormat; + private TimeZone mTimeZone; + + public ImageClock(Context context) { + this(context, null); + } + + public ImageClock(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ImageClock(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + mTime = Calendar.getInstance(); + mDescFormat = ((SimpleDateFormat) DateFormat.getTimeFormat(context)).toLocalizedPattern(); + } + + /** + * Call when the time changes to update the rotation of the clock hands. + */ + public void onTimeChanged() { + mTime.setTimeInMillis(System.currentTimeMillis()); + final float hourAngle = mTime.get(Calendar.HOUR) * 30f; + mHourHand.setRotation(hourAngle); + final float minuteAngle = mTime.get(Calendar.MINUTE) * 6f; + mMinuteHand.setRotation(minuteAngle); + setContentDescription(DateFormat.format(mDescFormat, mTime)); + invalidate(); + } + + /** + * Call when the time zone has changed to update clock hands. + * + * @param timeZone The updated time zone that will be used. + */ + public void onTimeZoneChanged(TimeZone timeZone) { + mTimeZone = timeZone; + mTime.setTimeZone(timeZone); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mHourHand = findViewById(R.id.hour_hand); + mMinuteHand = findViewById(R.id.minute_hand); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mTime = Calendar.getInstance(mTimeZone != null ? mTimeZone : TimeZone.getDefault()); + onTimeChanged(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index d7bf77da1011..957d772be730 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -284,8 +284,9 @@ public class BubbleController { @Nullable private PendingIntent getAppOverlayIntent(NotificationEntry notif) { Notification notification = notif.notification.getNotification(); - if (canLaunchInActivityView(notification.getAppOverlayIntent())) { - return notification.getAppOverlayIntent(); + if (canLaunchInActivityView(notification.getBubbleMetadata() != null + ? notification.getBubbleMetadata().getIntent() : null)) { + return notification.getBubbleMetadata().getIntent(); } else if (shouldUseContentIntent(mContext) && canLaunchInActivityView(notification.contentIntent)) { Log.d(TAG, "[addBubble " + notif.key @@ -446,15 +447,16 @@ public class BubbleController { StatusBarNotification n = entry.notification; boolean canAppOverlay = false; try { - canAppOverlay = mNotificationManagerService.areAppOverlaysAllowedForPackage( + canAppOverlay = mNotificationManagerService.areBubblesAllowedForPackage( n.getPackageName(), n.getUid()); } catch (RemoteException e) { Log.w(TAG, "Error calling NoMan to determine if app can overlay", e); } boolean canChannelOverlay = mNotificationEntryManager.getNotificationData().getChannel( - entry.key).canOverlayApps(); - boolean hasOverlayIntent = n.getNotification().getAppOverlayIntent() != null; + entry.key).canBubble(); + boolean hasOverlayIntent = n.getNotification().getBubbleMetadata() != null + && n.getNotification().getBubbleMetadata().getIntent() != null; return hasOverlayIntent && canChannelOverlay && canAppOverlay; } diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt index 6ed1ebad8e51..77e25e324915 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt @@ -20,6 +20,7 @@ import android.content.Context import android.content.DialogInterface import android.content.Intent import android.content.res.ColorStateList +import android.util.IconDrawableFactory import android.view.Gravity import android.view.LayoutInflater import android.view.View @@ -37,11 +38,22 @@ class OngoingPrivacyDialog constructor( private val iconSize = context.resources.getDimensionPixelSize( R.dimen.ongoing_appops_dialog_icon_size) + private val plusSize = context.resources.getDimensionPixelSize( + R.dimen.ongoing_appops_dialog_app_plus_size) private val iconColor = context.resources.getColor( com.android.internal.R.color.text_color_primary, context.theme) + private val plusColor: Int private val iconMargin = context.resources.getDimensionPixelSize( R.dimen.ongoing_appops_dialog_icon_margin) private val MAX_ITEMS = context.resources.getInteger(R.integer.ongoing_appops_dialog_max_apps) + private val iconFactory = IconDrawableFactory.newInstance(context, true) + + init { + val a = context.theme.obtainStyledAttributes( + intArrayOf(com.android.internal.R.attr.colorAccent)) + plusColor = a.getColor(0, 0) + a.recycle() + } fun createDialog(): Dialog { val builder = AlertDialog.Builder(context).apply { @@ -87,9 +99,15 @@ class OngoingPrivacyDialog constructor( numItems - MAX_ITEMS ) val overflowPlus = overflow.findViewById(R.id.app_icon) as ImageView + val lp = overflowPlus.layoutParams.apply { + height = plusSize + width = plusSize + } + overflowPlus.layoutParams = lp overflowPlus.apply { - imageTintList = ColorStateList.valueOf(iconColor) - setImageDrawable(context.getDrawable(R.drawable.plus)) + val plus = context.getDrawable(R.drawable.plus) + imageTintList = ColorStateList.valueOf(plusColor) + setImageDrawable(plus) } } @@ -114,7 +132,7 @@ class OngoingPrivacyDialog constructor( } app.icon.let { - appIcon.setImageDrawable(it) + appIcon.setImageDrawable(iconFactory.getShadowedIcon(it)) } appName.text = app.applicationName diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java new file mode 100644 index 000000000000..9e946faf2a52 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.keyguard.clock; + +import static com.google.common.truth.Truth.assertThat; + +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +public final class BubbleClockControllerTest extends SysuiTestCase { + + private BubbleClockController mClockController; + + @Before + public void setUp() { + LayoutInflater layoutInflater = LayoutInflater.from(getContext()); + mClockController = BubbleClockController.build(layoutInflater); + } + + @Test + public void setDarkAmount_fadeIn() { + ViewGroup smallClockFrame = (ViewGroup) mClockController.getView(); + View smallClock = smallClockFrame.getChildAt(0); + // WHEN dark amount is set to AOD + mClockController.setDarkAmount(1f); + // THEN small clock should not be shown. + assertThat(smallClock.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void setTextColor_setDigitalClock() { + ViewGroup smallClock = (ViewGroup) mClockController.getView(); + // WHEN text color is set + mClockController.setTextColor(42); + // THEN child of small clock should have text color set. + TextView digitalClock = (TextView) smallClock.getChildAt(0); + assertThat(digitalClock.getCurrentTextColor()).isEqualTo(42); + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/CrossFadeDarkControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/CrossFadeDarkControllerTest.java new file mode 100644 index 000000000000..fd7657ff18cc --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/CrossFadeDarkControllerTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.keyguard.clock; + +import static com.google.common.truth.Truth.assertThat; + +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; +import android.view.View; +import android.widget.TextView; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +public final class CrossFadeDarkControllerTest extends SysuiTestCase { + + private View mViewFadeIn; + private View mViewFadeOut; + private CrossFadeDarkController mDarkController; + + @Before + public void setUp() { + mViewFadeIn = new TextView(getContext()); + mViewFadeIn.setVisibility(View.VISIBLE); + mViewFadeIn.setAlpha(1f); + mViewFadeOut = new TextView(getContext()); + mViewFadeOut.setVisibility(View.VISIBLE); + mViewFadeOut.setAlpha(1f); + + mDarkController = new CrossFadeDarkController(mViewFadeIn, mViewFadeOut); + } + + @Test + public void setDarkAmount_fadeIn() { + // WHEN dark amount corresponds to AOD + mDarkController.setDarkAmount(1f); + // THEN fade in view should be faded in and fade out view faded out. + assertThat(mViewFadeIn.getAlpha()).isEqualTo(1f); + assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewFadeOut.getAlpha()).isEqualTo(0f); + assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void setDarkAmount_fadeOut() { + // WHEN dark amount corresponds to lock screen + mDarkController.setDarkAmount(0f); + // THEN fade out view should bed faded out and fade in view faded in. + assertThat(mViewFadeIn.getAlpha()).isEqualTo(0f); + assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewFadeOut.getAlpha()).isEqualTo(1f); + assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void setDarkAmount_partialFadeIn() { + // WHEN dark amount corresponds to a partial transition + mDarkController.setDarkAmount(0.9f); + // THEN views should have intermediate alpha value. + assertThat(mViewFadeIn.getAlpha()).isGreaterThan(0f); + assertThat(mViewFadeIn.getAlpha()).isLessThan(1f); + assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void setDarkAmount_partialFadeOut() { + // WHEN dark amount corresponds to a partial transition + mDarkController.setDarkAmount(0.1f); + // THEN views should have intermediate alpha value. + assertThat(mViewFadeOut.getAlpha()).isGreaterThan(0f); + assertThat(mViewFadeOut.getAlpha()).isLessThan(1f); + assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.VISIBLE); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java index 2b13f864b9a5..89525944b685 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java @@ -27,6 +27,7 @@ import android.app.NotificationChannel; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.graphics.drawable.Icon; import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.support.test.InstrumentationRegistry; @@ -220,8 +221,7 @@ public class NotificationTestHelper { notificationBuilder.setGroup(groupKey); } if (isBubble) { - PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); - notificationBuilder.setAppOverlayIntent(bubbleIntent); + notificationBuilder.setBubbleMetadata(makeBubbleMetadata()); } return notificationBuilder.build(); } @@ -282,4 +282,14 @@ public class NotificationTestHelper { mGroupManager.onEntryAdded(entry); return row; } + + private Notification.BubbleMetadata makeBubbleMetadata() { + PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); + return new Notification.BubbleMetadata.Builder() + .setIntent(bubbleIntent) + .setTitle("bubble title") + .setIcon(Icon.createWithResource(mContext, 1)) + .setDesiredHeight(314) + .build(); + } } diff --git a/packages/WallpaperCropper/Android.mk b/packages/WallpaperCropper/Android.mk index 848f2bd1a6fe..2fa1ddee0fd9 100644 --- a/packages/WallpaperCropper/Android.mk +++ b/packages/WallpaperCropper/Android.mk @@ -8,6 +8,7 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_PACKAGE_NAME := WallpaperCropper LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_CERTIFICATE := platform +LOCAL_PRODUCT_MODULE := true LOCAL_PRIVILEGED_MODULE := true LOCAL_PROGUARD_FLAG_FILES := proguard.flags diff --git a/packages/WallpaperCropper/CleanSpec.mk b/packages/WallpaperCropper/CleanSpec.mk new file mode 100644 index 000000000000..e6d8d5a774f1 --- /dev/null +++ b/packages/WallpaperCropper/CleanSpec.mk @@ -0,0 +1,50 @@ +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# If you don't need to do a full clean build but would like to touch +# a file or delete some intermediate files, add a clean step to the end +# of the list. These steps will only be run once, if they haven't been +# run before. +# +# E.g.: +# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) +# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) +# +# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with +# files that are missing or have been moved. +# +# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. +# Use $(OUT_DIR) to refer to the "out" directory. +# +# If you need to re-do something that's already mentioned, just copy +# the command and add it to the bottom of the list. E.g., if a change +# that you made last week required touching a file and a change you +# made today requires touching the same file, just copy the old +# touch step and add it to the end of the list. +# +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ + +# For example: +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) +#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) +#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/WallpaperCropper) + +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index 5b45a08af9b4..bb5d288f11e7 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -6794,8 +6794,24 @@ message MetricsEvent { // OS: Q NOTIFICATION_BLOCKING_HELPER = 1621; + // ACTION: Tap & Pay -> Default Application Setting -> Use Forground + // OS: Q + ACTION_NFC_PAYMENT_FOREGROUND_SETTING = 1622; + + // ACTION: Tap & Pay -> Default Application Setting -> Use Default + // OS: Q + ACTION_NFC_PAYMENT_ALWAYS_SETTING = 1623; // ---- End Q Constants, all Q constants go above this line ---- + // OPEN: Settings > System > Input & Gesture > Skip song gesture + // OS: Q + SETTINGS_GESTURE_SKIP_SONG = 1624; + + // OPEN: Settings > System > Input & Gesture > Silence gesture + // OS: Q + SETTINGS_GESTURE_SILENCE = 1625; + + // ---- End Q Constants, all Q constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS } diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java index e4ce62d0a5b5..4a1e5b9910bf 100644 --- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java +++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java @@ -23,9 +23,9 @@ import android.content.ComponentName; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.Signature; import android.content.pm.SigningInfo; import android.os.Build; @@ -49,9 +49,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Set; - import java.util.Objects; +import java.util.Set; /** * We back up the signatures of each package so that during a system restore, @@ -95,6 +94,7 @@ public class PackageManagerBackupAgent extends BackupAgent { // is coming from pre-Android P device. private static final int UNDEFINED_ANCESTRAL_RECORD_VERSION = -1; + private int mUserId; private List<PackageInfo> mAllPackages; private PackageManager mPackageManager; // version & signature info of each app in a restore set @@ -129,17 +129,18 @@ public class PackageManagerBackupAgent extends BackupAgent { // We're constructed with the set of applications that are participating // in backup. This set changes as apps are installed & removed. - public PackageManagerBackupAgent(PackageManager packageMgr, List<PackageInfo> packages) { - init(packageMgr, packages); + public PackageManagerBackupAgent( + PackageManager packageMgr, List<PackageInfo> packages, int userId) { + init(packageMgr, packages, userId); } - public PackageManagerBackupAgent(PackageManager packageMgr) { - init(packageMgr, null); + public PackageManagerBackupAgent(PackageManager packageMgr, int userId) { + init(packageMgr, null, userId); evaluateStorablePackages(); } - private void init(PackageManager packageMgr, List<PackageInfo> packages) { + private void init(PackageManager packageMgr, List<PackageInfo> packages, int userId) { mPackageManager = packageMgr; mAllPackages = packages; mRestoredSignatures = null; @@ -147,17 +148,19 @@ public class PackageManagerBackupAgent extends BackupAgent { mStoredSdkVersion = Build.VERSION.SDK_INT; mStoredIncrementalVersion = Build.VERSION.INCREMENTAL; + mUserId = userId; } // We will need to refresh our understanding of what is eligible for // backup periodically; this entry point serves that purpose. public void evaluateStorablePackages() { - mAllPackages = getStorableApplications(mPackageManager); + mAllPackages = getStorableApplications(mPackageManager, mUserId); } - public static List<PackageInfo> getStorableApplications(PackageManager pm) { - List<PackageInfo> pkgs; - pkgs = pm.getInstalledPackages(PackageManager.GET_SIGNING_CERTIFICATES); + /** Gets all packages installed on user {@code userId} eligible for backup. */ + public static List<PackageInfo> getStorableApplications(PackageManager pm, int userId) { + List<PackageInfo> pkgs = + pm.getInstalledPackagesAsUser(PackageManager.GET_SIGNING_CERTIFICATES, userId); int N = pkgs.size(); for (int a = N-1; a >= 0; a--) { PackageInfo pkg = pkgs.get(a); @@ -237,8 +240,8 @@ public class PackageManagerBackupAgent extends BackupAgent { ComponentName home = getPreferredHomeComponent(); if (home != null) { try { - homeInfo = mPackageManager.getPackageInfo(home.getPackageName(), - PackageManager.GET_SIGNING_CERTIFICATES); + homeInfo = mPackageManager.getPackageInfoAsUser(home.getPackageName(), + PackageManager.GET_SIGNING_CERTIFICATES, mUserId); homeInstaller = mPackageManager.getInstallerPackageName(home.getPackageName()); homeVersion = homeInfo.getLongVersionCode(); SigningInfo signingInfo = homeInfo.signingInfo; @@ -315,8 +318,8 @@ public class PackageManagerBackupAgent extends BackupAgent { } else { PackageInfo info = null; try { - info = mPackageManager.getPackageInfo(packName, - PackageManager.GET_SIGNING_CERTIFICATES); + info = mPackageManager.getPackageInfoAsUser(packName, + PackageManager.GET_SIGNING_CERTIFICATES, mUserId); } catch (NameNotFoundException e) { // Weird; we just found it, and now are told it doesn't exist. // Treat it as having been removed from the device. diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index ed6a46cc58aa..3a8966a04055 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -249,11 +249,11 @@ public class UserBackupManagerService { private final TransportManager mTransportManager; private final HandlerThread mUserBackupThread; - private Context mContext; - private PackageManager mPackageManager; - private IPackageManager mPackageManagerBinder; - private IActivityManager mActivityManager; - private ActivityManagerInternal mActivityManagerInternal; + private final Context mContext; + private final PackageManager mPackageManager; + private final IPackageManager mPackageManagerBinder; + private final IActivityManager mActivityManager; + private final ActivityManagerInternal mActivityManagerInternal; private PowerManager mPowerManager; private final AlarmManager mAlarmManager; private final IStorageManager mStorageManager; @@ -496,11 +496,18 @@ public class UserBackupManagerService { mBaseStateDir = checkNotNull(baseStateDir, "baseStateDir cannot be null"); mBaseStateDir.mkdirs(); if (!SELinux.restorecon(mBaseStateDir)) { - Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir); + Slog.w(TAG, "SELinux restorecon failed on " + mBaseStateDir); } mDataDir = checkNotNull(dataDir, "dataDir cannot be null"); - + // TODO(b/120424138): Remove when the system user moves out of the cache dir. The cache dir + // is managed by init.rc so we don't have to create it below. + if (userId != UserHandle.USER_SYSTEM) { + mDataDir.mkdirs(); + if (!SELinux.restorecon(mDataDir)) { + Slog.w(TAG, "SELinux restorecon failed on " + mDataDir); + } + } mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng); // Receivers for scheduled backups and transport initialization operations. @@ -797,7 +804,7 @@ public class UserBackupManagerService { * non-lifecycle agent instance, so we manually set up the context topology for it. */ public BackupAgent makeMetadataAgent() { - PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager); + PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager, mUserId); pmAgent.attach(mContext); pmAgent.onCreate(); return pmAgent; @@ -808,7 +815,7 @@ public class UserBackupManagerService { */ public PackageManagerBackupAgent makeMetadataAgent(List<PackageInfo> packages) { PackageManagerBackupAgent pmAgent = - new PackageManagerBackupAgent(mPackageManager, packages); + new PackageManagerBackupAgent(mPackageManager, packages, mUserId); pmAgent.attach(mContext); pmAgent.onCreate(); return pmAgent; @@ -879,7 +886,7 @@ public class UserBackupManagerService { boolean changed = false; ArrayList<FullBackupEntry> schedule = null; List<PackageInfo> apps = - PackageManagerBackupAgent.getStorableApplications(mPackageManager); + PackageManagerBackupAgent.getStorableApplications(mPackageManager, mUserId); if (mFullBackupScheduleFile.exists()) { try (FileInputStream fstream = new FileInputStream(mFullBackupScheduleFile); @@ -1428,8 +1435,7 @@ public class UserBackupManagerService { mConnecting = true; mConnectedAgent = null; try { - if (mActivityManager.bindBackupAgent(app.packageName, mode, - UserHandle.USER_OWNER)) { + if (mActivityManager.bindBackupAgent(app.packageName, mode, mUserId)) { Slog.d(TAG, "awaiting agent for " + app); // success; wait for the agent to arrive @@ -1460,11 +1466,7 @@ public class UserBackupManagerService { } } if (agent == null) { - try { - mActivityManager.clearPendingBackup(); - } catch (RemoteException e) { - // can't happen - ActivityManager is local - } + mActivityManagerInternal.clearPendingBackup(mUserId); } return agent; } @@ -1488,7 +1490,7 @@ public class UserBackupManagerService { public void clearApplicationDataSynchronous(String packageName, boolean keepSystemState) { // Don't wipe packages marked allowClearUserData=false try { - PackageInfo info = mPackageManager.getPackageInfo(packageName, 0); + PackageInfo info = mPackageManager.getPackageInfoAsUser(packageName, 0, mUserId); if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) { if (MORE_DEBUG) { Slog.i(TAG, "allowClearUserData=false so not wiping " @@ -1507,7 +1509,7 @@ public class UserBackupManagerService { mClearingData = true; try { mActivityManager.clearApplicationUserData( - packageName, keepSystemState, observer, 0); + packageName, keepSystemState, observer, mUserId); } catch (RemoteException e) { // can't happen because the activity manager is in this process } @@ -1616,8 +1618,8 @@ public class UserBackupManagerService { continue; } try { - PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, - PackageManager.GET_SIGNING_CERTIFICATES); + PackageInfo packageInfo = mPackageManager.getPackageInfoAsUser(packageName, + PackageManager.GET_SIGNING_CERTIFICATES, mUserId); if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo, mPackageManager)) { BackupObserverUtils.sendBackupOnPackageResult(observer, packageName, @@ -2339,8 +2341,8 @@ public class UserBackupManagerService { if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName); PackageInfo info; try { - info = mPackageManager.getPackageInfo(packageName, - PackageManager.GET_SIGNING_CERTIFICATES); + info = mPackageManager.getPackageInfoAsUser(packageName, + PackageManager.GET_SIGNING_CERTIFICATES, mUserId); } catch (NameNotFoundException e) { Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data"); return; @@ -3258,7 +3260,7 @@ public class UserBackupManagerService { if (packageName != null) { PackageInfo app = null; try { - app = mPackageManager.getPackageInfo(packageName, 0); + app = mPackageManager.getPackageInfoAsUser(packageName, 0, mUserId); } catch (NameNotFoundException nnf) { Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName); throw new IllegalArgumentException("Package " + packageName + " not found"); @@ -3362,7 +3364,7 @@ public class UserBackupManagerService { mTransportManager.getCurrentTransportClient(callerLogString); boolean eligible = AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport( - transportClient, packageName, mPackageManager); + transportClient, packageName, mPackageManager, mUserId); if (transportClient != null) { mTransportManager.disposeOfTransportClient(transportClient, callerLogString); } @@ -3386,7 +3388,7 @@ public class UserBackupManagerService { for (String packageName : packages) { if (AppBackupUtils .appIsRunningAndEligibleForBackupWithTransport( - transportClient, packageName, mPackageManager)) { + transportClient, packageName, mPackageManager, mUserId)) { eligibleApps.add(packageName); } } diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java index ef7ff9270a66..862ca711e694 100644 --- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java +++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java @@ -45,7 +45,6 @@ import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; import android.os.SELinux; -import android.os.UserHandle; import android.os.WorkSource; import com.android.internal.annotations.GuardedBy; @@ -241,6 +240,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { private final boolean mUserInitiated; private final boolean mNonIncremental; private final int mCurrentOpToken; + private final int mUserId; private final File mStateDirectory; private final File mDataDirectory; private final File mBlankStateFile; @@ -320,6 +320,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { mCurrentOpToken = backupManagerService.generateRandomIntegerToken(); mQueueLock = mBackupManagerService.getQueueLock(); mBlankStateFile = new File(mStateDirectory, BLANK_STATE_FILE_NAME); + mUserId = backupManagerService.getUserId(); } private void registerTask() { @@ -480,8 +481,8 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { final PackageInfo packageInfo; try { packageInfo = - mPackageManager.getPackageInfo( - packageName, PackageManager.GET_SIGNING_CERTIFICATES); + mPackageManager.getPackageInfoAsUser( + packageName, PackageManager.GET_SIGNING_CERTIFICATES, mUserId); } catch (PackageManager.NameNotFoundException e) { mReporter.onAgentUnknown(packageName); throw AgentException.permanent(e); @@ -770,8 +771,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { private void writeWidgetPayloadIfAppropriate(FileDescriptor fd, String pkgName) throws IOException { - // TODO: http://b/22388012 - byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName, UserHandle.USER_SYSTEM); + byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName, mUserId); File widgetFile = new File(mStateDirectory, pkgName + "_widget"); boolean priorStateExists = widgetFile.exists(); if (!priorStateExists && widgetState == null) { diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java index e273b329d51a..0fa0f89e329e 100644 --- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java +++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java @@ -54,6 +54,7 @@ public class ActiveRestoreSession extends IRestoreSession.Stub { private final TransportManager mTransportManager; private final String mTransportName; private final UserBackupManagerService mBackupManagerService; + private final int mUserId; @Nullable private final String mPackageName; public RestoreSet[] mRestoreSets = null; boolean mEnded = false; @@ -67,6 +68,7 @@ public class ActiveRestoreSession extends IRestoreSession.Stub { mPackageName = packageName; mTransportManager = backupManagerService.getTransportManager(); mTransportName = transportName; + mUserId = backupManagerService.getUserId(); } public void markTimedOut() { @@ -304,7 +306,8 @@ public class ActiveRestoreSession extends IRestoreSession.Stub { final PackageInfo app; try { - app = mBackupManagerService.getPackageManager().getPackageInfo(packageName, 0); + app = mBackupManagerService.getPackageManager().getPackageInfoAsUser( + packageName, 0, mUserId); } catch (NameNotFoundException nnf) { Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName); return -1; diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java index 45a398f80351..c7f3315493b4 100644 --- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java +++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java @@ -272,7 +272,7 @@ public class FullRestoreEngine extends RestoreEngine { instream, mBackupManagerService.getContext(), mDeleteObserver, mManifestSignatures, mPackagePolicies, info, installerPackageName, - bytesReadListener); + bytesReadListener, mBackupManagerService.getUserId()); // good to go; promote to ACCEPT mPackagePolicies.put(pkg, isSuccessfullyInstalled ? RestorePolicy.ACCEPT diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java index f7efad604e35..5284d94c2aa7 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java @@ -48,7 +48,6 @@ import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; -import android.os.UserHandle; import android.util.EventLog; import android.util.Slog; @@ -81,6 +80,7 @@ import java.util.List; public class PerformUnifiedRestoreTask implements BackupRestoreTask { private UserBackupManagerService backupManagerService; + private final int mUserId; private final TransportManager mTransportManager; // Transport client we're working with to do the restore private final TransportClient mTransportClient; @@ -175,6 +175,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { @Nullable String[] filterSet, OnTaskFinishedListener listener) { this.backupManagerService = backupManagerService; + mUserId = backupManagerService.getUserId(); mTransportManager = backupManagerService.getTransportManager(); mEphemeralOpToken = backupManagerService.generateRandomIntegerToken(); mState = UnifiedRestoreState.INITIAL; @@ -204,7 +205,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // We want everything and a pony List<PackageInfo> apps = PackageManagerBackupAgent.getStorableApplications( - backupManagerService.getPackageManager()); + backupManagerService.getPackageManager(), mUserId); filterSet = packagesToNames(apps); if (DEBUG) { Slog.i(TAG, "Full restore; asking about " + filterSet.length + " apps"); @@ -221,7 +222,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { for (int i = 0; i < filterSet.length; i++) { try { PackageManager pm = backupManagerService.getPackageManager(); - PackageInfo info = pm.getPackageInfo(filterSet[i], 0); + PackageInfo info = pm.getPackageInfoAsUser(filterSet[i], 0, mUserId); if ("android".equals(info.packageName)) { hasSystem = true; continue; @@ -240,16 +241,16 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { } if (hasSystem) { try { - mAcceptSet.add(0, - backupManagerService.getPackageManager().getPackageInfo("android", 0)); + mAcceptSet.add(0, backupManagerService.getPackageManager().getPackageInfoAsUser( + "android", 0, mUserId)); } catch (NameNotFoundException e) { // won't happen; we know a priori that it's valid } } if (hasSettings) { try { - mAcceptSet.add(backupManagerService.getPackageManager().getPackageInfo( - SETTINGS_PACKAGE, 0)); + mAcceptSet.add(backupManagerService.getPackageManager().getPackageInfoAsUser( + SETTINGS_PACKAGE, 0, mUserId)); } catch (NameNotFoundException e) { // this one is always valid too } @@ -360,7 +361,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // If we're starting a full-system restore, set up to begin widget ID remapping if (mIsSystemRestore) { // TODO: http://b/22388012 - AppWidgetBackupBridge.restoreStarting(UserHandle.USER_SYSTEM); + AppWidgetBackupBridge.restoreStarting(mUserId); } try { @@ -508,8 +509,8 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { } try { - mCurrentPackage = backupManagerService.getPackageManager().getPackageInfo( - pkgName, PackageManager.GET_SIGNING_CERTIFICATES); + mCurrentPackage = backupManagerService.getPackageManager().getPackageInfoAsUser( + pkgName, PackageManager.GET_SIGNING_CERTIFICATES, mUserId); } catch (NameNotFoundException e) { // Whoops, we thought we could restore this package but it // turns out not to be present. Skip it. @@ -1079,7 +1080,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // Kick off any work that may be needed regarding app widget restores // TODO: http://b/22388012 - AppWidgetBackupBridge.restoreFinished(UserHandle.USER_SYSTEM); + AppWidgetBackupBridge.restoreFinished(mUserId); // If this was a full-system restore, record the ancestral // dataset information diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java index e465c7e5264f..054879b077ad 100644 --- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java +++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java @@ -91,10 +91,13 @@ public class AppBackupUtils { * </ol> */ public static boolean appIsRunningAndEligibleForBackupWithTransport( - @Nullable TransportClient transportClient, String packageName, PackageManager pm) { + @Nullable TransportClient transportClient, + String packageName, + PackageManager pm, + int userId) { try { - PackageInfo packageInfo = pm.getPackageInfo(packageName, - PackageManager.GET_SIGNING_CERTIFICATES); + PackageInfo packageInfo = pm.getPackageInfoAsUser(packageName, + PackageManager.GET_SIGNING_CERTIFICATES, userId); ApplicationInfo applicationInfo = packageInfo.applicationInfo; if (!appIsEligibleForBackup(applicationInfo, pm) || appIsStopped(applicationInfo) diff --git a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java index df7e6d45ba0f..cce5b3b4e238 100644 --- a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java +++ b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java @@ -72,7 +72,9 @@ public class RestoreUtils { HashMap<String, Signature[]> manifestSignatures, HashMap<String, RestorePolicy> packagePolicies, FileMetadata info, - String installerPackageName, BytesReadListener bytesReadListener) { + String installerPackageName, + BytesReadListener bytesReadListener, + int userId) { boolean okay = true; if (DEBUG) { @@ -144,8 +146,8 @@ public class RestoreUtils { uninstall = true; } else { try { - PackageInfo pkg = packageManager.getPackageInfo(info.packageName, - PackageManager.GET_SIGNING_CERTIFICATES); + PackageInfo pkg = packageManager.getPackageInfoAsUser(info.packageName, + PackageManager.GET_SIGNING_CERTIFICATES, userId); if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) { Slog.w(TAG, "Restore stream contains apk of package " diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 3b08a00a65d2..bd6fa498934c 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -920,8 +920,8 @@ public class ActivityManagerService extends IActivityManager.Stub /** * Backup/restore process management */ - String mBackupAppName = null; - BackupRecord mBackupTarget = null; + @GuardedBy("this") + final SparseArray<BackupRecord> mBackupTargets = new SparseArray<>(); final ProviderMap mProviderMap; @@ -4365,6 +4365,7 @@ public class ActivityManagerService extends IActivityManager.Stub mProcessList.removeProcessLocked(app, false, true, "timeout publishing content providers"); } + @GuardedBy("this") private final void processStartTimedOutLocked(ProcessRecord app) { final int pid = app.pid; boolean gone = mPidsSelfLocked.removeIfNoThread(pid); @@ -4385,7 +4386,8 @@ public class ActivityManagerService extends IActivityManager.Stub mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid); } removeLruProcessLocked(app); - if (mBackupTarget != null && mBackupTarget.app.pid == pid) { + final BackupRecord backupTarget = mBackupTargets.get(app.userId); + if (backupTarget != null && backupTarget.app.pid == pid) { Slog.w(TAG, "Unattached app died before backup, skipping"); mHandler.post(new Runnable() { @Override @@ -4393,7 +4395,7 @@ public class ActivityManagerService extends IActivityManager.Stub try { IBackupManager bm = IBackupManager.Stub.asInterface( ServiceManager.getService(Context.BACKUP_SERVICE)); - bm.agentDisconnected(app.info.packageName); + bm.agentDisconnectedForUser(app.userId, app.info.packageName); } catch (RemoteException e) { // Can't happen; the backup manager is local } @@ -4516,6 +4518,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (DEBUG_ALL) Slog.v( TAG, "New app record " + app + " thread=" + thread.asBinder() + " pid=" + pid); + final BackupRecord backupTarget = mBackupTargets.get(app.userId); try { int testMode = ApplicationThreadConstants.DEBUG_OFF; if (mDebugApp != null && mDebugApp.equals(processName)) { @@ -4537,11 +4540,11 @@ public class ActivityManagerService extends IActivityManager.Stub // If the app is being launched for restore or full backup, set it up specially boolean isRestrictedBackupMode = false; - if (mBackupTarget != null && mBackupAppName.equals(processName)) { - isRestrictedBackupMode = mBackupTarget.appInfo.uid >= FIRST_APPLICATION_UID - && ((mBackupTarget.backupMode == BackupRecord.RESTORE) - || (mBackupTarget.backupMode == BackupRecord.RESTORE_FULL) - || (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL)); + if (backupTarget != null && backupTarget.appInfo.packageName.equals(processName)) { + isRestrictedBackupMode = backupTarget.appInfo.uid >= FIRST_APPLICATION_UID + && ((backupTarget.backupMode == BackupRecord.RESTORE) + || (backupTarget.backupMode == BackupRecord.RESTORE_FULL) + || (backupTarget.backupMode == BackupRecord.BACKUP_FULL)); } final ActiveInstrumentation instr = app.getActiveInstrumentation(); @@ -4749,15 +4752,15 @@ public class ActivityManagerService extends IActivityManager.Stub } // Check whether the next backup agent is in this process... - if (!badApp && mBackupTarget != null && mBackupTarget.app == app) { + if (!badApp && backupTarget != null && backupTarget.app == app) { if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "New app is backup target, launching agent for " + app); - notifyPackageUse(mBackupTarget.appInfo.packageName, + notifyPackageUse(backupTarget.appInfo.packageName, PackageManager.NOTIFY_PACKAGE_USE_BACKUP); try { - thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, - compatibilityInfoForPackage(mBackupTarget.appInfo), - mBackupTarget.backupMode); + thread.scheduleCreateBackupAgent(backupTarget.appInfo, + compatibilityInfoForPackage(backupTarget.appInfo), + backupTarget.backupMode); } catch (Exception e) { Slog.wtf(TAG, "Exception thrown creating backup agent in " + app, e); badApp = true; @@ -13224,16 +13227,17 @@ public class ActivityManagerService extends IActivityManager.Stub app.receivers.clear(); // If the app is undergoing backup, tell the backup manager about it - if (mBackupTarget != null && app.pid == mBackupTarget.app.pid) { + final BackupRecord backupTarget = mBackupTargets.get(app.userId); + if (backupTarget != null && app.pid == backupTarget.app.pid) { if (DEBUG_BACKUP || DEBUG_CLEANUP) Slog.d(TAG_CLEANUP, "App " - + mBackupTarget.appInfo + " died during backup"); + + backupTarget.appInfo + " died during backup"); mHandler.post(new Runnable() { @Override public void run(){ try { IBackupManager bm = IBackupManager.Stub.asInterface( ServiceManager.getService(Context.BACKUP_SERVICE)); - bm.agentDisconnected(app.info.packageName); + bm.agentDisconnectedForUser(app.userId, app.info.packageName); } catch (RemoteException e) { // can't happen; backup manager is local } @@ -13573,7 +13577,11 @@ public class ActivityManagerService extends IActivityManager.Stub // instantiated. The backup agent will invoke backupAgentCreated() on the // activity manager to announce its creation. public boolean bindBackupAgent(String packageName, int backupMode, int userId) { - if (DEBUG_BACKUP) Slog.v(TAG, "bindBackupAgent: app=" + packageName + " mode=" + backupMode); + if (DEBUG_BACKUP) { + Slog.v(TAG, "bindBackupAgent: app=" + packageName + " mode=" + + backupMode + " userId=" + userId + " callingUid = " + Binder.getCallingUid() + + " uid = " + Process.myUid()); + } enforceCallingPermission("android.permission.CONFIRM_FULL_BACKUP", "bindBackupAgent"); IPackageManager pm = AppGlobals.getPackageManager(); @@ -13625,10 +13633,10 @@ public class ActivityManagerService extends IActivityManager.Stub proc.inFullBackup = true; } r.app = proc; - oldBackupUid = mBackupTarget != null ? mBackupTarget.appInfo.uid : -1; + final BackupRecord backupTarget = mBackupTargets.get(userId); + oldBackupUid = backupTarget != null ? backupTarget.appInfo.uid : -1; newBackupUid = proc.inFullBackup ? r.appInfo.uid : -1; - mBackupTarget = r; - mBackupAppName = app.packageName; + mBackupTargets.put(userId, r); // Try not to kill the process during backup updateOomAdjLocked(proc, true); @@ -13663,14 +13671,14 @@ public class ActivityManagerService extends IActivityManager.Stub return true; } - @Override - public void clearPendingBackup() { - if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "clearPendingBackup"); - enforceCallingPermission("android.permission.BACKUP", "clearPendingBackup"); + private void clearPendingBackup(int userId) { + if (DEBUG_BACKUP) { + Slog.v(TAG_BACKUP, "clearPendingBackup: userId = " + userId + " callingUid = " + + Binder.getCallingUid() + " uid = " + Process.myUid()); + } synchronized (this) { - mBackupTarget = null; - mBackupAppName = null; + mBackupTargets.delete(userId); } JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class); @@ -13678,12 +13686,19 @@ public class ActivityManagerService extends IActivityManager.Stub } // A backup agent has just come up + @Override public void backupAgentCreated(String agentPackageName, IBinder agent) { - if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "backupAgentCreated: " + agentPackageName - + " = " + agent); + final int callingUserId = UserHandle.getCallingUserId(); + if (DEBUG_BACKUP) { + Slog.v(TAG_BACKUP, "backupAgentCreated: " + agentPackageName + " = " + agent + + " callingUserId = " + callingUserId + " callingUid = " + + Binder.getCallingUid() + " uid = " + Process.myUid()); + } synchronized(this) { - if (!agentPackageName.equals(mBackupAppName)) { + final BackupRecord backupTarget = mBackupTargets.get(callingUserId); + String backupAppName = backupTarget == null ? null : backupTarget.appInfo.packageName; + if (!agentPackageName.equals(backupAppName)) { Slog.e(TAG, "Backup agent created for " + agentPackageName + " but not requested!"); return; } @@ -13693,7 +13708,7 @@ public class ActivityManagerService extends IActivityManager.Stub try { IBackupManager bm = IBackupManager.Stub.asInterface( ServiceManager.getService(Context.BACKUP_SERVICE)); - bm.agentConnected(agentPackageName, agent); + bm.agentConnectedForUser(callingUserId, agentPackageName, agent); } catch (RemoteException e) { // can't happen; the backup manager service is local } catch (Exception e) { @@ -13706,7 +13721,12 @@ public class ActivityManagerService extends IActivityManager.Stub // done with this agent public void unbindBackupAgent(ApplicationInfo appInfo) { - if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "unbindBackupAgent: " + appInfo); + if (DEBUG_BACKUP) { + Slog.v(TAG_BACKUP, "unbindBackupAgent: " + appInfo + " appInfo.uid = " + + appInfo.uid + " callingUid = " + Binder.getCallingUid() + " uid = " + + Process.myUid()); + } + enforceCallingPermission("android.permission.CONFIRM_FULL_BACKUP", "unbindBackupAgent"); if (appInfo == null) { Slog.w(TAG, "unbind backup agent for null app"); @@ -13715,24 +13735,27 @@ public class ActivityManagerService extends IActivityManager.Stub int oldBackupUid; + final int userId = UserHandle.getUserId(appInfo.uid); synchronized(this) { + final BackupRecord backupTarget = mBackupTargets.get(userId); + String backupAppName = backupTarget == null ? null : backupTarget.appInfo.packageName; try { - if (mBackupAppName == null) { + if (backupAppName == null) { Slog.w(TAG, "Unbinding backup agent with no active backup"); return; } - if (!mBackupAppName.equals(appInfo.packageName)) { + if (!backupAppName.equals(appInfo.packageName)) { Slog.e(TAG, "Unbind of " + appInfo + " but is not the current backup target"); return; } // Not backing this app up any more; reset its OOM adjustment - final ProcessRecord proc = mBackupTarget.app; + final ProcessRecord proc = backupTarget.app; updateOomAdjLocked(proc, true); proc.inFullBackup = false; - oldBackupUid = mBackupTarget != null ? mBackupTarget.appInfo.uid : -1; + oldBackupUid = backupTarget != null ? backupTarget.appInfo.uid : -1; // If the app crashed during backup, 'thread' will be null here if (proc.thread != null) { @@ -13745,8 +13768,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } } finally { - mBackupTarget = null; - mBackupAppName = null; + mBackupTargets.delete(userId); } } @@ -17771,6 +17793,11 @@ public class ActivityManagerService extends IActivityManager.Stub public boolean isAppForeground(int uid) { return ActivityManagerService.this.isAppForeground(uid); } + + @Override + public void clearPendingBackup(int userId) { + ActivityManagerService.this.clearPendingBackup(userId); + } } long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) { diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index cb4cac916f65..1f9362e246b7 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1024,7 +1024,8 @@ public final class OomAdjuster { app.hasStartedServices = false; app.adjSeq = mAdjSeq; - if (mService.mBackupTarget != null && app == mService.mBackupTarget.app) { + final BackupRecord backupTarget = mService.mBackupTargets.get(app.userId); + if (backupTarget != null && app == backupTarget.app) { // If possible we want to avoid killing apps while they're being backed up if (adj > ProcessList.BACKUP_APP_ADJ) { if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "oom BACKUP_APP_ADJ for " + app); diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java index 8d7811fc8e78..714a807da1b0 100644 --- a/services/core/java/com/android/server/appop/HistoricalRegistry.java +++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java @@ -1454,8 +1454,8 @@ final class HistoricalRegistry { if (accessCount > 0) { if (!printedUidState) { mWriter.print(mUidStatePrefix); - mWriter.print(AppOpsManager.uidStateToString(uidState)); - mWriter.print("["); + mWriter.print(AppOpsService.UID_STATE_NAMES[uidState]); + mWriter.print(" = "); printedUidState = true; } mWriter.print("access="); @@ -1465,11 +1465,11 @@ final class HistoricalRegistry { if (rejectCount > 0) { if (!printedUidState) { mWriter.print(mUidStatePrefix); - mWriter.print(AppOpsManager.uidStateToString(uidState)); - mWriter.print("["); + mWriter.print(AppOpsService.UID_STATE_NAMES[uidState]); + mWriter.print(" = "); printedUidState = true; } else { - mWriter.print(","); + mWriter.print(", "); } mWriter.print("reject="); mWriter.print(rejectCount); @@ -1478,16 +1478,17 @@ final class HistoricalRegistry { if (accessDuration > 0) { if (!printedUidState) { mWriter.print(mUidStatePrefix); - mWriter.print(AppOpsManager.uidStateToString(uidState)); + mWriter.print(AppOpsService.UID_STATE_NAMES[uidState]); + mWriter.print(" = "); printedUidState = true; } else { - mWriter.print(","); + mWriter.print(", "); } mWriter.print("duration="); - mWriter.print(accessDuration); + TimeUtils.formatDuration(accessDuration, mWriter); } if (printedUidState) { - mWriter.println("]"); + mWriter.println(""); } } } diff --git a/services/core/java/com/android/server/content/SyncLogger.java b/services/core/java/com/android/server/content/SyncLogger.java index 5cbe5b958b7d..fc20ef20f63d 100644 --- a/services/core/java/com/android/server/content/SyncLogger.java +++ b/services/core/java/com/android/server/content/SyncLogger.java @@ -16,6 +16,7 @@ package com.android.server.content; +import android.accounts.Account; import android.app.job.JobParameters; import android.os.Build; import android.os.Environment; @@ -31,6 +32,8 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.IntPair; import com.android.server.IoThread; +import com.android.server.content.SyncManager.ActiveSyncContext; +import com.android.server.content.SyncStorageEngine.EndPoint; import libcore.io.IoUtils; @@ -309,4 +312,20 @@ public class SyncLogger { } } } + + static String logSafe(Account account) { + return account == null ? "[null]" : account.toSafeString(); + } + + static String logSafe(EndPoint endPoint) { + return endPoint == null ? "[null]" : endPoint.toSafeString(); + } + + static String logSafe(SyncOperation operation) { + return operation == null ? "[null]" : operation.toSafeString(); + } + + static String logSafe(ActiveSyncContext asc) { + return asc == null ? "[null]" : asc.toSafeString(); + } } diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 8b93e041fa4c..7096477ce5f4 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -16,6 +16,8 @@ package com.android.server.content; +import static com.android.server.content.SyncLogger.logSafe; + import android.accounts.Account; import android.accounts.AccountAndUser; import android.accounts.AccountManager; @@ -1140,7 +1142,7 @@ public class SyncManager { /* ignore - local call */ } if (checkAccountAccess && !canAccessAccount(account, owningPackage, owningUid)) { - Log.w(TAG, "Access to " + account + " denied for package " + Log.w(TAG, "Access to " + logSafe(account) + " denied for package " + owningPackage + " in UID " + syncAdapterInfo.uid); return AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS; } @@ -1474,7 +1476,8 @@ public class SyncManager { if (!syncOperation.ignoreBackoff()) { Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(syncOperation.target); if (backoff == null) { - Slog.e(TAG, "Couldn't find backoff values for " + syncOperation.target); + Slog.e(TAG, "Couldn't find backoff values for " + + logSafe(syncOperation.target)); backoff = new Pair<Long, Long>(SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE); } @@ -1745,8 +1748,8 @@ public class SyncManager { scheduleSyncOperationH(operation); } else { // Otherwise do not reschedule. - Log.d(TAG, "not retrying sync operation because the error is a hard error: " - + operation); + Log.e(TAG, "not retrying sync operation because the error is a hard error: " + + logSafe(operation)); } } @@ -1881,11 +1884,12 @@ public class SyncManager { sendSyncFinishedOrCanceledMessage(this, result); } - public void toString(StringBuilder sb) { + public void toString(StringBuilder sb, boolean logSafe) { sb.append("startTime ").append(mStartTime) .append(", mTimeoutStartTime ").append(mTimeoutStartTime) .append(", mHistoryRowId ").append(mHistoryRowId) - .append(", syncOperation ").append(mSyncOperation); + .append(", syncOperation ").append( + logSafe ? logSafe(mSyncOperation) : mSyncOperation); } public void onServiceConnected(ComponentName name, IBinder service) { @@ -1947,7 +1951,13 @@ public class SyncManager { public String toString() { StringBuilder sb = new StringBuilder(); - toString(sb); + toString(sb, false); + return sb.toString(); + } + + public String toSafeString() { + StringBuilder sb = new StringBuilder(); + toString(sb, true); return sb.toString(); } @@ -2036,7 +2046,7 @@ public class SyncManager { int count = 0; for (SyncOperation op: pendingSyncs) { if (!op.isPeriodic) { - pw.println(op.dump(null, false, buckets)); + pw.println(op.dump(null, false, buckets, /*logSafe=*/ false)); count++; } } @@ -2053,7 +2063,7 @@ public class SyncManager { int count = 0; for (SyncOperation op: pendingSyncs) { if (op.isPeriodic) { - pw.println(op.dump(null, false, buckets)); + pw.println(op.dump(null, false, buckets, /*logSafe=*/ false)); count++; } } @@ -2186,7 +2196,7 @@ public class SyncManager { sb.setLength(0); pw.print(formatDurationHMS(sb, durationInSeconds)); pw.print(" - "); - pw.print(activeSyncContext.mSyncOperation.dump(pm, false, buckets)); + pw.print(activeSyncContext.mSyncOperation.dump(pm, false, buckets, /*logSafe=*/ false)); pw.println(); } pw.println(); @@ -2974,7 +2984,7 @@ public class SyncManager { case SyncHandler.MESSAGE_CANCEL: SyncStorageEngine.EndPoint endpoint = (SyncStorageEngine.EndPoint) msg.obj; Bundle extras = msg.peekData(); - if (Log.isLoggable(TAG, Log.DEBUG)) { + if (isLoggable) { Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_CANCEL: " + endpoint + " bundle: " + extras); } @@ -2985,9 +2995,11 @@ public class SyncManager { SyncFinishedOrCancelledMessagePayload payload = (SyncFinishedOrCancelledMessagePayload) msg.obj; if (!isSyncStillActiveH(payload.activeSyncContext)) { - Log.d(TAG, "handleSyncHandlerMessage: dropping since the " - + "sync is no longer active: " - + payload.activeSyncContext); + if (isLoggable) { + Log.d(TAG, "handleSyncHandlerMessage: dropping since the " + + "sync is no longer active: " + + payload.activeSyncContext); + } break; } if (isLoggable) { @@ -3002,7 +3014,7 @@ public class SyncManager { case SyncHandler.MESSAGE_SERVICE_CONNECTED: { ServiceConnectionData msgData = (ServiceConnectionData) msg.obj; - if (Log.isLoggable(TAG, Log.VERBOSE)) { + if (isLoggable) { Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: " + msgData.activeSyncContext); } @@ -3018,7 +3030,7 @@ public class SyncManager { case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: { final ActiveSyncContext currentSyncContext = ((ServiceConnectionData) msg.obj).activeSyncContext; - if (Log.isLoggable(TAG, Log.VERBOSE)) { + if (isLoggable) { Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: " + currentSyncContext); } @@ -3053,7 +3065,7 @@ public class SyncManager { case SyncHandler.MESSAGE_MONITOR_SYNC: ActiveSyncContext monitoredSyncContext = (ActiveSyncContext) msg.obj; - if (Log.isLoggable(TAG, Log.DEBUG)) { + if (isLoggable) { Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_MONITOR_SYNC: " + monitoredSyncContext.mSyncOperation.target); } @@ -3061,7 +3073,7 @@ public class SyncManager { if (isSyncNotUsingNetworkH(monitoredSyncContext)) { Log.w(TAG, String.format( "Detected sync making no progress for %s. cancelling.", - monitoredSyncContext)); + logSafe(monitoredSyncContext))); SyncJobService.callJobFinished( monitoredSyncContext.mSyncOperation.jobId, false, "no network activity"); @@ -3558,7 +3570,8 @@ public class SyncManager { } catch (RuntimeException exc) { mLogger.log("Sync failed with RuntimeException: ", exc.toString()); closeActiveSyncContext(activeSyncContext); - Slog.e(TAG, "Caught RuntimeException while starting the sync " + syncOperation, exc); + Slog.e(TAG, "Caught RuntimeException while starting the sync " + + logSafe(syncOperation), exc); } } @@ -3658,7 +3671,8 @@ public class SyncManager { reschedulePeriodicSyncH(syncOperation); } } else { - Log.w(TAG, "failed sync operation " + syncOperation + ", " + syncResult); + Log.w(TAG, "failed sync operation " + + logSafe(syncOperation) + ", " + syncResult); syncOperation.retries++; if (syncOperation.retries > mConstants.getMaxRetriesWithAppStandbyExemption()) { @@ -4042,11 +4056,6 @@ public class SyncManager { getJobScheduler().cancel(op.jobId); } - private void wtfWithLog(String message) { - Slog.wtf(TAG, message); - mLogger.log("WTF: ", message); - } - public void resetTodayStats() { mSyncStorageEngine.resetTodayStats(/*force=*/ true); } diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java index 25edf4070689..2abc2e60a47b 100644 --- a/services/core/java/com/android/server/content/SyncOperation.java +++ b/services/core/java/com/android/server/content/SyncOperation.java @@ -363,14 +363,19 @@ public class SyncOperation { @Override public String toString() { - return dump(null, true, null); + return dump(null, true, null, false); } - String dump(PackageManager pm, boolean shorter, SyncAdapterStateFetcher appStates) { + public String toSafeString() { + return dump(null, true, null, true); + } + + String dump(PackageManager pm, boolean shorter, SyncAdapterStateFetcher appStates, + boolean logSafe) { StringBuilder sb = new StringBuilder(); sb.append("JobId=").append(jobId) .append(" ") - .append(target.account.name) + .append(logSafe ? "***" : target.account.name) .append("/") .append(target.account.type) .append(" u") diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java index bfd179141554..6b441a0d0f30 100644 --- a/services/core/java/com/android/server/content/SyncStorageEngine.java +++ b/services/core/java/com/android/server/content/SyncStorageEngine.java @@ -16,6 +16,8 @@ package com.android.server.content; +import static com.android.server.content.SyncLogger.logSafe; + import android.accounts.Account; import android.accounts.AccountAndUser; import android.accounts.AccountManager; @@ -225,6 +227,15 @@ public class SyncStorageEngine { sb.append(":u" + userId); return sb.toString(); } + + public String toSafeString() { + StringBuilder sb = new StringBuilder(); + sb.append(account == null ? "ALL ACCS" : logSafe(account)) + .append("/") + .append(provider == null ? "ALL PDRS" : provider); + sb.append(":u" + userId); + return sb.toString(); + } } public static class AuthorityInfo { @@ -1861,8 +1872,8 @@ public class SyncStorageEngine { } } else { - Slog.w(TAG, "Failure adding authority: account=" - + accountName + " auth=" + authorityName + Slog.w(TAG, "Failure adding authority:" + + " auth=" + authorityName + " enabled=" + enabled + " syncable=" + syncable); } diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java index b332c47ce281..2fe17d8f3c98 100644 --- a/services/core/java/com/android/server/display/ColorDisplayService.java +++ b/services/core/java/com/android/server/display/ColorDisplayService.java @@ -34,8 +34,10 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.content.res.Resources; import android.database.ContentObserver; import android.hardware.display.ColorDisplayManager; +import android.graphics.ColorSpace; import android.hardware.display.IColorDisplayManager; import android.net.Uri; import android.opengl.Matrix; @@ -59,6 +61,7 @@ import com.android.server.twilight.TwilightListener; import com.android.server.twilight.TwilightManager; import com.android.server.twilight.TwilightState; +import java.io.PrintWriter; import java.time.DateTimeException; import java.time.Instant; import java.time.LocalDateTime; @@ -148,26 +151,204 @@ public final class ColorDisplayService extends SystemService { }; private final TintController mDisplayWhiteBalanceTintController = new TintController() { - + // Three chromaticity coordinates per color: X, Y, and Z + private final int NUM_VALUES_PER_PRIMARY = 3; + // Four colors: red, green, blue, and white + private final int NUM_DISPLAY_PRIMARIES_VALS = 4 * NUM_VALUES_PER_PRIMARY; + + private final Object mLock = new Object(); + private int mTemperatureMin; + private int mTemperatureMax; + private int mTemperatureDefault; + private float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY]; + private ColorSpace.Rgb mDisplayColorSpaceRGB; + private float[] mChromaticAdaptationMatrix; + private int mCurrentColorTemperature; + private float[] mCurrentColorTemperatureXYZ; + private boolean mSetUp = false; private float[] mMatrixDisplayWhiteBalance = new float[16]; @Override public void setUp(Context context, boolean needsLinear) { + mSetUp = false; + + final Resources res = getContext().getResources(); + final String[] displayPrimariesValues = res.getStringArray( + R.array.config_displayWhiteBalanceDisplayPrimaries); + final String[] nominalWhiteValues = res.getStringArray( + R.array.config_displayWhiteBalanceDisplayNominalWhite); + + if (displayPrimariesValues.length != NUM_DISPLAY_PRIMARIES_VALS) { + Slog.e(TAG, "Unexpected display white balance primaries resource length " + + displayPrimariesValues.length); + return; + } + + if (nominalWhiteValues.length != NUM_VALUES_PER_PRIMARY) { + Slog.e(TAG, "Unexpected display white balance nominal white resource length " + + nominalWhiteValues.length); + return; + } + + float[] displayRedGreenBlueXYZ = + new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY]; + float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY]; + for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) { + displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]); + } + for (int i = 0; i < displayWhiteXYZ.length; i++) { + displayWhiteXYZ[i] = Float.parseFloat( + displayPrimariesValues[displayRedGreenBlueXYZ.length + i]); + } + + final ColorSpace.Rgb displayColorSpaceRGB = new ColorSpace.Rgb( + "Display Color Space", + displayRedGreenBlueXYZ, + displayWhiteXYZ, + 2.2f // gamma, unused for display white balance + ); + + float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY]; + for (int i = 0; i < nominalWhiteValues.length; i++) { + displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]); + } + + final int colorTemperatureMin = res.getInteger( + R.integer.config_displayWhiteBalanceColorTemperatureMin); + if (colorTemperatureMin <= 0) { + Slog.e(TAG, "display white balance minimum temperature must be greater than 0"); + return; + } + + final int colorTemperatureMax = res.getInteger( + R.integer.config_displayWhiteBalanceColorTemperatureMax); + if (colorTemperatureMax < colorTemperatureMin) { + Slog.e(TAG, "display white balance max temp must be greater or equal to min"); + return; + } + + final int colorTemperature = res.getInteger( + R.integer.config_displayWhiteBalanceColorTemperatureDefault); + + synchronized (mLock) { + mDisplayColorSpaceRGB = displayColorSpaceRGB; + mDisplayNominalWhiteXYZ = displayNominalWhiteXYZ; + mTemperatureMin = colorTemperatureMin; + mTemperatureMax = colorTemperatureMax; + mTemperatureDefault = colorTemperature; + mSetUp = true; + } + + setMatrix(mTemperatureDefault); } @Override public float[] getMatrix() { - return isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY; + return mSetUp && isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY; } @Override public void setMatrix(int cct) { + if (!mSetUp) { + Slog.w(TAG, "Can't set display white balance temperature: uninitialized"); + return; + } + + if (cct < mTemperatureMin) { + Slog.w(TAG, "Requested display color temperature is below allowed minimum"); + cct = mTemperatureMin; + } else if (cct > mTemperatureMax) { + Slog.w(TAG, "Requested display color temperature is above allowed maximum"); + cct = mTemperatureMax; + } + + Slog.d(TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct); + + synchronized (mLock) { + mCurrentColorTemperature = cct; + + // Adapt the display's nominal white point to match the requested CCT value + mCurrentColorTemperatureXYZ = ColorSpace.cctToIlluminantdXyz(cct); + + mChromaticAdaptationMatrix = + ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD, + mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ); + + // Convert the adaptation matrix to RGB space + float[] result = ColorSpace.mul3x3(mChromaticAdaptationMatrix, + mDisplayColorSpaceRGB.getTransform()); + result = ColorSpace.mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result); + + // Normalize the transform matrix to peak white value in RGB space + final float adaptedMaxR = result[0] + result[3] + result[6]; + final float adaptedMaxG = result[1] + result[4] + result[7]; + final float adaptedMaxB = result[2] + result[5] + result[8]; + final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB); + for (int i = 0; i < result.length; i++) { + result[i] /= denum; + } + + Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0); + java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3); + java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3); + java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3); + } } @Override public int getLevel() { return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE; } + + /** + * Format a given matrix into a string. + * + * @param matrix the matrix to format + * @param cols number of columns in the matrix + */ + private String matrixToString(float[] matrix, int cols) { + if (matrix == null || cols <= 0) { + Slog.e(TAG, "Invalid arguments when formatting matrix to string"); + return ""; + } + + StringBuilder sb = new StringBuilder(""); + for (int i = 0; i < matrix.length; i++) { + if (i % cols == 0) { + sb.append("\n "); + } + sb.append(String.format("%9.6f ", matrix[i])); + } + return sb.toString(); + } + + @Override + public void dump(PrintWriter pw) { + synchronized (mLock) { + pw.println("ColorDisplayService"); + pw.println(" mSetUp = " + mSetUp); + + if (!mSetUp) { + return; + } + + pw.println(" isActivated = " + isActivated()); + pw.println(" mTemperatureMin = " + mTemperatureMin); + pw.println(" mTemperatureMax = " + mTemperatureMax); + pw.println(" mTemperatureDefault = " + mTemperatureDefault); + pw.println(" mCurrentColorTemperature = " + mCurrentColorTemperature); + pw.println(" mCurrentColorTemperatureXYZ = " + + matrixToString(mCurrentColorTemperatureXYZ, 3)); + pw.println(" mDisplayColorSpaceRGB RGB-to-XYZ = " + + matrixToString(mDisplayColorSpaceRGB.getTransform(), 3)); + pw.println(" mChromaticAdaptationMatrix = " + + matrixToString(mChromaticAdaptationMatrix, 3)); + pw.println(" mDisplayColorSpaceRGB XYZ-to-RGB = " + + matrixToString(mDisplayColorSpaceRGB.getInverseTransform(), 3)); + pw.println(" mMatrixDisplayWhiteBalance = " + + matrixToString(mMatrixDisplayWhiteBalance, 4)); + } + } }; private final TintController mGlobalSaturationTintController = new TintController() { @@ -230,8 +411,6 @@ public final class ColorDisplayService extends SystemService { private NightDisplayAutoMode mNightDisplayAutoMode; - private Integer mDisplayWhiteBalanceColorTemperature; - public ColorDisplayService(Context context) { super(context); mHandler = new TintHandler(Looper.getMainLooper()); @@ -363,7 +542,7 @@ public final class ColorDisplayService extends SystemService { onAccessibilityTransformChanged(); break; case Secure.DISPLAY_WHITE_BALANCE_ENABLED: - onDisplayWhiteBalanceEnabled(isDisplayWhiteBalanceSettingEnabled()); + updateDisplayWhiteBalanceStatus(); break; } } @@ -416,14 +595,9 @@ public final class ColorDisplayService extends SystemService { if (ColorDisplayManager.isDisplayWhiteBalanceAvailable(getContext())) { // Prepare the display white balance transform matrix. - mDisplayWhiteBalanceTintController - .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix()); - if (mDisplayWhiteBalanceColorTemperature != null) { - mDisplayWhiteBalanceTintController - .setMatrix(mDisplayWhiteBalanceColorTemperature); - } + mDisplayWhiteBalanceTintController.setUp(getContext(), true /* needsLinear */); - onDisplayWhiteBalanceEnabled(isDisplayWhiteBalanceSettingEnabled()); + updateDisplayWhiteBalanceStatus(); } } @@ -460,6 +634,8 @@ public final class ColorDisplayService extends SystemService { mNightDisplayAutoMode.onActivated(activated); } + updateDisplayWhiteBalanceStatus(); + applyTint(mNightDisplayTintController, false); } } @@ -516,11 +692,7 @@ public final class ColorDisplayService extends SystemService { .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode)); mNightDisplayTintController.setMatrix(mNightDisplayController.getColorTemperature()); - mDisplayWhiteBalanceTintController - .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode)); - if (mDisplayWhiteBalanceColorTemperature != null) { - mDisplayWhiteBalanceTintController.setMatrix(mDisplayWhiteBalanceColorTemperature); - } + updateDisplayWhiteBalanceStatus(); final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class); dtm.setColorMode(mode, mNightDisplayTintController.getMatrix()); @@ -611,10 +783,15 @@ public final class ColorDisplayService extends SystemService { return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt; } - private void onDisplayWhiteBalanceEnabled(boolean enabled) { - mDisplayWhiteBalanceTintController.setActivated(enabled); - if (mDisplayWhiteBalanceListener != null) { - mDisplayWhiteBalanceListener.onDisplayWhiteBalanceStatusChanged(enabled); + private void updateDisplayWhiteBalanceStatus() { + boolean oldActivated = mDisplayWhiteBalanceTintController.isActivated(); + mDisplayWhiteBalanceTintController.setActivated(isDisplayWhiteBalanceSettingEnabled() && + !mNightDisplayTintController.isActivated() && + DisplayTransformManager.needsLinearColorMatrix()); + boolean activated = mDisplayWhiteBalanceTintController.isActivated(); + + if (mDisplayWhiteBalanceListener != null && oldActivated != activated) { + mDisplayWhiteBalanceListener.onDisplayWhiteBalanceStatusChanged(activated); } } @@ -896,6 +1073,12 @@ public final class ColorDisplayService extends SystemService { } /** + * Dump debug information. + */ + public void dump(PrintWriter pw) { + } + + /** * Set up any constants needed for computing the matrix. */ public abstract void setUp(Context context, boolean needsLinear); @@ -929,11 +1112,10 @@ public final class ColorDisplayService extends SystemService { */ public boolean setDisplayWhiteBalanceColorTemperature(int cct) { // Update the transform matrix even if it can't be applied. - mDisplayWhiteBalanceColorTemperature = cct; mDisplayWhiteBalanceTintController.setMatrix(cct); if (mDisplayWhiteBalanceTintController.isActivated()) { - applyTint(mDisplayWhiteBalanceTintController, true); + applyTint(mDisplayWhiteBalanceTintController, false); return true; } return false; @@ -946,6 +1128,10 @@ public final class ColorDisplayService extends SystemService { mDisplayWhiteBalanceListener = listener; return mDisplayWhiteBalanceTintController.isActivated(); } + + public void dump(PrintWriter pw) { + mDisplayWhiteBalanceTintController.dump(pw); + } } /** diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 4a17c6591449..2340b77c4a3c 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -95,6 +95,7 @@ import com.android.server.SystemService; import com.android.server.UiThread; import com.android.server.wm.SurfaceAnimationThread; import com.android.server.wm.WindowManagerInternal; +import com.android.server.display.ColorDisplayService.ColorDisplayServiceInternal; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -1459,6 +1460,13 @@ public final class DisplayManagerService extends SystemService { pw.println(); mPersistentDataStore.dump(pw); + + final ColorDisplayServiceInternal cds = LocalServices.getService( + ColorDisplayServiceInternal.class); + if (cds != null) { + pw.println(); + cds.dump(pw); + } } } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 5fa3f52c6302..7ff6a2fc5574 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -89,6 +89,7 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; +import android.os.UserManagerInternal; import android.provider.Settings; import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; @@ -309,6 +310,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private final HardKeyboardListener mHardKeyboardListener; private final AppOpsManager mAppOpsManager; private final UserManager mUserManager; + private final UserManagerInternal mUserManagerInternal; // All known input methods. mMethodMap also serves as the global // lock for this class. @@ -1405,6 +1407,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub }, true /*asyncHandler*/); mAppOpsManager = mContext.getSystemService(AppOpsManager.class); mUserManager = mContext.getSystemService(UserManager.class); + mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); mHardKeyboardListener = new HardKeyboardListener(); mHasFeature = context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_INPUT_METHODS); @@ -1489,7 +1492,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // If the system is not ready or the device is not yed unlocked by the user, then we use // copy-on-write settings. final boolean useCopyOnWriteSettings = - !mSystemReady || !mUserManager.isUserUnlockingOrUnlocked(newUserId); + !mSystemReady || !mUserManagerInternal.isUserUnlockingOrUnlocked(newUserId); mSettings.switchCurrentUser(newUserId, useCopyOnWriteSettings); updateCurrentProfileIds(); // Additional subtypes should be reset when the user is changed @@ -1562,7 +1565,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mLastSystemLocales = mRes.getConfiguration().getLocales(); final int currentUserId = mSettings.getCurrentUserId(); mSettings.switchCurrentUser(currentUserId, - !mUserManager.isUserUnlockingOrUnlocked(currentUserId)); + !mUserManagerInternal.isUserUnlockingOrUnlocked(currentUserId)); mKeyguardManager = mContext.getSystemService(KeyguardManager.class); mNotificationManager = mContext.getSystemService(NotificationManager.class); mStatusBar = statusBar; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 20eebe7bf2fc..7323e93ebf84 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2311,22 +2311,22 @@ public class NotificationManagerService extends SystemService { } @Override - public boolean areAppOverlaysAllowed(String pkg) { - return areAppOverlaysAllowedForPackage(pkg, Binder.getCallingUid()); + public boolean areBubblesAllowed(String pkg) { + return areBubblesAllowedForPackage(pkg, Binder.getCallingUid()); } @Override - public boolean areAppOverlaysAllowedForPackage(String pkg, int uid) { - enforceSystemOrSystemUIOrSamePackage("Caller not system or systemui or same package", - pkg); - return mPreferencesHelper.areAppOverlaysAllowed(pkg, uid); + public boolean areBubblesAllowedForPackage(String pkg, int uid) { + enforceSystemOrSystemUIOrSamePackage(pkg, + "Caller not system or systemui or same package"); + return mPreferencesHelper.areBubblessAllowed(pkg, uid); } @Override - public void setAppOverlaysAllowed(String pkg, int uid, boolean allowed) { + public void setBubblesAllowed(String pkg, int uid, boolean allowed) { checkCallerIsSystem(); - mPreferencesHelper.setAppOverlaysAllowed(pkg, uid, allowed); + mPreferencesHelper.setBubblesAllowed(pkg, uid, allowed); handleSavePolicyFile(); } diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 28f6972636be..7a21aa208dfd 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -81,7 +81,7 @@ public class PreferencesHelper implements RankingConfig { private static final String ATT_NAME = "name"; private static final String ATT_UID = "uid"; private static final String ATT_ID = "id"; - private static final String ATT_APP_OVERLAY = "overlay"; + private static final String ATT_ALLOW_BUBBLE = "allow_bubble"; private static final String ATT_PRIORITY = "priority"; private static final String ATT_VISIBILITY = "visibility"; private static final String ATT_IMPORTANCE = "importance"; @@ -94,8 +94,9 @@ public class PreferencesHelper implements RankingConfig { private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE; private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED; private static final boolean DEFAULT_SHOW_BADGE = true; - private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true; + private static final boolean DEFAULT_ALLOW_BUBBLE = true; private static final boolean DEFAULT_OEM_LOCKED_IMPORTANCE = false; + /** * Default value for what fields are user locked. See {@link LockableAppFields} for all lockable * fields. @@ -108,7 +109,7 @@ public class PreferencesHelper implements RankingConfig { @IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE}) public @interface LockableAppFields { int USER_LOCKED_IMPORTANCE = 0x00000001; - int USER_LOCKED_APP_OVERLAY = 0x00000002; + int USER_LOCKED_BUBBLE = 0x00000002; } // pkg|uid => PackagePreferences @@ -176,7 +177,7 @@ public class PreferencesHelper implements RankingConfig { XmlUtils.readBooleanAttribute( parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE), XmlUtils.readBooleanAttribute( - parser, ATT_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY)); + parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE)); r.importance = XmlUtils.readIntAttribute( parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE); r.priority = XmlUtils.readIntAttribute( @@ -272,11 +273,11 @@ public class PreferencesHelper implements RankingConfig { private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid) { return getOrCreatePackagePreferences(pkg, uid, DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE, - DEFAULT_ALLOW_APP_OVERLAY); + DEFAULT_ALLOW_BUBBLE); } private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid, int importance, - int priority, int visibility, boolean showBadge, boolean allowAppOverlay) { + int priority, int visibility, boolean showBadge, boolean allowBubble) { final String key = packagePreferencesKey(pkg, uid); synchronized (mPackagePreferences) { PackagePreferences @@ -290,7 +291,7 @@ public class PreferencesHelper implements RankingConfig { r.priority = priority; r.visibility = visibility; r.showBadge = showBadge; - r.appOverlay = allowAppOverlay; + r.allowBubble = allowBubble; try { createDefaultChannelIfNeeded(r); @@ -392,7 +393,7 @@ public class PreferencesHelper implements RankingConfig { || r.channels.size() > 0 || r.groups.size() > 0 || r.delegate != null - || r.appOverlay != DEFAULT_ALLOW_APP_OVERLAY; + || r.allowBubble != DEFAULT_ALLOW_BUBBLE; if (hasNonDefaultSettings) { out.startTag(null, TAG_PACKAGE); out.attribute(null, ATT_NAME, r.pkg); @@ -405,8 +406,8 @@ public class PreferencesHelper implements RankingConfig { if (r.visibility != DEFAULT_VISIBILITY) { out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility)); } - if (r.appOverlay != DEFAULT_ALLOW_APP_OVERLAY) { - out.attribute(null, ATT_APP_OVERLAY, Boolean.toString(r.appOverlay)); + if (r.allowBubble != DEFAULT_ALLOW_BUBBLE) { + out.attribute(null, ATT_ALLOW_BUBBLE, Boolean.toString(r.allowBubble)); } out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge)); out.attribute(null, ATT_APP_USER_LOCKED_FIELDS, @@ -452,14 +453,28 @@ public class PreferencesHelper implements RankingConfig { out.endTag(null, TAG_RANKING); } - public void setAppOverlaysAllowed(String pkg, int uid, boolean allowed) { + /** + * Sets whether bubbles are allowed. + * + * @param pkg the package to allow or not allow bubbles for. + * @param uid the uid to allow or not allow bubbles for. + * @param allowed whether bubbles are allowed. + */ + public void setBubblesAllowed(String pkg, int uid, boolean allowed) { PackagePreferences p = getOrCreatePackagePreferences(pkg, uid); - p.appOverlay = allowed; - p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_APP_OVERLAY; + p.allowBubble = allowed; + p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_BUBBLE; } - public boolean areAppOverlaysAllowed(String pkg, int uid) { - return getOrCreatePackagePreferences(pkg, uid).appOverlay; + /** + * Whether bubbles are allowed. + * + * @param pkg the package to check if bubbles are allowed for + * @param uid the uid to check if bubbles are allowed for. + * @return whether bubbles are allowed. + */ + public boolean areBubblessAllowed(String pkg, int uid) { + return getOrCreatePackagePreferences(pkg, uid).allowBubble; } public int getAppLockedFields(String pkg, int uid) { @@ -1232,8 +1247,8 @@ public class PreferencesHelper implements RankingConfig { if (original.canShowBadge() != update.canShowBadge()) { update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE); } - if (original.isAppOverlayAllowed() != update.isAppOverlayAllowed()) { - update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_APP_OVERLAY); + if (original.isBubbleAllowed() != update.isBubbleAllowed()) { + update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_BUBBLE); } } @@ -1654,7 +1669,7 @@ public class PreferencesHelper implements RankingConfig { int priority = DEFAULT_PRIORITY; int visibility = DEFAULT_VISIBILITY; boolean showBadge = DEFAULT_SHOW_BADGE; - boolean appOverlay = DEFAULT_ALLOW_APP_OVERLAY; + boolean allowBubble = DEFAULT_ALLOW_BUBBLE; int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS; boolean oemLockedImportance = DEFAULT_OEM_LOCKED_IMPORTANCE; List<String> futureOemLockedChannels = new ArrayList<>(); diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index eb3017db7340..b6dae1977262 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -304,6 +304,17 @@ public class LauncherAppsService extends SystemService { } @Override + public boolean shouldHideFromSuggestions(String packageName, UserHandle user) { + if (!canAccessProfile(user.getIdentifier(), "cannot get shouldHideFromSuggestions")) { + return false; + } + final PackageManagerInternal pmi = LocalServices.getService( + PackageManagerInternal.class); + int flags = pmi.getDistractingPackageRestrictions(packageName, user.getIdentifier()); + return (flags & PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS) != 0; + } + + @Override public ParceledListSlice<ResolveInfo> getLauncherActivities(String callingPackage, String packageName, UserHandle user) throws RemoteException { ParceledListSlice<ResolveInfo> launcherActivities = queryActivitiesForUser( diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index d0b20e815a42..bf4e272d4004 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -225,6 +225,17 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements Slog.w(TAG, "Deleting orphan icon " + icon); icon.delete(); } + + // Invalid sessions might have been marked while parsing. Re-write the database with + // the updated information. + writeSessionsLocked(); + + for (int i = 0; i < mSessions.size(); i++) { + final PackageInstallerSession session = mSessions.valueAt(i); + if (session.isStaged()) { + mStagingManager.restoreSession(session); + } + } } } @@ -329,9 +340,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements if (valid) { mSessions.put(session.sessionId, session); - if (session.isStaged()) { - mStagingManager.restoreSession(session); - } } else { // Since this is early during boot we don't send // any observer events about the session, but we @@ -1122,6 +1130,11 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress); } + public void onStagedSessionChanged(PackageInstallerSession session) { + writeSessionsAsync(); + mPm.sendSessionUpdatedBroadcast(session.generateInfo(false), session.userId); + } + public void onSessionFinished(final PackageInstallerSession session, boolean success) { mCallbacks.notifySessionFinished(session.sessionId, session.userId, success); diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 516927fa7c49..12d335dd87ab 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -2009,6 +2009,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mCallback.onSessionFinished(this, success); } + /** {@hide} */ void setStagedSessionReady() { synchronized (mLock) { mStagedSessionReady = true; @@ -2016,6 +2017,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mStagedSessionFailed = false; mStagedSessionErrorCode = SessionInfo.NO_ERROR; } + mCallback.onStagedSessionChanged(this); } /** {@hide} */ @@ -2026,6 +2028,33 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mStagedSessionFailed = true; mStagedSessionErrorCode = errorCode; } + mCallback.onStagedSessionChanged(this); + } + + /** {@hide} */ + void setStagedSessionApplied() { + synchronized (mLock) { + mStagedSessionReady = false; + mStagedSessionApplied = true; + mStagedSessionFailed = false; + mStagedSessionErrorCode = SessionInfo.NO_ERROR; + } + mCallback.onStagedSessionChanged(this); + } + + /** {@hide} */ + boolean isStagedSessionReady() { + return mStagedSessionReady; + } + + /** {@hide} */ + boolean isStagedSessionApplied() { + return mStagedSessionApplied; + } + + /** {@hide} */ + boolean isStagedSessionFailed() { + return mStagedSessionFailed; } private void destroyInternal() { @@ -2221,6 +2250,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return permissionsArray; } + // Sanity check to be performed when the session is restored from an external file. Only one + // of the session states should be true, or none of them. + private static boolean isStagedSessionStateValid(boolean isReady, boolean isApplied, + boolean isFailed) { + return (!isReady && !isApplied && !isFailed) + || (isReady && !isApplied && !isFailed) + || (!isReady && isApplied && !isFailed) + || (!isReady && !isApplied && isFailed); + } + /** * Read new session from a {@link XmlPullParser xml description} and create it. * @@ -2287,6 +2326,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final boolean isApplied = readBooleanAttribute(in, ATTR_IS_APPLIED); final int stagedSessionErrorCode = readIntAttribute(in, ATTR_STAGED_SESSION_ERROR_CODE); + if (!isStagedSessionStateValid(isReady, isApplied, isFailed)) { + throw new IllegalArgumentException("Can't restore staged session with invalid state."); + } + return new PackageInstallerSession(callback, context, pm, sessionProvider, installerThread, stagingManager, sessionId, userId, installerPackageName, installerUid, params, createdMillis, stageDir, stageCid, prepared, sealed, diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 597f5b3f4e05..69323902edc8 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -84,6 +84,7 @@ import static android.content.pm.PackageManager.MOVE_FAILED_OPERATION_PENDING; import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE; import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.content.pm.PackageManager.RESTRICTION_NONE; import static android.content.pm.PackageParser.isApkFile; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static android.os.storage.StorageManager.FLAG_STORAGE_CE; @@ -238,6 +239,7 @@ import android.os.storage.StorageManager; import android.os.storage.StorageManagerInternal; import android.os.storage.VolumeInfo; import android.os.storage.VolumeRecord; +import android.permission.PermissionControllerManager; import android.provider.MediaStore; import android.provider.Settings.Global; import android.provider.Settings.Secure; @@ -316,7 +318,6 @@ import com.android.server.pm.permission.PermissionManagerInternal; import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback; import com.android.server.pm.permission.PermissionManagerService; import com.android.server.pm.permission.PermissionsState; -import com.android.server.pm.permission.PermissionsState.PermissionState; import com.android.server.security.VerityUtils; import com.android.server.storage.DeviceStorageMonitorInternal; import com.android.server.wm.ActivityTaskManagerInternal; @@ -369,6 +370,7 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; @@ -442,6 +444,8 @@ public class PackageManagerService extends IPackageManager.Stub private static final boolean ENABLE_FREE_CACHE_V2 = SystemProperties.getBoolean("fw.free_cache_v2", true); + private static final long BACKUP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(60); + private static final int RADIO_UID = Process.PHONE_UID; private static final int LOG_UID = Process.LOG_UID; private static final int NFC_UID = Process.NFC_UID; @@ -12657,22 +12661,30 @@ public class PackageManagerService extends IPackageManager.Stub info.sendPackageRemovedBroadcasts(true /*killApp*/); } + private void sendDistractingPackagesChanged(String[] pkgList, int[] uidList, int userId, + int distractionFlags) { + final Bundle extras = new Bundle(3); + extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList); + extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList); + extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags); + sendPackageBroadcast(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null, extras, + Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null, new int[]{userId}, null); + } + private void sendPackagesSuspendedForUser(String[] pkgList, int[] uidList, int userId, boolean suspended, PersistableBundle launcherExtras) { - if (pkgList.length > 0) { - Bundle extras = new Bundle(1); - extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList); - extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList); - if (launcherExtras != null) { - extras.putBundle(Intent.EXTRA_LAUNCHER_EXTRAS, - new Bundle(launcherExtras.deepCopy())); - } - sendPackageBroadcast( - suspended ? Intent.ACTION_PACKAGES_SUSPENDED - : Intent.ACTION_PACKAGES_UNSUSPENDED, - null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null, - new int[] {userId}, null); + final Bundle extras = new Bundle(3); + extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList); + extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList); + if (launcherExtras != null) { + extras.putBundle(Intent.EXTRA_LAUNCHER_EXTRAS, + new Bundle(launcherExtras.deepCopy())); } + sendPackageBroadcast( + suspended ? Intent.ACTION_PACKAGES_SUSPENDED + : Intent.ACTION_PACKAGES_UNSUSPENDED, + null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null, + new int[] {userId}, null); } /** @@ -12822,6 +12834,62 @@ public class PackageManagerService extends IPackageManager.Stub } @Override + public String[] setDistractingPackageRestrictionsAsUser(String[] packageNames, + int restrictionFlags, int userId) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS, + "setPackagesSuspendedAsUser"); + + final int callingUid = Binder.getCallingUid(); + if (callingUid != Process.ROOT_UID && callingUid != Process.SYSTEM_UID + && UserHandle.getUserId(callingUid) != userId) { + throw new SecurityException("Calling uid " + callingUid + " cannot call for user " + + userId); + } + Preconditions.checkNotNull(packageNames, "packageNames cannot be null"); + + final List<String> changedPackagesList = new ArrayList<>(packageNames.length); + final IntArray changedUids = new IntArray(packageNames.length); + final List<String> unactionedPackages = new ArrayList<>(packageNames.length); + + for (int i = 0; i < packageNames.length; i++) { + final String packageName = packageNames[i]; + final PackageSetting pkgSetting; + synchronized (mPackages) { + pkgSetting = mSettings.mPackages.get(packageName); + if (pkgSetting == null || filterAppAccessLPr(pkgSetting, callingUid, userId)) { + Slog.w(TAG, "Could not find package setting for package: " + packageName + + ". Skipping..."); + unactionedPackages.add(packageName); + continue; + } + } + if (restrictionFlags != 0 && !canSuspendPackageForUserInternal(packageName, userId)) { + unactionedPackages.add(packageName); + continue; + } + synchronized (mPackages) { + final int oldDistractionFlags = pkgSetting.getDistractionFlags(userId); + if (restrictionFlags != oldDistractionFlags) { + pkgSetting.setDistractionFlags(restrictionFlags, userId); + changedPackagesList.add(packageName); + changedUids.add(UserHandle.getUid(userId, pkgSetting.appId)); + } + } + } + + if (!changedPackagesList.isEmpty()) { + final String[] changedPackages = changedPackagesList.toArray( + new String[changedPackagesList.size()]); + sendDistractingPackagesChanged(changedPackages, changedUids.toArray(), userId, + restrictionFlags); + synchronized (mPackages) { + scheduleWritePackageRestrictionsLocked(userId); + } + } + return unactionedPackages.toArray(new String[0]); + } + + @Override public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended, PersistableBundle appExtras, PersistableBundle launcherExtras, SuspendDialogInfo dialogInfo, String callingPackage, int userId) { @@ -12846,44 +12914,37 @@ public class PackageManagerService extends IPackageManager.Stub final List<String> changedPackagesList = new ArrayList<>(packageNames.length); final IntArray changedUids = new IntArray(packageNames.length); final List<String> unactionedPackages = new ArrayList<>(packageNames.length); - final long callingId = Binder.clearCallingIdentity(); - try { - for (int i = 0; i < packageNames.length; i++) { - final String packageName = packageNames[i]; - if (callingPackage.equals(packageName)) { - Slog.w(TAG, "Calling package: " + callingPackage + " trying to " - + (suspended ? "" : "un") + "suspend itself. Ignoring"); - unactionedPackages.add(packageName); - continue; - } - PackageSetting pkgSetting; - synchronized (mPackages) { - pkgSetting = mSettings.mPackages.get(packageName); - if (pkgSetting == null - || filterAppAccessLPr(pkgSetting, callingUid, userId)) { - Slog.w(TAG, "Could not find package setting for package: " + packageName - + ". Skipping suspending/un-suspending."); - unactionedPackages.add(packageName); - continue; - } - } - if (suspended && !canSuspendPackageForUserInternal(packageName, userId)) { + + for (int i = 0; i < packageNames.length; i++) { + final String packageName = packageNames[i]; + if (callingPackage.equals(packageName)) { + Slog.w(TAG, "Calling package: " + callingPackage + " trying to " + + (suspended ? "" : "un") + "suspend itself. Ignoring"); + unactionedPackages.add(packageName); + continue; + } + final PackageSetting pkgSetting; + synchronized (mPackages) { + pkgSetting = mSettings.mPackages.get(packageName); + if (pkgSetting == null || filterAppAccessLPr(pkgSetting, callingUid, userId)) { + Slog.w(TAG, "Could not find package setting for package: " + packageName + + ". Skipping suspending/un-suspending."); unactionedPackages.add(packageName); continue; } - synchronized (mPackages) { - pkgSetting = mSettings.mPackages.get(packageName); - if (pkgSetting != null) { - pkgSetting.setSuspended(suspended, callingPackage, dialogInfo, appExtras, - launcherExtras, userId); - } - } - changedPackagesList.add(packageName); - changedUids.add(UserHandle.getUid(userId, pkgSetting.appId)); } - } finally { - Binder.restoreCallingIdentity(callingId); + if (suspended && !canSuspendPackageForUserInternal(packageName, userId)) { + unactionedPackages.add(packageName); + continue; + } + synchronized (mPackages) { + pkgSetting.setSuspended(suspended, callingPackage, dialogInfo, appExtras, + launcherExtras, userId); + } + changedPackagesList.add(packageName); + changedUids.add(UserHandle.getUid(userId, pkgSetting.appId)); } + if (!changedPackagesList.isEmpty()) { final String[] changedPackages = changedPackagesList.toArray( new String[changedPackagesList.size()]); @@ -13035,88 +13096,87 @@ public class PackageManagerService extends IPackageManager.Stub + " cannot query getUnsuspendablePackagesForUser for user " + userId); } final ArraySet<String> unactionablePackages = new ArraySet<>(); - final long identity = Binder.clearCallingIdentity(); - try { - for (String packageName : packageNames) { - if (!canSuspendPackageForUserInternal(packageName, userId)) { - unactionablePackages.add(packageName); - } + for (String packageName : packageNames) { + if (!canSuspendPackageForUserInternal(packageName, userId)) { + unactionablePackages.add(packageName); } - } finally { - Binder.restoreCallingIdentity(identity); } return unactionablePackages.toArray(new String[unactionablePackages.size()]); } private boolean canSuspendPackageForUserInternal(String packageName, int userId) { - if (isPackageDeviceAdmin(packageName, userId)) { - Slog.w(TAG, "Cannot suspend package \"" + packageName - + "\": has an active device admin"); - return false; - } - - String activeLauncherPackageName = getActiveLauncherPackageName(userId); - if (packageName.equals(activeLauncherPackageName)) { - Slog.w(TAG, "Cannot suspend package \"" + packageName - + "\": contains the active launcher"); - return false; - } - - if (packageName.equals(mRequiredInstallerPackage)) { - Slog.w(TAG, "Cannot suspend package \"" + packageName - + "\": required for package installation"); - return false; - } + final long callingId = Binder.clearCallingIdentity(); + try { + if (isPackageDeviceAdmin(packageName, userId)) { + Slog.w(TAG, "Cannot suspend package \"" + packageName + + "\": has an active device admin"); + return false; + } - if (packageName.equals(mRequiredUninstallerPackage)) { - Slog.w(TAG, "Cannot suspend package \"" + packageName - + "\": required for package uninstallation"); - return false; - } + String activeLauncherPackageName = getActiveLauncherPackageName(userId); + if (packageName.equals(activeLauncherPackageName)) { + Slog.w(TAG, "Cannot suspend package \"" + packageName + + "\": contains the active launcher"); + return false; + } - if (packageName.equals(mRequiredVerifierPackage)) { - Slog.w(TAG, "Cannot suspend package \"" + packageName - + "\": required for package verification"); - return false; - } + if (packageName.equals(mRequiredInstallerPackage)) { + Slog.w(TAG, "Cannot suspend package \"" + packageName + + "\": required for package installation"); + return false; + } - if (packageName.equals(getDefaultDialerPackageName(userId))) { - Slog.w(TAG, "Cannot suspend package \"" + packageName - + "\": is the default dialer"); - return false; - } + if (packageName.equals(mRequiredUninstallerPackage)) { + Slog.w(TAG, "Cannot suspend package \"" + packageName + + "\": required for package uninstallation"); + return false; + } - if (packageName.equals(mRequiredPermissionControllerPackage)) { - Slog.w(TAG, "Cannot suspend package \"" + packageName - + "\": required for permissions management"); - return false; - } + if (packageName.equals(mRequiredVerifierPackage)) { + Slog.w(TAG, "Cannot suspend package \"" + packageName + + "\": required for package verification"); + return false; + } - synchronized (mPackages) { - if (mProtectedPackages.isPackageStateProtected(userId, packageName)) { + if (packageName.equals(getDefaultDialerPackageName(userId))) { Slog.w(TAG, "Cannot suspend package \"" + packageName - + "\": protected package"); + + "\": is the default dialer"); return false; } - // Cannot suspend static shared libs as they are considered - // a part of the using app (emulating static linking). Also - // static libs are installed always on internal storage. - PackageParser.Package pkg = mPackages.get(packageName); - if (pkg != null && pkg.applicationInfo.isStaticSharedLibrary()) { - Slog.w(TAG, "Cannot suspend package: " + packageName - + " providing static shared library: " - + pkg.staticSharedLibName); + if (packageName.equals(mRequiredPermissionControllerPackage)) { + Slog.w(TAG, "Cannot suspend package \"" + packageName + + "\": required for permissions management"); return false; } - } - if (PLATFORM_PACKAGE_NAME.equals(packageName)) { - Slog.w(TAG, "Cannot suspend the platform package: " + packageName); - return false; - } + synchronized (mPackages) { + if (mProtectedPackages.isPackageStateProtected(userId, packageName)) { + Slog.w(TAG, "Cannot suspend package \"" + packageName + + "\": protected package"); + return false; + } - return true; + // Cannot suspend static shared libs as they are considered + // a part of the using app (emulating static linking). Also + // static libs are installed always on internal storage. + PackageParser.Package pkg = mPackages.get(packageName); + if (pkg != null && pkg.applicationInfo.isStaticSharedLibrary()) { + Slog.w(TAG, "Cannot suspend package: " + packageName + + " providing static shared library: " + + pkg.staticSharedLibName); + return false; + } + } + + if (PLATFORM_PACKAGE_NAME.equals(packageName)) { + Slog.w(TAG, "Cannot suspend the platform package: " + packageName); + return false; + } + return true; + } finally { + Binder.restoreCallingIdentity(callingId); + } } private String getActiveLauncherPackageName(int userId) { @@ -13717,14 +13777,15 @@ public class PackageManagerService extends IPackageManager.Stub IBackupManager bm = IBackupManager.Stub.asInterface( ServiceManager.getService(Context.BACKUP_SERVICE)); if (bm != null) { + int userId = args.user.getIdentifier(); if (DEBUG_INSTALL) { - Log.v(TAG, "token " + token + " to BM for possible restore"); + Log.v(TAG, "token " + token + " to BM for possible restore for user " + userId); } Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token); try { - // TODO: http://b/22388012 - if (bm.isBackupServiceActive(UserHandle.USER_SYSTEM)) { - bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token); + if (bm.isBackupServiceActive(userId)) { + bm.restoreAtInstallForUser( + userId, res.pkg.applicationInfo.packageName, token); } else { doRestore = false; } @@ -18425,6 +18486,7 @@ public class PackageManagerService extends IPackageManager.Stub true /*stopped*/, true /*notLaunched*/, false /*hidden*/, + 0 /*distractionFlags*/, false /*suspended*/, null /*suspendingPackage*/, null /*dialogInfo*/, @@ -19514,28 +19576,32 @@ public class PackageManagerService extends IPackageManager.Stub throw new SecurityException("Only the system may call getPermissionGrantBackup()"); } - ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); - try { - final XmlSerializer serializer = new FastXmlSerializer(); - serializer.setOutput(dataStream, StandardCharsets.UTF_8.name()); - serializer.startDocument(null, true); - serializer.startTag(null, TAG_PERMISSION_BACKUP); + AtomicReference<byte[]> backup = new AtomicReference<>(); + mContext.getSystemService(PermissionControllerManager.class).getRuntimePermissionBackup( + UserHandle.of(userId), mContext.getMainExecutor(), (b) -> { + synchronized (backup) { + backup.set(b); + backup.notifyAll(); + } + }); - synchronized (mPackages) { - serializeRuntimePermissionGrantsLPr(serializer, userId); - } + long start = System.currentTimeMillis(); + synchronized (backup) { + while (backup.get() == null) { + long timeLeft = start + BACKUP_TIMEOUT_MILLIS - System.currentTimeMillis(); + if (timeLeft <= 0) { + return null; + } - serializer.endTag(null, TAG_PERMISSION_BACKUP); - serializer.endDocument(); - serializer.flush(); - } catch (Exception e) { - if (DEBUG_BACKUP) { - Slog.e(TAG, "Unable to write default apps for backup", e); + try { + backup.wait(timeLeft); + } catch (InterruptedException ignored) { + return null; + } } - return null; } - return dataStream.toByteArray(); + return backup.get(); } @Override @@ -19561,66 +19627,6 @@ public class PackageManagerService extends IPackageManager.Stub } @GuardedBy("mPackages") - private void serializeRuntimePermissionGrantsLPr(XmlSerializer serializer, final int userId) - throws IOException { - serializer.startTag(null, TAG_ALL_GRANTS); - - final int N = mSettings.mPackages.size(); - for (int i = 0; i < N; i++) { - final PackageSetting ps = mSettings.mPackages.valueAt(i); - boolean pkgGrantsKnown = false; - - PermissionsState packagePerms = ps.getPermissionsState(); - - for (PermissionState state : packagePerms.getRuntimePermissionStates(userId)) { - final int grantFlags = state.getFlags(); - // only look at grants that are not system/policy fixed - if ((grantFlags & SYSTEM_RUNTIME_GRANT_MASK) == 0) { - final boolean isGranted = state.isGranted(); - // And only back up the user-twiddled state bits - if (isGranted || (grantFlags & USER_RUNTIME_GRANT_MASK) != 0) { - final String packageName = mSettings.mPackages.keyAt(i); - if (!pkgGrantsKnown) { - serializer.startTag(null, TAG_GRANT); - serializer.attribute(null, ATTR_PACKAGE_NAME, packageName); - pkgGrantsKnown = true; - } - - final boolean userSet = - (grantFlags & FLAG_PERMISSION_USER_SET) != 0; - final boolean userFixed = - (grantFlags & FLAG_PERMISSION_USER_FIXED) != 0; - final boolean revoke = - (grantFlags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0; - - serializer.startTag(null, TAG_PERMISSION); - serializer.attribute(null, ATTR_PERMISSION_NAME, state.getName()); - if (isGranted) { - serializer.attribute(null, ATTR_IS_GRANTED, "true"); - } - if (userSet) { - serializer.attribute(null, ATTR_USER_SET, "true"); - } - if (userFixed) { - serializer.attribute(null, ATTR_USER_FIXED, "true"); - } - if (revoke) { - serializer.attribute(null, ATTR_REVOKE_ON_UPGRADE, "true"); - } - serializer.endTag(null, TAG_PERMISSION); - } - } - } - - if (pkgGrantsKnown) { - serializer.endTag(null, TAG_GRANT); - } - } - - serializer.endTag(null, TAG_ALL_GRANTS); - } - - @GuardedBy("mPackages") private void processRestoredPermissionGrantsLPr(XmlPullParser parser, int userId) throws XmlPullParserException, IOException { String pkgName = null; @@ -23240,6 +23246,14 @@ public class PackageManagerService extends IPackageManager.Stub } @Override + public int getDistractingPackageRestrictions(String packageName, int userId) { + synchronized (mPackages) { + final PackageSetting ps = mSettings.mPackages.get(packageName); + return (ps != null) ? ps.getDistractionFlags(userId) : RESTRICTION_NONE; + } + } + + @Override public int getPackageUid(String packageName, int flags, int userId) { return PackageManagerService.this .getPackageUid(packageName, flags, userId); diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 3c22f07ad108..58f262c4c889 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -392,6 +392,14 @@ public abstract class PackageSettingBase extends SettingBase { modifyUserState(userId).hidden = hidden; } + int getDistractionFlags(int userId) { + return readUserState(userId).distractionFlags; + } + + void setDistractionFlags(int distractionFlags, int userId) { + modifyUserState(userId).distractionFlags = distractionFlags; + } + boolean getSuspended(int userId) { return readUserState(userId).suspended; } @@ -423,7 +431,8 @@ public abstract class PackageSettingBase extends SettingBase { } void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped, - boolean notLaunched, boolean hidden, boolean suspended, String suspendingPackage, + boolean notLaunched, boolean hidden, int distractionFlags, boolean suspended, + String suspendingPackage, SuspendDialogInfo dialogInfo, PersistableBundle suspendedAppExtras, PersistableBundle suspendedLauncherExtras, boolean instantApp, boolean virtualPreload, String lastDisableAppCaller, @@ -437,6 +446,7 @@ public abstract class PackageSettingBase extends SettingBase { state.stopped = stopped; state.notLaunched = notLaunched; state.hidden = hidden; + state.distractionFlags = distractionFlags; state.suspended = suspended; state.suspendingPackage = suspendingPackage; state.dialogInfo = dialogInfo; @@ -607,6 +617,7 @@ public abstract class PackageSettingBase extends SettingBase { } proto.write(PackageProto.UserInfoProto.INSTALL_TYPE, installType); proto.write(PackageProto.UserInfoProto.IS_HIDDEN, state.hidden); + proto.write(PackageProto.UserInfoProto.DISTRACTION_FLAGS, state.distractionFlags); proto.write(PackageProto.UserInfoProto.IS_SUSPENDED, state.suspended); if (state.suspended) { proto.write(PackageProto.UserInfoProto.SUSPENDING_PACKAGE, state.suspendingPackage); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index c524dba01ba6..95da2091828d 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -223,6 +223,7 @@ public final class Settings { private static final String ATTR_BLOCKED = "blocked"; // New name for the above attribute. private static final String ATTR_HIDDEN = "hidden"; + private static final String ATTR_DISTRACTION_FLAGS = "distraction_flags"; private static final String ATTR_SUSPENDED = "suspended"; private static final String ATTR_SUSPENDING_PACKAGE = "suspending-package"; /** @@ -734,6 +735,7 @@ public final class Settings { true /*stopped*/, true /*notLaunched*/, false /*hidden*/, + 0 /*distractionFlags*/, false /*suspended*/, null /*suspendingPackage*/, null /*dialogInfo*/, @@ -1628,6 +1630,7 @@ public final class Settings { false /*stopped*/, false /*notLaunched*/, false /*hidden*/, + 0 /*distractionFlags*/, false /*suspended*/, null /*suspendingPackage*/, null /*dialogInfo*/, @@ -1703,6 +1706,8 @@ public final class Settings { hidden = hiddenStr == null ? hidden : Boolean.parseBoolean(hiddenStr); + final int distractionFlags = XmlUtils.readIntAttribute(parser, + ATTR_DISTRACTION_FLAGS, 0); final boolean suspended = XmlUtils.readBooleanAttribute(parser, ATTR_SUSPENDED, false); String suspendingPackage = parser.getAttributeValue(null, @@ -1781,7 +1786,8 @@ public final class Settings { setBlockUninstallLPw(userId, name, true); } ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched, - hidden, suspended, suspendingPackage, suspendDialogInfo, + hidden, distractionFlags, suspended, suspendingPackage, + suspendDialogInfo, suspendedAppExtras, suspendedLauncherExtras, instantApp, virtualPreload, enabledCaller, enabledComponents, disabledComponents, verifState, linkGeneration, installReason, harmfulAppWarning); @@ -2089,6 +2095,10 @@ public final class Settings { if (ustate.hidden) { serializer.attribute(null, ATTR_HIDDEN, "true"); } + if (ustate.distractionFlags != 0) { + serializer.attribute(null, ATTR_DISTRACTION_FLAGS, + Integer.toString(ustate.distractionFlags)); + } if (ustate.suspended) { serializer.attribute(null, ATTR_SUSPENDED, "true"); if (ustate.suspendingPackage != null) { diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index c297c62dfece..bd14223c962c 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -130,7 +130,7 @@ public class StagingManager { ServiceManager.getService("apexservice")); boolean success; try { - success = apex.submitStagedSession(sessionId, apexInfoList); + success = apex.submitStagedSession(sessionId, new int[0], apexInfoList); } catch (RemoteException re) { Slog.e(TAG, "Unable to contact apexservice", re); return false; @@ -168,7 +168,6 @@ public class StagingManager { } else { session.setStagedSessionFailed(SessionInfo.VERIFICATION_FAILED); } - mPm.sendSessionUpdatedBroadcast(session.generateInfo(false), session.userId); } void commitSession(@NonNull PackageInstallerSession session) { diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index b4883371b468..c0ec3672c665 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -406,7 +406,6 @@ public class RoleManagerService extends SystemService implements RoleUserState.C @Nullable private ArraySet<String> getRoleHoldersInternal(@NonNull String roleName, @UserIdInt int userId) { - migrateRoleIfNecessary(roleName, userId); RoleUserState userState = getOrCreateUserState(userId); return userState.getRoleHolders(roleName); } diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java index 69e144951154..02dcc4945664 100644 --- a/services/core/java/com/android/server/role/RoleUserState.java +++ b/services/core/java/com/android/server/role/RoleUserState.java @@ -193,14 +193,18 @@ public class RoleUserState { * * @param roleName the name of the role to query for * - * @return the set of role holders. {@code null} should not be returned and indicates an issue. + * @return the set of role holders, or {@code null} if and only if the role is not found */ @Nullable public ArraySet<String> getRoleHolders(@NonNull String roleName) { synchronized (mLock) { throwIfDestroyedLocked(); - return new ArraySet<>(mRoles.get(roleName)); + ArraySet<String> packageNames = mRoles.get(roleName); + if (packageNames == null) { + return null; + } + return new ArraySet<>(packageNames); } } @@ -268,8 +272,7 @@ public class RoleUserState { * @param roleName the name of the role to add the holder to * @param packageName the package name of the new holder * - * @return {@code false} only if the set of role holders is null, which should not happen and - * indicates an issue. + * @return {@code false} if and only if the role is not found */ @CheckResult public boolean addRoleHolder(@NonNull String roleName, @NonNull String packageName) { @@ -302,8 +305,7 @@ public class RoleUserState { * @param roleName the name of the role to remove the holder from * @param packageName the package name of the holder to remove * - * @return {@code false} only if the set of role holders is null, which should not happen and - * indicates an issue. + * @return {@code false} if and only if the role is not found */ @CheckResult public boolean removeRoleHolder(@NonNull String roleName, @NonNull String packageName) { diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index 80212655d616..d12f7eda9a1b 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -977,6 +977,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { ensureRollbackDataLoadedLocked(); mAvailableRollbacks.add(data); } + + scheduleExpiration(ROLLBACK_LIFETIME_DURATION_MILLIS); } catch (IOException e) { Log.e(TAG, "Unable to enable rollback", e); removeFile(data.backupDir); diff --git a/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java b/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java index 438c303de6cc..d77cf900a0a9 100644 --- a/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java +++ b/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java @@ -23,6 +23,7 @@ import android.provider.Settings; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; +import android.util.StatsLog; import java.security.GeneralSecurityException; import java.util.Arrays; @@ -66,12 +67,14 @@ class GlobalSettingsConfigApplicator { private final Context mContext; private final String mSourcePackage; + private final SignedConfigEvent mEvent; private final SignatureVerifier mVerifier; - GlobalSettingsConfigApplicator(Context context, String sourcePackage) { + GlobalSettingsConfigApplicator(Context context, String sourcePackage, SignedConfigEvent event) { mContext = context; mSourcePackage = sourcePackage; - mVerifier = new SignatureVerifier(); + mEvent = event; + mVerifier = new SignatureVerifier(mEvent); } private boolean checkSignature(String data, String signature) { @@ -79,6 +82,7 @@ class GlobalSettingsConfigApplicator { return mVerifier.verifySignature(data, signature); } catch (GeneralSecurityException e) { Slog.e(TAG, "Failed to verify signature", e); + mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__SECURITY_EXCEPTION; return false; } } @@ -109,14 +113,17 @@ class GlobalSettingsConfigApplicator { SignedConfig config; try { config = SignedConfig.parse(configStr, ALLOWED_KEYS, KEY_VALUE_MAPPERS); + mEvent.version = config.version; } catch (InvalidConfigException e) { Slog.e(TAG, "Failed to parse global settings from package " + mSourcePackage, e); + mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__INVALID_CONFIG; return; } int currentVersion = getCurrentConfigVersion(); if (currentVersion >= config.version) { Slog.i(TAG, "Global settings from package " + mSourcePackage + " is older than existing: " + config.version + "<=" + currentVersion); + mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__OLD_CONFIG; return; } // We have new config! @@ -126,10 +133,12 @@ class GlobalSettingsConfigApplicator { config.getMatchingConfig(Build.VERSION.SDK_INT); if (matchedConfig == null) { Slog.i(TAG, "Settings is not applicable to current SDK version; ignoring"); + mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__NOT_APPLICABLE; return; } Slog.i(TAG, "Updating global settings to version " + config.version); updateCurrentConfig(config.version, matchedConfig.values); + mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__APPLIED; } } diff --git a/services/core/java/com/android/server/signedconfig/SignatureVerifier.java b/services/core/java/com/android/server/signedconfig/SignatureVerifier.java index 944db84acc71..56db32a3071d 100644 --- a/services/core/java/com/android/server/signedconfig/SignatureVerifier.java +++ b/services/core/java/com/android/server/signedconfig/SignatureVerifier.java @@ -18,6 +18,7 @@ package com.android.server.signedconfig; import android.os.Build; import android.util.Slog; +import android.util.StatsLog; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; @@ -42,11 +43,18 @@ public class SignatureVerifier { private static final String DEBUG_KEY = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaAn2XVifsLTHg616nTsOMVmlhBoECGbTEBTKKvdd2hO60" + "pj1pnU8SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ=="; + private static final String PROD_KEY = + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+lky6wKyGL6lE1VrD0YTMHwb0Xwc+tzC8MvnrzVxodvTp" + + "VY/jV7V+Zktcx+pry43XPABFRXtbhTo+qykhyBA1g=="; + private final SignedConfigEvent mEvent; private final PublicKey mDebugKey; + private final PublicKey mProdKey; - public SignatureVerifier() { - mDebugKey = createKey(DEBUG_KEY); + public SignatureVerifier(SignedConfigEvent event) { + mEvent = event; + mDebugKey = Build.IS_DEBUGGABLE ? createKey(DEBUG_KEY) : null; + mProdKey = createKey(PROD_KEY); } private static PublicKey createKey(String base64) { @@ -67,6 +75,14 @@ public class SignatureVerifier { } } + private boolean verifyWithPublicKey(PublicKey key, byte[] data, byte[] signature) + throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { + Signature verifier = Signature.getInstance("SHA256withECDSA"); + verifier.initVerify(key); + verifier.update(data); + return verifier.verify(signature); + } + /** * Verify a signature for signed config. * @@ -80,6 +96,7 @@ public class SignatureVerifier { try { signature = Base64.getDecoder().decode(base64Signature); } catch (IllegalArgumentException e) { + mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__BASE64_FAILURE_SIGNATURE; Slog.e(TAG, "Failed to base64 decode signature"); return false; } @@ -89,11 +106,9 @@ public class SignatureVerifier { if (Build.IS_DEBUGGABLE) { if (mDebugKey != null) { if (DBG) Slog.w(TAG, "Trying to verify signature using debug key"); - Signature verifier = Signature.getInstance("SHA256withECDSA"); - verifier.initVerify(mDebugKey); - verifier.update(data); - if (verifier.verify(signature)) { + if (verifyWithPublicKey(mDebugKey, data, signature)) { Slog.i(TAG, "Verified config using debug key"); + mEvent.verifiedWith = StatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__DEBUG; return true; } else { if (DBG) Slog.i(TAG, "Config verification failed using debug key"); @@ -102,8 +117,20 @@ public class SignatureVerifier { Slog.w(TAG, "Debuggable build, but have no debug key"); } } - // TODO verify production key. - Slog.w(TAG, "NO PRODUCTION KEY YET, FAILING VERIFICATION"); - return false; + if (mProdKey == null) { + Slog.e(TAG, "No prod key; construction failed?"); + mEvent.status = + StatsLog.SIGNED_CONFIG_REPORTED__STATUS__SIGNATURE_CHECK_FAILED_PROD_KEY_ABSENT; + return false; + } + if (verifyWithPublicKey(mProdKey, data, signature)) { + Slog.i(TAG, "Verified config using production key"); + mEvent.verifiedWith = StatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__PRODUCTION; + return true; + } else { + if (DBG) Slog.i(TAG, "Verification failed using production key"); + mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__SIGNATURE_CHECK_FAILED; + return false; + } } } diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigEvent.java b/services/core/java/com/android/server/signedconfig/SignedConfigEvent.java new file mode 100644 index 000000000000..2f2062c6f2ee --- /dev/null +++ b/services/core/java/com/android/server/signedconfig/SignedConfigEvent.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.signedconfig; + +import android.util.StatsLog; + +/** + * Helper class to allow a SignedConfigReported event to be built up in stages. + */ +public class SignedConfigEvent { + + public int type = StatsLog.SIGNED_CONFIG_REPORTED__TYPE__UNKNOWN_TYPE; + public int status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__UNKNOWN_STATUS; + public int version = 0; + public String fromPackage = null; + public int verifiedWith = StatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__NO_KEY; + + /** + * Write this event to statslog. + */ + public void send() { + StatsLog.write(StatsLog.SIGNED_CONFIG_REPORTED, + type, status, version, fromPackage, verifiedWith); + } + +} diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigService.java b/services/core/java/com/android/server/signedconfig/SignedConfigService.java index 6bcee1413fa8..dc39542dc29f 100644 --- a/services/core/java/com/android/server/signedconfig/SignedConfigService.java +++ b/services/core/java/com/android/server/signedconfig/SignedConfigService.java @@ -26,6 +26,7 @@ import android.content.pm.PackageManagerInternal; import android.net.Uri; import android.os.Bundle; import android.util.Slog; +import android.util.StatsLog; import com.android.server.LocalServices; @@ -82,21 +83,30 @@ public class SignedConfigService { } if (metaData.containsKey(KEY_GLOBAL_SETTINGS) && metaData.containsKey(KEY_GLOBAL_SETTINGS_SIGNATURE)) { - String config = metaData.getString(KEY_GLOBAL_SETTINGS); - String signature = metaData.getString(KEY_GLOBAL_SETTINGS_SIGNATURE); + SignedConfigEvent event = new SignedConfigEvent(); try { - // Base64 encoding is standard (not URL safe) encoding: RFC4648 - config = new String(Base64.getDecoder().decode(config), StandardCharsets.UTF_8); - } catch (IllegalArgumentException iae) { - Slog.e(TAG, "Failed to base64 decode global settings config from " + packageName); - return; + event.type = StatsLog.SIGNED_CONFIG_REPORTED__TYPE__GLOBAL_SETTINGS; + event.fromPackage = packageName; + String config = metaData.getString(KEY_GLOBAL_SETTINGS); + String signature = metaData.getString(KEY_GLOBAL_SETTINGS_SIGNATURE); + try { + // Base64 encoding is standard (not URL safe) encoding: RFC4648 + config = new String(Base64.getDecoder().decode(config), StandardCharsets.UTF_8); + } catch (IllegalArgumentException iae) { + Slog.e(TAG, "Failed to base64 decode global settings config from " + + packageName); + event.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__BASE64_FAILURE_CONFIG; + return; + } + if (DBG) { + Slog.d(TAG, "Got global settings config: " + config); + Slog.d(TAG, "Got global settings signature: " + signature); + } + new GlobalSettingsConfigApplicator(mContext, packageName, event).applyConfig( + config, signature); + } finally { + event.send(); } - if (DBG) { - Slog.d(TAG, "Got global settings config: " + config); - Slog.d(TAG, "Got global settings signature: " + signature); - } - new GlobalSettingsConfigApplicator(mContext, packageName).applyConfig( - config, signature); } else { if (DBG) Slog.d(TAG, "Package has no global settings config/signature."); } diff --git a/services/core/java/com/android/server/slice/SlicePermissionManager.java b/services/core/java/com/android/server/slice/SlicePermissionManager.java index 315d5e39c94b..1d1c28f5f9b7 100644 --- a/services/core/java/com/android/server/slice/SlicePermissionManager.java +++ b/services/core/java/com/android/server/slice/SlicePermissionManager.java @@ -175,18 +175,24 @@ public class SlicePermissionManager implements DirtyTracker { handlePersist(); } for (String file : new File(mSliceDir.getAbsolutePath()).list()) { - if (file.isEmpty()) continue; try (ParserHolder parser = getParser(file)) { - Persistable p; - while (parser.parser.getEventType() != XmlPullParser.START_TAG) { + Persistable p = null; + while (parser.parser.getEventType() != XmlPullParser.END_DOCUMENT) { + if (parser.parser.getEventType() == XmlPullParser.START_TAG) { + if (SliceClientPermissions.TAG_CLIENT.equals(parser.parser.getName())) { + p = SliceClientPermissions.createFrom(parser.parser, tracker); + } else { + p = SliceProviderPermissions.createFrom(parser.parser, tracker); + } + break; + } parser.parser.next(); } - if (SliceClientPermissions.TAG_CLIENT.equals(parser.parser.getName())) { - p = SliceClientPermissions.createFrom(parser.parser, tracker); + if (p != null) { + p.writeTo(out); } else { - p = SliceProviderPermissions.createFrom(parser.parser, tracker); + Slog.w(TAG, "Invalid or empty slice permissions file: " + file); } - p.writeTo(out); } } diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 30aa5280eac6..4e71a054fa80 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -43,6 +43,7 @@ import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.hardware.fingerprint.FingerprintManager; import android.net.ConnectivityManager; +import android.net.INetworkStatsService; import android.net.Network; import android.net.NetworkRequest; import android.net.NetworkStats; @@ -90,7 +91,6 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.procstats.IProcessStats; import com.android.internal.app.procstats.ProcessStats; -import com.android.internal.net.NetworkStatsFactory; import com.android.internal.os.BatterySipper; import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.BinderCallsStats.ExportedCallStat; @@ -210,6 +210,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { private final Context mContext; private final AlarmManager mAlarmManager; + private final INetworkStatsService mNetworkStatsService; @GuardedBy("sStatsdLock") private static IStatsManager sStatsd; private static final Object sStatsdLock = new Object(); @@ -257,6 +258,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { super(); mContext = context; mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); + mNetworkStatsService = INetworkStatsService.Stub.asInterface( + ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); mBaseDir.mkdirs(); mAppUpdateReceiver = new AppUpdateReceiver(); mUserUpdateReceiver = new BroadcastReceiver() { @@ -788,14 +791,14 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { if (ifaces.length == 0) { return; } - NetworkStatsFactory nsf = new NetworkStatsFactory(); + if (mNetworkStatsService == null) { + Slog.e(TAG, "NetworkStats Service is not available!"); + return; + } // Combine all the metrics per Uid into one record. - NetworkStats stats = - nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, - null) - .groupedByUid(); + NetworkStats stats = mNetworkStatsService.getDetailedUidStats(ifaces).groupedByUid(); addNetworkStats(tagId, pulledData, stats, false); - } catch (java.io.IOException e) { + } catch (RemoteException e) { Slog.e(TAG, "Pulling netstats for wifi bytes has error", e); } finally { Binder.restoreCallingIdentity(token); @@ -812,12 +815,14 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { if (ifaces.length == 0) { return; } - NetworkStatsFactory nsf = new NetworkStatsFactory(); + if (mNetworkStatsService == null) { + Slog.e(TAG, "NetworkStats Service is not available!"); + return; + } NetworkStats stats = rollupNetworkStatsByFGBG( - nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, - null)); + mNetworkStatsService.getDetailedUidStats(ifaces)); addNetworkStats(tagId, pulledData, stats, true); - } catch (java.io.IOException e) { + } catch (RemoteException e) { Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e); } finally { Binder.restoreCallingIdentity(token); @@ -834,14 +839,14 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { if (ifaces.length == 0) { return; } - NetworkStatsFactory nsf = new NetworkStatsFactory(); + if (mNetworkStatsService == null) { + Slog.e(TAG, "NetworkStats Service is not available!"); + return; + } // Combine all the metrics per Uid into one record. - NetworkStats stats = - nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, - null) - .groupedByUid(); + NetworkStats stats = mNetworkStatsService.getDetailedUidStats(ifaces).groupedByUid(); addNetworkStats(tagId, pulledData, stats, false); - } catch (java.io.IOException e) { + } catch (RemoteException e) { Slog.e(TAG, "Pulling netstats for mobile bytes has error", e); } finally { Binder.restoreCallingIdentity(token); @@ -874,12 +879,14 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { if (ifaces.length == 0) { return; } - NetworkStatsFactory nsf = new NetworkStatsFactory(); + if (mNetworkStatsService == null) { + Slog.e(TAG, "NetworkStats Service is not available!"); + return; + } NetworkStats stats = rollupNetworkStatsByFGBG( - nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, - null)); + mNetworkStatsService.getDetailedUidStats(ifaces)); addNetworkStats(tagId, pulledData, stats, true); - } catch (java.io.IOException e) { + } catch (RemoteException e) { Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e); } finally { Binder.restoreCallingIdentity(token); @@ -2058,6 +2065,17 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { mContext.unregisterReceiver(mShutdownEventReceiver); cancelAnomalyAlarm(); cancelPullingAlarm(); + + BinderCallsStatsService.Internal binderStats = + LocalServices.getService(BinderCallsStatsService.Internal.class); + if (binderStats != null) { + binderStats.reset(); + } + + LooperStats looperStats = LocalServices.getService(LooperStats.class); + if (looperStats != null) { + looperStats.reset(); + } } @Override diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index a207354f6c88..916baa08fcbc 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND; import static android.app.Activity.RESULT_CANCELED; import static android.app.ActivityManager.START_ABORTED; import static android.app.ActivityManager.START_CANCELED; @@ -50,6 +51,7 @@ import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; @@ -745,8 +747,9 @@ class ActivityStarter { // not sure if we need to create START_ABORTED_BACKGROUND so for now piggybacking // on START_ABORTED if (!abort) { - abort |= shouldAbortBackgroundActivityStart(callingUid, callingPackage, realCallingUid, - callerApp, originatingPendingIntent, allowBackgroundActivityStart); + abort |= shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage, + realCallingUid, callerApp, originatingPendingIntent, + allowBackgroundActivityStart, intent); } // Merge the two options bundles, while realCallerOptions takes precedence. @@ -893,9 +896,10 @@ class ActivityStarter { true /* doResume */, checkedOptions, inTask, outActivity); } - private boolean shouldAbortBackgroundActivityStart(int callingUid, final String callingPackage, - int realCallingUid, WindowProcessController callerApp, - PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) { + private boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid, + final String callingPackage, int realCallingUid, WindowProcessController callerApp, + PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart, + Intent intent) { if (mService.isBackgroundActivityStartsEnabled()) { return false; } @@ -908,19 +912,24 @@ class ActivityStarter { return false; } // don't abort if the callingUid is in the foreground or is a persistent system process - if (isUidForeground(callingUid) || isUidPersistentSystemProcess(callingUid)) { + final boolean isCallingUidForeground = isUidForeground(callingUid); + final boolean isCallingUidPersistentSystemProcess = isUidPersistentSystemProcess( + callingUid); + if (isCallingUidForeground || isCallingUidPersistentSystemProcess) { return false; } // take realCallingUid into consideration + final boolean isRealCallingUidForeground = isUidForeground(realCallingUid); + final boolean isRealCallingUidPersistentSystemProcess = isUidPersistentSystemProcess( + realCallingUid); if (realCallingUid != callingUid) { // don't abort if the realCallingUid is in the foreground and callingUid isn't - if (isUidForeground(realCallingUid)) { + if (isRealCallingUidForeground) { return false; } // if the realCallingUid is a persistent system process, abort if the IntentSender // wasn't whitelisted to start an activity - if (isUidPersistentSystemProcess(realCallingUid) && (originatingPendingIntent != null) - && allowBackgroundActivityStart) { + if (isRealCallingUidPersistentSystemProcess && allowBackgroundActivityStart) { return false; } } @@ -928,11 +937,28 @@ class ActivityStarter { if (callerApp != null && callerApp.areBackgroundActivityStartsAllowed()) { return false; } + // don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission + if (mService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid) + == PERMISSION_GRANTED) { + return false; + } // don't abort if the caller has the same uid as the recents component if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) { return false; } // anything that has fallen through will currently be aborted + Slog.w(TAG, "Blocking background activity start [callingPackage: " + callingPackage + + "; callingUid: " + callingUid + + "; isCallingUidForeground: " + isCallingUidForeground + + "; isCallingUidPersistentSystemProcess: " + isCallingUidPersistentSystemProcess + + "; realCallingUid: " + realCallingUid + + "; isRealCallingUidForeground: " + isRealCallingUidForeground + + "; isRealCallingUidPersistentSystemProcess: " + + isRealCallingUidPersistentSystemProcess + + "; originatingPendingIntent: " + originatingPendingIntent + + "; isBgStartWhitelisted: " + allowBackgroundActivityStart + + "; intent: " + intent + + "]"); // TODO: remove this toast after feature development is done mService.mUiHandler.post(() -> { Toast.makeText(mService.mContext, 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 0d3b6b2afd2c..b8db3f39eb62 100644 --- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java +++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java @@ -46,6 +46,7 @@ import android.platform.test.annotations.Presubmit; import android.util.SparseArray; import com.android.server.backup.testing.TransportData; +import com.android.server.testing.shadows.ShadowApplicationPackageManager; import com.android.server.testing.shadows.ShadowBinder; import org.junit.After; @@ -65,7 +66,7 @@ import java.io.PrintWriter; /** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */ @RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowBinder.class}) +@Config(shadows = {ShadowApplicationPackageManager.class, ShadowBinder.class}) @Presubmit public class BackupManagerServiceTest { private static final String TEST_PACKAGE = "package"; diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java index 496817613216..3b7fa3d6ed05 100644 --- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java +++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java @@ -42,6 +42,8 @@ import android.content.ComponentName; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; import android.os.Binder; import android.os.HandlerThread; import android.os.PowerManager; @@ -54,6 +56,7 @@ import com.android.server.backup.testing.TransportData; import com.android.server.backup.testing.TransportTestUtils.TransportMock; import com.android.server.backup.transport.TransportNotRegisteredException; import com.android.server.testing.shadows.ShadowAppBackupUtils; +import com.android.server.testing.shadows.ShadowApplicationPackageManager; import com.android.server.testing.shadows.ShadowBinder; import com.android.server.testing.shadows.ShadowKeyValueBackupJob; import com.android.server.testing.shadows.ShadowKeyValueBackupTask; @@ -80,7 +83,7 @@ import java.util.List; * UserBackupManagerService} that performs operations for its target user. */ @RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowAppBackupUtils.class}) +@Config(shadows = {ShadowAppBackupUtils.class, ShadowApplicationPackageManager.class}) @Presubmit public class UserBackupManagerServiceTest { private static final String TAG = "BMSTest"; @@ -137,6 +140,7 @@ public class UserBackupManagerServiceTest { public void tearDown() throws Exception { mBackupThread.quit(); ShadowAppBackupUtils.reset(); + ShadowApplicationPackageManager.reset(); } /** @@ -195,6 +199,7 @@ public class UserBackupManagerServiceTest { public void testIsAppEligibleForBackup_whenAppNotEligible() throws Exception { mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); setUpCurrentTransport(mTransportManager, mTransport); + registerPackages(PACKAGE_1); UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks(); boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1); @@ -210,6 +215,7 @@ public class UserBackupManagerServiceTest { public void testIsAppEligibleForBackup_whenAppEligible() throws Exception { mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); TransportMock transportMock = setUpCurrentTransport(mTransportManager, backupTransport()); + registerPackages(PACKAGE_1); ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1); UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks(); @@ -228,6 +234,7 @@ public class UserBackupManagerServiceTest { public void testIsAppEligibleForBackup_withoutPermission() throws Exception { mShadowContext.denyPermissions(android.Manifest.permission.BACKUP); setUpCurrentTransport(mTransportManager, mTransport); + registerPackages(PACKAGE_1); ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1); UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks(); @@ -245,6 +252,7 @@ public class UserBackupManagerServiceTest { public void testFilterAppsEligibleForBackup() throws Exception { mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); TransportMock transportMock = setUpCurrentTransport(mTransportManager, mTransport); + registerPackages(PACKAGE_1, PACKAGE_2); ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1); UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks(); @@ -264,6 +272,7 @@ public class UserBackupManagerServiceTest { @Test public void testFilterAppsEligibleForBackup_whenNoneIsEligible() throws Exception { mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); + registerPackages(PACKAGE_1, PACKAGE_2); UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks(); String[] filtered = @@ -281,6 +290,7 @@ public class UserBackupManagerServiceTest { public void testFilterAppsEligibleForBackup_withoutPermission() throws Exception { mShadowContext.denyPermissions(android.Manifest.permission.BACKUP); setUpCurrentTransport(mTransportManager, mTransport); + registerPackages(PACKAGE_1, PACKAGE_2); UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks(); expectThrows( @@ -749,7 +759,7 @@ public class UserBackupManagerServiceTest { private void setUpForRequestBackup(String... packages) throws Exception { mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); for (String packageName : packages) { - mShadowPackageManager.addPackage(packageName); + registerPackages(packageName); ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(packageName); } setUpCurrentTransport(mTransportManager, mTransport); @@ -864,7 +874,7 @@ public class UserBackupManagerServiceTest { @Test public void testRequestBackup_whenAppNotEligibleForBackup() throws Exception { mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); - mShadowPackageManager.addPackage(PACKAGE_1); + registerPackages(PACKAGE_1); setUpCurrentTransport(mTransportManager, mTransport); UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks(); backupManagerService.setEnabled(true); @@ -1127,6 +1137,22 @@ public class UserBackupManagerServiceTest { backupManagerService.setPowerManager(powerManagerMock); } + private PackageInfo getPackageInfo(String packageName) { + PackageInfo packageInfo = new PackageInfo(); + packageInfo.packageName = packageName; + packageInfo.applicationInfo = new ApplicationInfo(); + packageInfo.applicationInfo.packageName = packageName; + return packageInfo; + } + + private void registerPackages(String... packages) { + for (String packageName : packages) { + PackageInfo packageInfo = getPackageInfo(packageName); + mShadowPackageManager.installPackage(packageInfo); + ShadowApplicationPackageManager.addInstalledPackage(packageName, packageInfo); + } + } + /** * We can't mock the void method {@link #schedule(Context, long, BackupManagerConstants)} so we * extend {@link ShadowKeyValueBackupJob} and throw an exception at the end of the method. diff --git a/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java b/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java index b08ae6d5756e..bfb2b1479c74 100644 --- a/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java +++ b/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java @@ -29,6 +29,7 @@ import com.android.server.backup.TransportManager; import com.android.server.backup.UserBackupManagerService; import com.android.server.backup.testing.BackupManagerServiceTestUtils; import com.android.server.backup.testing.TestUtils; +import com.android.server.testing.shadows.ShadowApplicationPackageManager; import org.junit.Before; import org.junit.Test; @@ -37,6 +38,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; import java.io.File; @@ -45,6 +47,7 @@ import java.io.File; * UserBackupManagerService}. */ @RunWith(RobolectricTestRunner.class) +@Config(shadows = {ShadowApplicationPackageManager.class}) @Presubmit public class SetupObserverTest { private static final String TAG = "SetupObserverTest"; diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java index b923bb09a423..4811523b22f9 100644 --- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java +++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java @@ -71,7 +71,6 @@ import static java.util.stream.Collectors.toList; import android.annotation.Nullable; import android.app.Application; -import android.app.ApplicationPackageManager; import android.app.IBackupAgent; import android.app.backup.BackupAgent; import android.app.backup.BackupDataInput; @@ -116,6 +115,7 @@ import com.android.server.backup.testing.TransportData; import com.android.server.backup.testing.TransportTestUtils; import com.android.server.backup.testing.TransportTestUtils.TransportMock; import com.android.server.testing.shadows.FrameworkShadowLooper; +import com.android.server.testing.shadows.ShadowApplicationPackageManager; import com.android.server.testing.shadows.ShadowBackupDataInput; import com.android.server.testing.shadows.ShadowBackupDataOutput; import com.android.server.testing.shadows.ShadowEventLog; @@ -135,8 +135,6 @@ import org.mockito.stubbing.Answer; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; -import org.robolectric.annotation.Implements; -import org.robolectric.annotation.Resetter; import org.robolectric.shadows.ShadowLooper; import org.robolectric.shadows.ShadowPackageManager; import org.robolectric.shadows.ShadowQueuedWork; @@ -160,7 +158,7 @@ import java.util.stream.Stream; @Config( shadows = { FrameworkShadowLooper.class, - KeyValueBackupTaskTest.ShadowApplicationPackageManager.class, + ShadowApplicationPackageManager.class, ShadowBackupDataInput.class, ShadowBackupDataOutput.class, ShadowEventLog.class, @@ -2437,11 +2435,12 @@ public class KeyValueBackupTaskTest { private AgentMock setUpAgent(PackageData packageData) { try { + String packageName = packageData.packageName; mPackageManager.setApplicationEnabledSetting( - packageData.packageName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0); + packageName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0); PackageInfo packageInfo = getPackageInfo(packageData); mShadowPackageManager.installPackage(packageInfo); - ShadowApplicationPackageManager.setPackageInfo(packageInfo); + ShadowApplicationPackageManager.addInstalledPackage(packageName, packageInfo); mContext.sendBroadcast(getPackageAddedIntent(packageData)); // Run the backup looper because on the receiver we post MSG_SCHEDULE_BACKUP_PACKAGE mShadowBackupLooper.runToEndOfTasks(); @@ -2537,7 +2536,7 @@ public class KeyValueBackupTaskTest { private PackageManagerBackupAgent createPmAgent() { PackageManagerBackupAgent pmAgent = - new PackageManagerBackupAgent(mApplication.getPackageManager()); + new PackageManagerBackupAgent(mApplication.getPackageManager(), USER_ID); pmAgent.attach(mApplication); pmAgent.onCreate(); return pmAgent; @@ -2842,7 +2841,7 @@ public class KeyValueBackupTaskTest { ThrowingPackageManagerBackupAgent( PackageManager packageManager, RuntimeException exception) { - super(packageManager); + super(packageManager, USER_ID); mException = exception; } @@ -2854,29 +2853,4 @@ public class KeyValueBackupTaskTest { throw mException; } } - - /** - * Extends {@link org.robolectric.shadows.ShadowApplicationPackageManager} to return the correct - * package in user-specific invocations. - */ - @Implements(value = ApplicationPackageManager.class) - public static class ShadowApplicationPackageManager - extends org.robolectric.shadows.ShadowApplicationPackageManager { - private static PackageInfo sPackageInfo; - - static void setPackageInfo(PackageInfo packageInfo) { - sPackageInfo = packageInfo; - } - - @Override - protected PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId) { - return sPackageInfo; - } - - /** Clear {@link #sPackageInfo}. */ - @Resetter - public static void reset() { - sPackageInfo = null; - } - } } diff --git a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java index 4009876314e8..f17a9fe48c61 100644 --- a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java +++ b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java @@ -57,6 +57,7 @@ import com.android.server.backup.internal.BackupHandler; import com.android.server.backup.testing.TransportData; import com.android.server.backup.testing.TransportTestUtils; import com.android.server.backup.testing.TransportTestUtils.TransportMock; +import com.android.server.testing.shadows.ShadowApplicationPackageManager; import com.android.server.testing.shadows.ShadowEventLog; import com.android.server.testing.shadows.ShadowPerformUnifiedRestoreTask; @@ -77,7 +78,13 @@ import org.robolectric.shadows.ShadowPackageManager; import java.util.ArrayDeque; @RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowEventLog.class, ShadowPerformUnifiedRestoreTask.class, ShadowBinder.class}) +@Config( + shadows = { + ShadowApplicationPackageManager.class, + ShadowBinder.class, + ShadowEventLog.class, + ShadowPerformUnifiedRestoreTask.class + }) @Presubmit public class ActiveRestoreSessionTest { private static final String PACKAGE_1 = "com.example.package1"; @@ -140,6 +147,7 @@ public class ActiveRestoreSessionTest { @After public void tearDown() throws Exception { + ShadowApplicationPackageManager.reset(); ShadowPerformUnifiedRestoreTask.reset(); } @@ -561,7 +569,8 @@ public class ActiveRestoreSessionTest { packageInfo.packageName = packageName; packageInfo.applicationInfo = new ApplicationInfo(); packageInfo.applicationInfo.uid = uid; - mShadowPackageManager.addPackage(packageInfo); + mShadowPackageManager.installPackage(packageInfo); + ShadowApplicationPackageManager.addInstalledPackage(packageName, packageInfo); } private IRestoreSession createActiveRestoreSession( diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java b/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java index 5fffb149fd96..aefc871d2639 100644 --- a/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java +++ b/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java @@ -54,7 +54,10 @@ public class ShadowAppBackupUtils { @Implementation protected static boolean appIsRunningAndEligibleForBackupWithTransport( - @Nullable TransportClient transportClient, String packageName, PackageManager pm) { + @Nullable TransportClient transportClient, + String packageName, + PackageManager pm, + int userId) { return sAppsRunningAndEligibleForBackupWithTransport.contains(packageName); } diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java new file mode 100644 index 000000000000..dc322094add8 --- /dev/null +++ b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.testing.shadows; + +import static android.content.pm.PackageManager.NameNotFoundException; + +import android.app.ApplicationPackageManager; +import android.content.pm.PackageInfo; +import android.util.ArrayMap; + +import org.robolectric.annotation.Implements; +import org.robolectric.annotation.Resetter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Extends {@link org.robolectric.shadows.ShadowApplicationPackageManager} to return the correct + * package in user-specific invocations. + */ +@Implements(value = ApplicationPackageManager.class) +public class ShadowApplicationPackageManager + extends org.robolectric.shadows.ShadowApplicationPackageManager { + private static final Map<String, PackageInfo> sPackageInfos = new ArrayMap<>(); + private static final List<PackageInfo> sInstalledPackages = new ArrayList<>(); + + /** + * Registers the package {@code packageName} to be returned when invoking {@link + * ApplicationPackageManager#getPackageInfoAsUser(String, int, int)} and {@link + * ApplicationPackageManager#getInstalledPackagesAsUser(int, int)}. + */ + public static void addInstalledPackage(String packageName, PackageInfo packageInfo) { + sPackageInfos.put(packageName, packageInfo); + sInstalledPackages.add(packageInfo); + } + + @Override + protected PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId) + throws NameNotFoundException { + if (!sPackageInfos.containsKey(packageName)) { + throw new NameNotFoundException(packageName); + } + return sPackageInfos.get(packageName); + } + + @Override + protected List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) { + return sInstalledPackages; + } + + /** Clear package state. */ + @Resetter + public static void reset() { + sPackageInfos.clear(); + sInstalledPackages.clear(); + org.robolectric.shadows.ShadowApplicationPackageManager.reset(); + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java index c8e67820ce46..4a48468867df 100644 --- a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java @@ -85,6 +85,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -793,6 +794,7 @@ public class AppStateTrackerTest { } @Test + @FlakyTest(bugId = 114098433) public void testAllListeners() throws Exception { final AppStateTrackerTestable instance = newInstance(); callStart(instance); diff --git a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java index 8109b1c56207..1dfce51725f1 100644 --- a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java +++ b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java @@ -26,6 +26,7 @@ import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import com.android.server.appop.AppOpsService; +import com.android.server.wm.ActivityTaskManagerService; import org.junit.Before; import org.junit.Test; @@ -62,13 +63,15 @@ public class AppErrorDialogTest { return false; } }); + mService.mActivityTaskManager = new ActivityTaskManagerService(mContext); + mService.mActivityTaskManager.initialize(null, null, mContext.getMainLooper()); } @Test @UiThreadTest public void testCreateWorks() { AppErrorDialog.Data data = new AppErrorDialog.Data(); - data.proc = new ProcessRecord(null, mContext.getApplicationInfo(), "name", 12345); + data.proc = new ProcessRecord(mService, mContext.getApplicationInfo(), "name", 12345); data.result = new AppErrorResult(); AppErrorDialog dialog = new AppErrorDialog(mContext, mService, data); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index 517b5ade44b8..6d28ed19af4f 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -35,6 +35,7 @@ import static org.junit.Assert.fail; import android.annotation.NonNull; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.content.pm.PackageUserState; import android.content.pm.SuspendDialogInfo; @@ -226,8 +227,8 @@ public class PackageManagerSettingsTests { settingsUnderTest.mPackages.put(PACKAGE_NAME_3, createPackageSetting(PACKAGE_NAME_3)); // now read and verify settingsUnderTest.readPackageRestrictionsLPr(0); - final PackageUserState readPus1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1). - readUserState(0); + final PackageUserState readPus1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1) + .readUserState(0); assertThat(readPus1.suspended, is(true)); assertThat(readPus1.suspendingPackage, equalTo("suspendingPackage1")); assertThat(readPus1.dialogInfo, equalTo(dialogInfo1)); @@ -235,16 +236,16 @@ public class PackageManagerSettingsTests { assertThat(BaseBundle.kindofEquals(readPus1.suspendedLauncherExtras, launcherExtras1), is(true)); - final PackageUserState readPus2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2). - readUserState(0); + final PackageUserState readPus2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2) + .readUserState(0); assertThat(readPus2.suspended, is(true)); assertThat(readPus2.suspendingPackage, equalTo("suspendingPackage2")); assertThat(readPus2.dialogInfo, is(nullValue())); assertThat(readPus2.suspendedAppExtras, is(nullValue())); assertThat(readPus2.suspendedLauncherExtras, is(nullValue())); - final PackageUserState readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3). - readUserState(0); + final PackageUserState readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3) + .readUserState(0); assertThat(readPus3.suspended, is(false)); assertThat(readPus3.suspendingPackage, is(nullValue())); assertThat(readPus3.dialogInfo, is(nullValue())); @@ -254,11 +255,59 @@ public class PackageManagerSettingsTests { @Test public void testPackageRestrictionsSuspendedDefault() { - final PackageSetting defaultSetting = createPackageSetting(PACKAGE_NAME_1); + final PackageSetting defaultSetting = createPackageSetting(PACKAGE_NAME_1); assertThat(defaultSetting.getSuspended(0), is(false)); } @Test + public void testReadWritePackageRestrictions_distractionFlags() { + final Context context = InstrumentationRegistry.getTargetContext(); + final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, new Object()); + final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1); + final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2); + final PackageSetting ps3 = createPackageSetting(PACKAGE_NAME_3); + + final int distractionFlags1 = PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS; + ps1.setDistractionFlags(distractionFlags1, 0); + settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1); + + final int distractionFlags2 = PackageManager.RESTRICTION_HIDE_NOTIFICATIONS + | PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS; + ps2.setDistractionFlags(distractionFlags2, 0); + settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2); + + final int distractionFlags3 = PackageManager.RESTRICTION_NONE; + ps3.setDistractionFlags(distractionFlags3, 0); + settingsUnderTest.mPackages.put(PACKAGE_NAME_3, ps3); + + settingsUnderTest.writePackageRestrictionsLPr(0); + + settingsUnderTest.mPackages.clear(); + settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1)); + settingsUnderTest.mPackages.put(PACKAGE_NAME_2, createPackageSetting(PACKAGE_NAME_2)); + settingsUnderTest.mPackages.put(PACKAGE_NAME_3, createPackageSetting(PACKAGE_NAME_3)); + // now read and verify + settingsUnderTest.readPackageRestrictionsLPr(0); + final PackageUserState readPus1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1) + .readUserState(0); + assertThat(readPus1.distractionFlags, is(distractionFlags1)); + + final PackageUserState readPus2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2) + .readUserState(0); + assertThat(readPus2.distractionFlags, is(distractionFlags2)); + + final PackageUserState readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3) + .readUserState(0); + assertThat(readPus3.distractionFlags, is(distractionFlags3)); + } + + @Test + public void testPackageRestrictionsDistractionFlagsDefault() { + final PackageSetting defaultSetting = createPackageSetting(PACKAGE_NAME_1); + assertThat(defaultSetting.getDistractionFlags(0), is(PackageManager.RESTRICTION_NONE)); + } + + @Test public void testEnableDisable() { // Write the package files and make sure they're parsed properly the first time writeOldFiles(); @@ -692,6 +741,7 @@ public class PackageManagerSettingsTests { assertThat(userState.notLaunched, is(notLaunched)); assertThat(userState.stopped, is(stopped)); assertThat(userState.suspended, is(false)); + assertThat(userState.distractionFlags, is(0)); if (oldUserState != null) { assertThat(userState.equals(oldUserState), is(not(userStateChanged))); } diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java index f0ed612400ed..8eaf35f6432f 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java @@ -22,6 +22,7 @@ import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATIO import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; +import android.content.pm.PackageManager; import android.content.pm.PackageUserState; import android.content.pm.SuspendDialogInfo; import android.os.PersistableBundle; @@ -227,4 +228,19 @@ public class PackageUserStateTest { assertThat(testUserState1.equals(testUserState2), is(true)); } + @Test + public void testPackageUserState06() { + final PackageUserState userState1 = new PackageUserState(); + assertThat(userState1.distractionFlags, is(PackageManager.RESTRICTION_NONE)); + userState1.distractionFlags = PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS; + + final PackageUserState copyOfUserState1 = new PackageUserState(userState1); + assertThat(userState1.distractionFlags, is(copyOfUserState1.distractionFlags)); + assertThat(userState1.equals(copyOfUserState1), is(true)); + + final PackageUserState userState2 = new PackageUserState(userState1); + userState2.distractionFlags = PackageManager.RESTRICTION_HIDE_NOTIFICATIONS; + assertThat(userState1.equals(userState2), is(false)); + } + } diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 41d569106922..8171469d4da4 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -61,6 +61,7 @@ import android.util.ArraySet; import android.view.Display; import androidx.test.InstrumentationRegistry; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -416,6 +417,7 @@ public class AppStandbyControllerTests { } @Test + @FlakyTest(bugId = 119774928) public void testEnabledState() throws Exception { TestParoleListener paroleListener = new TestParoleListener(); mController.addListener(paroleListener); 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 83c1c7670338..94b21af65799 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -3511,9 +3511,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void testAppOverlay() throws Exception { - mBinderService.setAppOverlaysAllowed(PKG, mUid, false); - assertFalse(mBinderService.areAppOverlaysAllowedForPackage(PKG, mUid)); + public void testBubble() throws Exception { + mBinderService.setBubblesAllowed(PKG, mUid, false); + assertFalse(mBinderService.areBubblesAllowedForPackage(PKG, mUid)); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 0fcfea716e8f..24a1f8c19f12 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -809,7 +809,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { channel.setBypassDnd(true); channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); channel.setShowBadge(true); - channel.setAllowAppOverlay(false); + channel.setAllowBubbles(false); int lockMask = 0; for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) { lockMask |= NotificationChannel.LOCKABLE_FIELDS[i]; @@ -826,7 +826,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertFalse(savedChannel.canBypassDnd()); assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility()); assertEquals(channel.canShowBadge(), savedChannel.canShowBadge()); - assertEquals(channel.canOverlayApps(), savedChannel.canOverlayApps()); + assertEquals(channel.canBubble(), savedChannel.canBubble()); verify(mHandler, never()).requestSort(); } @@ -840,7 +840,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { channel.setBypassDnd(true); channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); channel.setShowBadge(true); - channel.setAllowAppOverlay(false); + channel.setAllowBubbles(false); int lockMask = 0; for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) { lockMask |= NotificationChannel.LOCKABLE_FIELDS[i]; @@ -857,7 +857,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertFalse(savedChannel.canBypassDnd()); assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility()); assertEquals(channel.canShowBadge(), savedChannel.canShowBadge()); - assertEquals(channel.canOverlayApps(), savedChannel.canOverlayApps()); + assertEquals(channel.canBubble(), savedChannel.canBubble()); } @Test @@ -969,16 +969,16 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testLockFields_appOverlay() { + public void testLockFields_allowBubble() { mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false); assertEquals(0, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel().getId(), false) .getUserLockedFields()); final NotificationChannel update = getChannel(); - update.setAllowAppOverlay(false); + update.setAllowBubbles(false); mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update, true); - assertEquals(NotificationChannel.USER_LOCKED_ALLOW_APP_OVERLAY, + assertEquals(NotificationChannel.USER_LOCKED_ALLOW_BUBBLE, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update.getId(), false) .getUserLockedFields()); } @@ -2161,30 +2161,30 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testAllowAppOverlay_defaults() throws Exception { - assertTrue(mHelper.areAppOverlaysAllowed(PKG_O, UID_O)); + public void testAllowBubbles_defaults() throws Exception { + assertTrue(mHelper.areBubblessAllowed(PKG_O, UID_O)); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); loadStreamXml(baos, false); - assertTrue(mHelper.areAppOverlaysAllowed(PKG_O, UID_O)); + assertTrue(mHelper.areBubblessAllowed(PKG_O, UID_O)); assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O)); } @Test - public void testAllowAppOverlay_xml() throws Exception { - mHelper.setAppOverlaysAllowed(PKG_O, UID_O, false); - assertFalse(mHelper.areAppOverlaysAllowed(PKG_O, UID_O)); - assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_APP_OVERLAY, + public void testAllowBubbles_xml() throws Exception { + mHelper.setBubblesAllowed(PKG_O, UID_O, false); + assertFalse(mHelper.areBubblessAllowed(PKG_O, UID_O)); + assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE, mHelper.getAppLockedFields(PKG_O, UID_O)); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); loadStreamXml(baos, false); - assertFalse(mHelper.areAppOverlaysAllowed(PKG_O, UID_O)); - assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_APP_OVERLAY, + assertFalse(mHelper.areBubblessAllowed(PKG_O, UID_O)); + assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE, mHelper.getAppLockedFields(PKG_O, UID_O)); } @@ -2290,14 +2290,14 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.lockChannelsForOEM(new String[] {PKG_O}); NotificationChannel update = new NotificationChannel("a", "a", IMPORTANCE_NONE); - update.setAllowAppOverlay(false); + update.setAllowBubbles(false); mHelper.updateNotificationChannel(PKG_O, UID_O, update, true); assertEquals(IMPORTANCE_HIGH, mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).getImportance()); assertEquals(false, - mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).canOverlayApps()); + mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).canBubble()); mHelper.updateNotificationChannel(PKG_O, UID_O, update, true); diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java index b315e514d6a6..efefee119e5f 100644 --- a/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java +++ b/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java @@ -16,6 +16,7 @@ package com.android.server.slice; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import android.content.ContentProvider; import android.content.ContentResolver; @@ -26,6 +27,7 @@ import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; +import android.util.Log; import android.util.Xml.Encoding; import com.android.server.UiServiceTestCase; @@ -46,10 +48,12 @@ import java.io.IOException; @RunWith(AndroidTestingRunner.class) @RunWithLooper public class SlicePermissionManagerTest extends UiServiceTestCase { + private static final String TAG = "SlicePerManTest"; @Test public void testGrant() { - File sliceDir = new File(mContext.getDataDir(), "system/slices"); + File sliceDir = new File(mContext.getCacheDir(), "testGrantSlices"); + Log.v(TAG, "testGrant: slice permissions stored in " + sliceDir.getAbsolutePath()); SlicePermissionManager permissions = new SlicePermissionManager(mContext, TestableLooper.get(this).getLooper(), sliceDir); Uri uri = new Builder().scheme(ContentResolver.SCHEME_CONTENT) @@ -59,11 +63,15 @@ public class SlicePermissionManagerTest extends UiServiceTestCase { permissions.grantSliceAccess("my.pkg", 0, "provider.pkg", 0, uri); assertTrue(permissions.hasPermission("my.pkg", 0, uri)); + + // Cleanup. + assertTrue(FileUtils.deleteContentsAndDir(sliceDir)); } @Test public void testBackup() throws XmlPullParserException, IOException { - File sliceDir = new File(mContext.getDataDir(), "system/slices"); + File sliceDir = new File(mContext.getCacheDir(), "testBackupSlices"); + Log.v(TAG, "testBackup: slice permissions stored in " + sliceDir.getAbsolutePath()); Uri uri = new Builder().scheme(ContentResolver.SCHEME_CONTENT) .authority("authority") .path("something").build(); @@ -90,7 +98,10 @@ public class SlicePermissionManagerTest extends UiServiceTestCase { TestableLooper.get(this).getLooper()); permissions.readRestore(parser); - assertTrue(permissions.hasFullAccess("com.android.mypkg", 10)); + if (!permissions.hasFullAccess("com.android.mypkg", 10)) { + fail("com.android.mypkg@10 did not have full access. backup file: " + + output.toString()); + } assertTrue(permissions.hasPermission("com.android.otherpkg", 0, ContentProvider.maybeAddUserId(uri, 1))); permissions.removePkg("com.android.lastpkg", 1); @@ -102,8 +113,9 @@ public class SlicePermissionManagerTest extends UiServiceTestCase { } @Test - public void testInvalid() throws Exception { - File sliceDir = new File(mContext.getCacheDir(), "slices-test"); + public void testInvalid() { + File sliceDir = new File(mContext.getCacheDir(), "testInvalidSlices"); + Log.v(TAG, "testInvalid: slice permissions stored in " + sliceDir.getAbsolutePath()); if (!sliceDir.exists()) { sliceDir.mkdir(); } @@ -118,7 +130,8 @@ public class SlicePermissionManagerTest extends UiServiceTestCase { @Override public void writeTo(XmlSerializer out) throws IOException { - throw new RuntimeException("this doesn't work"); + throw new RuntimeException("this RuntimeException inside junk.writeTo() " + + "should be caught and suppressed by surrounding code"); } }; diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java index 904d55e6d0f3..00c75480ba80 100644 --- a/services/usb/java/com/android/server/usb/UsbHostManager.java +++ b/services/usb/java/com/android/server/usb/UsbHostManager.java @@ -418,12 +418,10 @@ public class UsbHostManager { parser.getRawDescriptors()); // Stats collection - if (parser.hasAudioInterface()) { - StatsLog.write(StatsLog.USB_DEVICE_ATTACHED, newDevice.getVendorId(), - newDevice.getProductId(), parser.hasAudioInterface(), - parser.hasHIDInterface(), parser.hasStorageInterface(), - StatsLog.USB_DEVICE_ATTACHED__STATE__STATE_CONNECTED, 0); - } + StatsLog.write(StatsLog.USB_DEVICE_ATTACHED, newDevice.getVendorId(), + newDevice.getProductId(), parser.hasAudioInterface(), + parser.hasHIDInterface(), parser.hasStorageInterface(), + StatsLog.USB_DEVICE_ATTACHED__STATE__STATE_CONNECTED, 0); } } @@ -455,14 +453,12 @@ public class UsbHostManager { if (current != null) { UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress, current.mDescriptors); - if (parser.hasAudioInterface()) { // Stats collection - StatsLog.write(StatsLog.USB_DEVICE_ATTACHED, device.getVendorId(), - device.getProductId(), parser.hasAudioInterface(), - parser.hasHIDInterface(), parser.hasStorageInterface(), - StatsLog.USB_DEVICE_ATTACHED__STATE__STATE_DISCONNECTED, - System.currentTimeMillis() - current.mTimestamp); - } + StatsLog.write(StatsLog.USB_DEVICE_ATTACHED, device.getVendorId(), + device.getProductId(), parser.hasAudioInterface(), + parser.hasHIDInterface(), parser.hasStorageInterface(), + StatsLog.USB_DEVICE_ATTACHED__STATE__STATE_DISCONNECTED, + System.currentTimeMillis() - current.mTimestamp); } } else { Slog.d(TAG, "Removed device at " + deviceAddress + " was already gone"); diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp index 2fc3a0dd874e..7dc83c3db868 100644 --- a/startop/view_compiler/Android.bp +++ b/startop/view_compiler/Android.bp @@ -46,7 +46,7 @@ cc_defaults { }, } -cc_library_host_static { +cc_library_static { name: "libviewcompiler", defaults: ["viewcompiler_defaults"], srcs: [ @@ -58,9 +58,10 @@ cc_library_host_static { "util.cc", "layout_validation.cc", ], + host_supported: true, } -cc_binary_host { +cc_binary { name: "viewcompiler", defaults: ["viewcompiler_defaults"], srcs: [ @@ -70,6 +71,7 @@ cc_binary_host { "libgflags", "libviewcompiler", ], + host_supported: true } cc_test_host { diff --git a/startop/view_compiler/apk_layout_compiler.cc b/startop/view_compiler/apk_layout_compiler.cc index e95041ba34a4..09cdbd5fee58 100644 --- a/startop/view_compiler/apk_layout_compiler.cc +++ b/startop/view_compiler/apk_layout_compiler.cc @@ -79,9 +79,9 @@ bool CanCompileLayout(ResXMLParser* parser) { return visitor.can_compile(); } -void CompileApkLayouts(const std::string& filename, CompilationTarget target, - std::ostream& target_out) { - auto assets = android::ApkAssets::Load(filename); +namespace { +void CompileApkAssetsLayouts(const std::unique_ptr<const android::ApkAssets>& assets, + CompilationTarget target, std::ostream& target_out) { android::AssetManager2 resources; resources.SetApkAssets({assets.get()}); @@ -155,5 +155,20 @@ void CompileApkLayouts(const std::string& filename, CompilationTarget target, target_out.write(image.ptr<const char>(), image.size()); } } +} // namespace + +void CompileApkLayouts(const std::string& filename, CompilationTarget target, + std::ostream& target_out) { + auto assets = android::ApkAssets::Load(filename); + CompileApkAssetsLayouts(assets, target, target_out); +} + +void CompileApkLayoutsFd(android::base::unique_fd fd, CompilationTarget target, + std::ostream& target_out) { + constexpr const char* friendly_name{"viewcompiler assets"}; + auto assets = android::ApkAssets::LoadFromFd( + std::move(fd), friendly_name, /*system=*/false, /*force_shared_lib=*/false); + CompileApkAssetsLayouts(assets, target, target_out); +} } // namespace startop diff --git a/startop/view_compiler/apk_layout_compiler.h b/startop/view_compiler/apk_layout_compiler.h index c85ddd65ac1b..03bd545d9121 100644 --- a/startop/view_compiler/apk_layout_compiler.h +++ b/startop/view_compiler/apk_layout_compiler.h @@ -19,12 +19,16 @@ #include <string> +#include "android-base/unique_fd.h" + namespace startop { enum class CompilationTarget { kJavaLanguage, kDex }; void CompileApkLayouts(const std::string& filename, CompilationTarget target, std::ostream& target_out); +void CompileApkLayoutsFd(android::base::unique_fd fd, CompilationTarget target, + std::ostream& target_out); } // namespace startop diff --git a/startop/view_compiler/main.cc b/startop/view_compiler/main.cc index 871a421cee2d..11ecde27f5cd 100644 --- a/startop/view_compiler/main.cc +++ b/startop/view_compiler/main.cc @@ -49,6 +49,7 @@ constexpr char kStdoutFilename[]{"stdout"}; DEFINE_bool(apk, false, "Compile layouts in an APK"); DEFINE_bool(dex, false, "Generate a DEX file instead of Java"); +DEFINE_int32(infd, -1, "Read input from the given file descriptor"); DEFINE_string(out, kStdoutFilename, "Where to write the generated class"); DEFINE_string(package, "", "The package name for the generated class (required)"); @@ -95,7 +96,7 @@ void CompileLayout(XMLDocument* xml, Builder* builder) { int main(int argc, char** argv) { constexpr size_t kProgramName = 0; constexpr size_t kFileNameParam = 1; - constexpr size_t kNumRequiredArgs = 2; + constexpr size_t kNumRequiredArgs = 1; gflags::SetUsageMessage( "Compile XML layout files into equivalent Java language code\n" @@ -104,12 +105,11 @@ int main(int argc, char** argv) { gflags::ParseCommandLineFlags(&argc, &argv, /*remove_flags*/ true); gflags::CommandLineFlagInfo cmd = gflags::GetCommandLineFlagInfoOrDie("package"); - if (argc != kNumRequiredArgs || cmd.is_default) { + if (argc < kNumRequiredArgs || cmd.is_default) { gflags::ShowUsageWithFlags(argv[kProgramName]); return 1; } - const char* const filename = argv[kFileNameParam]; const bool is_stdout = FLAGS_out == kStdoutFilename; std::ofstream outfile; @@ -118,13 +118,23 @@ int main(int argc, char** argv) { } if (FLAGS_apk) { - startop::CompileApkLayouts( - filename, - FLAGS_dex ? startop::CompilationTarget::kDex : startop::CompilationTarget::kJavaLanguage, - is_stdout ? std::cout : outfile); + const startop::CompilationTarget target = + FLAGS_dex ? startop::CompilationTarget::kDex : startop::CompilationTarget::kJavaLanguage; + if (FLAGS_infd >= 0) { + startop::CompileApkLayoutsFd( + android::base::unique_fd{FLAGS_infd}, target, is_stdout ? std::cout : outfile); + } else { + if (argc < 2) { + gflags::ShowUsageWithFlags(argv[kProgramName]); + return 1; + } + const char* const filename = argv[kFileNameParam]; + startop::CompileApkLayouts(filename, target, is_stdout ? std::cout : outfile); + } return 0; } + const char* const filename = argv[kFileNameParam]; const string layout_name = startop::util::FindLayoutNameFromFilename(filename); XMLDocument xml; diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 84256037b379..05d5a13092f1 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -505,6 +505,14 @@ public abstract class Connection extends Conferenceable { "android.telecom.extra.ORIGINAL_CONNECTION_ID"; /** + * Boolean connection extra key set on the extras passed to + * {@link Connection#sendConnectionEvent} which indicates that audio is present + * on the RTT call when the extra value is true. + */ + public static final String EXTRA_IS_RTT_AUDIO_PRESENT = + "android.telecom.extra.IS_RTT_AUDIO_PRESENT"; + + /** * Connection event used to inform Telecom that it should play the on hold tone. This is used * to play a tone when the peer puts the current call on hold. Sent to Telecom via * {@link #sendConnectionEvent(String, Bundle)}. @@ -619,6 +627,13 @@ public abstract class Connection extends Conferenceable { */ public static final String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE"; + /** + * Connection event used to inform an {@link InCallService} that the RTT audio indication + * has changed. + */ + public static final String EVENT_RTT_AUDIO_INDICATION_CHANGED = + "android.telecom.event.RTT_AUDIO_INDICATION_CHANGED"; + // Flag controlling whether PII is emitted into the logs private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG); diff --git a/telephony/java/android/telephony/CallAttributes.aidl b/telephony/java/android/telephony/CallAttributes.aidl new file mode 100644 index 000000000000..69127df19714 --- /dev/null +++ b/telephony/java/android/telephony/CallAttributes.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +parcelable CallAttributes; + diff --git a/telephony/java/android/telephony/CallAttributes.java b/telephony/java/android/telephony/CallAttributes.java new file mode 100644 index 000000000000..2b99ce1d8252 --- /dev/null +++ b/telephony/java/android/telephony/CallAttributes.java @@ -0,0 +1,151 @@ +/* + * 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.telephony; + +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.TelephonyManager.NetworkType; + +import java.util.Objects; + +/** + * Contains information about a call's attributes as passed up from the HAL. If there are multiple + * ongoing calls, the CallAttributes will pertain to the call in the foreground. + * @hide + */ +@SystemApi +public class CallAttributes implements Parcelable { + private PreciseCallState mPreciseCallState; + @NetworkType + private int mNetworkType; // TelephonyManager.NETWORK_TYPE_* ints + private CallQuality mCallQuality; + + + public CallAttributes(PreciseCallState state, @NetworkType int networkType, + CallQuality callQuality) { + this.mPreciseCallState = state; + this.mNetworkType = networkType; + this.mCallQuality = callQuality; + } + + @Override + public String toString() { + return "mPreciseCallState=" + mPreciseCallState + " mNetworkType=" + mNetworkType + + " mCallQuality=" + mCallQuality; + } + + private CallAttributes(Parcel in) { + mPreciseCallState = (PreciseCallState) in.readValue(mPreciseCallState.getClass() + .getClassLoader()); + mNetworkType = in.readInt(); + mCallQuality = (CallQuality) in.readValue(mCallQuality.getClass().getClassLoader()); + } + + // getters + /** + * Returns the {@link PreciseCallState} of the call. + */ + public PreciseCallState getPreciseCallState() { + return mPreciseCallState; + } + + /** + * Returns the {@link TelephonyManager#NetworkType} of the call. + * + * @see TelephonyManager#NETWORK_TYPE_UNKNOWN + * @see TelephonyManager#NETWORK_TYPE_GPRS + * @see TelephonyManager#NETWORK_TYPE_EDGE + * @see TelephonyManager#NETWORK_TYPE_UMTS + * @see TelephonyManager#NETWORK_TYPE_CDMA + * @see TelephonyManager#NETWORK_TYPE_EVDO_0 + * @see TelephonyManager#NETWORK_TYPE_EVDO_A + * @see TelephonyManager#NETWORK_TYPE_1xRTT + * @see TelephonyManager#NETWORK_TYPE_HSDPA + * @see TelephonyManager#NETWORK_TYPE_HSUPA + * @see TelephonyManager#NETWORK_TYPE_HSPA + * @see TelephonyManager#NETWORK_TYPE_IDEN + * @see TelephonyManager#NETWORK_TYPE_EVDO_B + * @see TelephonyManager#NETWORK_TYPE_LTE + * @see TelephonyManager#NETWORK_TYPE_EHRPD + * @see TelephonyManager#NETWORK_TYPE_HSPAP + * @see TelephonyManager#NETWORK_TYPE_GSM + * @see TelephonyManager#NETWORK_TYPE_TD_SCDMA + * @see TelephonyManager#NETWORK_TYPE_IWLAN + * @see TelephonyManager#NETWORK_TYPE_LTE_CA + * @see TelephonyManager#NETWORK_TYPE_NR + */ + @NetworkType + public int getNetworkType() { + return mNetworkType; + } + + /** + * Returns the {#link CallQuality} of the call. + */ + public CallQuality getCallQuality() { + return mCallQuality; + } + + @Override + public int hashCode() { + return Objects.hash(mPreciseCallState, mNetworkType, mCallQuality); + } + + @Override + public boolean equals(Object o) { + if (o == null || !(o instanceof CallAttributes) || hashCode() != o.hashCode()) { + return false; + } + + if (this == o) { + return true; + } + + CallAttributes s = (CallAttributes) o; + + return (mPreciseCallState == s.mPreciseCallState + && mNetworkType == s.mNetworkType + && mCallQuality == s.mCallQuality); + } + + /** + * {@link Parcelable#describeContents} + */ + public @Parcelable.ContentsFlags int describeContents() { + return 0; + } + + /** + * {@link Parcelable#writeToParcel} + */ + public void writeToParcel(Parcel dest, @Parcelable.WriteFlags int flags) { + mPreciseCallState.writeToParcel(dest, flags); + dest.writeInt(mNetworkType); + mCallQuality.writeToParcel(dest, flags); + } + + public static final Parcelable.Creator<CallAttributes> CREATOR = new Parcelable.Creator() { + public CallAttributes createFromParcel(Parcel in) { + return new CallAttributes(in); + } + + public CallAttributes[] newArray(int size) { + return new CallAttributes[size]; + } + }; +} diff --git a/telephony/java/android/telephony/CallQuality.aidl b/telephony/java/android/telephony/CallQuality.aidl new file mode 100644 index 000000000000..f54355f48ad5 --- /dev/null +++ b/telephony/java/android/telephony/CallQuality.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +parcelable CallQuality; + diff --git a/telephony/java/android/telephony/CallQuality.java b/telephony/java/android/telephony/CallQuality.java new file mode 100644 index 000000000000..b27f6b44370f --- /dev/null +++ b/telephony/java/android/telephony/CallQuality.java @@ -0,0 +1,341 @@ +/* + * 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.telephony; + +import android.annotation.IntDef; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * Parcelable object to handle call quality. + * <p> + * Currently this supports IMS calls. + * <p> + * It provides the call quality level, duration, and additional information related to RTP packets, + * jitter and delay. + * <p> + * If there are multiple active calls, the CallQuality will pertain to the call in the foreground. + * + * @hide + */ +@SystemApi +public final class CallQuality implements Parcelable { + + // Constants representing the call quality level (see #CallQuality); + public static final int CALL_QUALITY_EXCELLENT = 0; + public static final int CALL_QUALITY_GOOD = 1; + public static final int CALL_QUALITY_FAIR = 2; + public static final int CALL_QUALITY_POOR = 3; + public static final int CALL_QUALITY_BAD = 4; + public static final int CALL_QUALITY_NOT_AVAILABLE = 5; + + /** + * Call quality + * @hide + */ + @IntDef(prefix = { "CALL_QUALITY_" }, value = { + CALL_QUALITY_EXCELLENT, + CALL_QUALITY_GOOD, + CALL_QUALITY_FAIR, + CALL_QUALITY_POOR, + CALL_QUALITY_BAD, + CALL_QUALITY_NOT_AVAILABLE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CallQualityLevel {} + + @CallQualityLevel + private int mDownlinkCallQualityLevel; + @CallQualityLevel + private int mUplinkCallQualityLevel; + private int mCallDuration; + private int mNumRtpPacketsTransmitted; + private int mNumRtpPacketsReceived; + private int mNumRtpPacketsTransmittedLost; + private int mNumRtpPacketsNotReceived; + private int mAverageRelativeJitter; + private int mMaxRelativeJitter; + private int mAverageRoundTripTime; + private int mCodecType; + + /** @hide **/ + public CallQuality(Parcel in) { + mDownlinkCallQualityLevel = in.readInt(); + mUplinkCallQualityLevel = in.readInt(); + mCallDuration = in.readInt(); + mNumRtpPacketsTransmitted = in.readInt(); + mNumRtpPacketsReceived = in.readInt(); + mNumRtpPacketsTransmittedLost = in.readInt(); + mNumRtpPacketsNotReceived = in.readInt(); + mAverageRelativeJitter = in.readInt(); + mMaxRelativeJitter = in.readInt(); + mAverageRoundTripTime = in.readInt(); + mCodecType = in.readInt(); + } + + /** + * Constructor. + * + * @param callQualityLevel the call quality level (see #CallQualityLevel) + * @param callDuration the call duration in milliseconds + * @param numRtpPacketsTransmitted RTP packets sent to network + * @param numRtpPacketsReceived RTP packets received from network + * @param numRtpPacketsTransmittedLost RTP packets which were lost in network and never + * transmitted + * @param numRtpPacketsNotReceived RTP packets which were lost in network and never recieved + * @param averageRelativeJitter average relative jitter in milliseconds + * @param maxRelativeJitter maximum relative jitter in milliseconds + * @param averageRoundTripTime average round trip delay in milliseconds + * @param codecType the codec type + */ + public CallQuality( + @CallQualityLevel int downlinkCallQualityLevel, + @CallQualityLevel int uplinkCallQualityLevel, + int callDuration, + int numRtpPacketsTransmitted, + int numRtpPacketsReceived, + int numRtpPacketsTransmittedLost, + int numRtpPacketsNotReceived, + int averageRelativeJitter, + int maxRelativeJitter, + int averageRoundTripTime, + int codecType) { + this.mDownlinkCallQualityLevel = downlinkCallQualityLevel; + this.mUplinkCallQualityLevel = uplinkCallQualityLevel; + this.mCallDuration = callDuration; + this.mNumRtpPacketsTransmitted = numRtpPacketsTransmitted; + this.mNumRtpPacketsReceived = numRtpPacketsReceived; + this.mNumRtpPacketsTransmittedLost = numRtpPacketsTransmittedLost; + this.mNumRtpPacketsNotReceived = numRtpPacketsNotReceived; + this.mAverageRelativeJitter = averageRelativeJitter; + this.mMaxRelativeJitter = maxRelativeJitter; + this.mAverageRoundTripTime = averageRoundTripTime; + this.mCodecType = codecType; + } + + // getters + /** + * Returns the downlink CallQualityLevel for a given ongoing call. + */ + @CallQualityLevel + public int getDownlinkCallQualityLevel() { + return mDownlinkCallQualityLevel; + } + + /** + * Returns the uplink CallQualityLevel for a given ongoing call. + */ + @CallQualityLevel + public int getUplinkCallQualityLevel() { + return mUplinkCallQualityLevel; + } + + /** + * Returns the duration of the call, in milliseconds. + */ + public int getCallDuration() { + return mCallDuration; + } + + /** + * Returns the total number of RTP packets transmitted by this device for a given ongoing call. + */ + public int getNumRtpPacketsTransmitted() { + return mNumRtpPacketsTransmitted; + } + + /** + * Returns the total number of RTP packets received by this device for a given ongoing call. + */ + public int getNumRtpPacketsReceived() { + return mNumRtpPacketsReceived; + } + + /** + * Returns the number of RTP packets which were sent by this device but were lost in the + * network before reaching the other party. + */ + public int getNumRtpPacketsTransmittedLost() { + return mNumRtpPacketsTransmittedLost; + } + + /** + * Returns the number of RTP packets which were sent by the other party but were lost in the + * network before reaching this device. + */ + public int getNumRtpPacketsNotReceived() { + return mNumRtpPacketsNotReceived; + } + + /** + * Returns the average relative jitter in milliseconds. Jitter represents the amount of variance + * in interarrival time of packets, for example, if two packets are sent 2 milliseconds apart + * but received 3 milliseconds apart, the relative jitter between those packets is 1 + * millisecond. + * + * <p>See RFC 3550 for more information on jitter calculations. + */ + public int getAverageRelativeJitter() { + return mAverageRelativeJitter; + } + + /** + * Returns the maximum relative jitter for a given ongoing call. Jitter represents the amount of + * variance in interarrival time of packets, for example, if two packets are sent 2 milliseconds + * apart but received 3 milliseconds apart, the relative jitter between those packets is 1 + * millisecond. + * + * <p>See RFC 3550 for more information on jitter calculations. + */ + public int getMaxRelativeJitter() { + return mMaxRelativeJitter; + } + + /** + * Returns the average round trip time in milliseconds. + */ + public int getAverageRoundTripTime() { + return mAverageRoundTripTime; + } + + /** + * Returns the codec type. This value corresponds to the AUDIO_QUALITY_* constants in + * {@link ImsStreamMediaProfile}. + * + * @see ImsStreamMediaProfile#AUDIO_QUALITY_NONE + * @see ImsStreamMediaProfile#AUDIO_QUALITY_AMR + * @see ImsStreamMediaProfile#AUDIO_QUALITY_AMR_WB + * @see ImsStreamMediaProfile#AUDIO_QUALITY_QCELP13K + * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC + * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC_B + * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC_WB + * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC_NW + * @see ImsStreamMediaProfile#AUDIO_QUALITY_GSM_EFR + * @see ImsStreamMediaProfile#AUDIO_QUALITY_GSM_FR + * @see ImsStreamMediaProfile#AUDIO_QUALITY_GSM_HR + * @see ImsStreamMediaProfile#AUDIO_QUALITY_G711U + * @see ImsStreamMediaProfile#AUDIO_QUALITY_G723 + * @see ImsStreamMediaProfile#AUDIO_QUALITY_G711A + * @see ImsStreamMediaProfile#AUDIO_QUALITY_G722 + * @see ImsStreamMediaProfile#AUDIO_QUALITY_G711AB + * @see ImsStreamMediaProfile#AUDIO_QUALITY_G729 + * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_NB + * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_WB + * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_SWB + * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_FB + */ + public int getCodecType() { + return mCodecType; + } + + // Parcelable things + @Override + public String toString() { + return "CallQuality: {downlinkCallQualityLevel=" + mDownlinkCallQualityLevel + + " uplinkCallQualityLevel=" + mUplinkCallQualityLevel + + " callDuration=" + mCallDuration + + " numRtpPacketsTransmitted=" + mNumRtpPacketsTransmitted + + " numRtpPacketsReceived=" + mNumRtpPacketsReceived + + " numRtpPacketsTransmittedLost=" + mNumRtpPacketsTransmittedLost + + " numRtpPacketsNotReceived=" + mNumRtpPacketsNotReceived + + " averageRelativeJitter=" + mAverageRelativeJitter + + " maxRelativeJitter=" + mMaxRelativeJitter + + " averageRoundTripTime=" + mAverageRoundTripTime + + " codecType=" + mCodecType + + "}"; + } + + @Override + public int hashCode() { + return Objects.hash( + mDownlinkCallQualityLevel, + mUplinkCallQualityLevel, + mCallDuration, + mNumRtpPacketsTransmitted, + mNumRtpPacketsReceived, + mNumRtpPacketsTransmittedLost, + mNumRtpPacketsNotReceived, + mAverageRelativeJitter, + mMaxRelativeJitter, + mAverageRoundTripTime, + mCodecType); + } + + @Override + public boolean equals(Object o) { + if (o == null || !(o instanceof CallQuality) || hashCode() != o.hashCode()) { + return false; + } + + if (this == o) { + return true; + } + + CallQuality s = (CallQuality) o; + + return (mDownlinkCallQualityLevel == s.mDownlinkCallQualityLevel + && mUplinkCallQualityLevel == s.mUplinkCallQualityLevel + && mCallDuration == s.mCallDuration + && mNumRtpPacketsTransmitted == s.mNumRtpPacketsTransmitted + && mNumRtpPacketsReceived == s.mNumRtpPacketsReceived + && mNumRtpPacketsTransmittedLost == s.mNumRtpPacketsTransmittedLost + && mNumRtpPacketsNotReceived == s.mNumRtpPacketsNotReceived + && mAverageRelativeJitter == s.mAverageRelativeJitter + && mMaxRelativeJitter == s.mMaxRelativeJitter + && mAverageRoundTripTime == s.mAverageRoundTripTime + && mCodecType == s.mCodecType); + } + + /** + * {@link Parcelable#describeContents} + */ + public @Parcelable.ContentsFlags int describeContents() { + return 0; + } + + /** + * {@link Parcelable#writeToParcel} + */ + public void writeToParcel(Parcel dest, @Parcelable.WriteFlags int flags) { + dest.writeInt(mDownlinkCallQualityLevel); + dest.writeInt(mUplinkCallQualityLevel); + dest.writeInt(mCallDuration); + dest.writeInt(mNumRtpPacketsTransmitted); + dest.writeInt(mNumRtpPacketsReceived); + dest.writeInt(mNumRtpPacketsTransmittedLost); + dest.writeInt(mNumRtpPacketsNotReceived); + dest.writeInt(mAverageRelativeJitter); + dest.writeInt(mMaxRelativeJitter); + dest.writeInt(mAverageRoundTripTime); + dest.writeInt(mCodecType); + } + + public static final Parcelable.Creator<CallQuality> CREATOR = new Parcelable.Creator() { + public CallQuality createFromParcel(Parcel in) { + return new CallQuality(in); + } + + public CallQuality[] newArray(int size) { + return new CallQuality[size]; + } + }; +} diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java index e27b38596324..c81670139eae 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/telephony/java/android/telephony/PhoneStateListener.java @@ -296,7 +296,7 @@ public class PhoneStateListener { /** * Listen for changes to preferred data subId. - * See {@link SubscriptionManager#setPreferredData(int)} + * See {@link SubscriptionManager#setPreferredDataSubId(int)} * for more details. * * @see #onPreferredDataSubIdChanged @@ -335,6 +335,18 @@ public class PhoneStateListener { @SystemApi public static final int LISTEN_CALL_DISCONNECT_CAUSES = 0x02000000; + /** + * Listen for changes to the call attributes of a currently active call. + * {@more} + * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE + * READ_PRECISE_PHONE_STATE} + * + * @see #onCallAttributesChanged + * @hide + */ + @SystemApi + public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 0x04000000; + /* * Subscription used to listen to the phone state changes * @hide @@ -683,6 +695,17 @@ public class PhoneStateListener { } /** + * Callback invoked when the call attributes changes. Requires + * the READ_PRIVILEGED_PHONE_STATE permission. + * @param callAttributes the call attributes + * @hide + */ + @SystemApi + public void onCallAttributesChanged(CallAttributes callAttributes) { + // default implementation empty + } + + /** * Callback invoked when modem radio power state changes. Requires * the READ_PRIVILEGED_PHONE_STATE permission. * @param state the modem radio power state @@ -941,6 +964,14 @@ public class PhoneStateListener { () -> mExecutor.execute(() -> psl.onRadioPowerStateChanged(state))); } + public void onCallAttributesChanged(CallAttributes callAttributes) { + PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); + if (psl == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> psl.onCallAttributesChanged(callAttributes))); + } + public void onPreferredDataSubIdChanged(int subId) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 34f7abd79a70..9fa4c3ce899f 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -2453,10 +2453,39 @@ public class SubscriptionManager { * */ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public void setPreferredData(int subId) { - if (VDBG) logd("[setPreferredData]+ subId:" + subId); - setSubscriptionPropertyHelper(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, - "setPreferredData", (iSub)-> iSub.setPreferredData(subId)); + public void setPreferredDataSubscriptionId(int subId) { + if (VDBG) logd("[setPreferredDataSubscriptionId]+ subId:" + subId); + setSubscriptionPropertyHelper(DEFAULT_SUBSCRIPTION_ID, "setPreferredDataSubscriptionId", + (iSub)-> iSub.setPreferredDataSubscriptionId(subId)); + } + + /** + * Get which subscription is preferred for cellular data. + * It's also usually the subscription we set up internet connection on. + * + * PreferredData overwrites user setting of default data subscription. And it's used + * by AlternativeNetworkService or carrier apps to switch primary and CBRS + * subscription dynamically in multi-SIM devices. + * + * @return preferred subscription id for cellular data. {@link DEFAULT_SUBSCRIPTION_ID} if + * there's no prefered subscription. + * + * @hide + * + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public int getPreferredDataSubscriptionId() { + int preferredSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; + try { + ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + if (iSub != null) { + preferredSubId = iSub.getPreferredDataSubscriptionId(); + } + } catch (RemoteException ex) { + // ignore it + } + + return preferredSubId; } /** diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 7f7d5e7faae5..babeb7b1b61c 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -9910,10 +9910,11 @@ public class TelephonyManager { try { IOns iOpportunisticNetworkService = getIOns(); if (iOpportunisticNetworkService != null) { - return iOpportunisticNetworkService.setPreferredData(subId, pkgForDebug); + return iOpportunisticNetworkService + .setPreferredDataSubscriptionId(subId, pkgForDebug); } } catch (RemoteException ex) { - Rlog.e(TAG, "setPreferredData RemoteException", ex); + Rlog.e(TAG, "setPreferredDataSubscriptionId RemoteException", ex); } return false; } @@ -9934,10 +9935,10 @@ public class TelephonyManager { try { IOns iOpportunisticNetworkService = getIOns(); if (iOpportunisticNetworkService != null) { - subId = iOpportunisticNetworkService.getPreferredData(pkgForDebug); + subId = iOpportunisticNetworkService.getPreferredDataSubscriptionId(pkgForDebug); } } catch (RemoteException ex) { - Rlog.e(TAG, "getPreferredData RemoteException", ex); + Rlog.e(TAG, "getPreferredDataSubscriptionId RemoteException", ex); } return subId; } diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java index cc9befedcdf7..42a788d0cc8e 100644 --- a/telephony/java/android/telephony/euicc/EuiccManager.java +++ b/telephony/java/android/telephony/euicc/EuiccManager.java @@ -30,6 +30,7 @@ import android.content.pm.PackageManager; import android.os.Bundle; import android.os.RemoteException; import android.os.ServiceManager; +import android.telephony.TelephonyManager; import com.android.internal.telephony.euicc.IEuiccController; @@ -40,7 +41,11 @@ import java.lang.annotation.RetentionPolicy; * EuiccManager is the application interface to eUICCs, or eSIMs/embedded SIMs. * * <p>You do not instantiate this class directly; instead, you retrieve an instance through - * {@link Context#getSystemService(String)} and {@link Context#EUICC_SERVICE}. + * {@link Context#getSystemService(String)} and {@link Context#EUICC_SERVICE}. This instance will be + * created using the default eUICC. + * + * <p>On a device with multiple eUICCs, you may want to create multiple EuiccManagers. To do this + * you can call {@link #createForCardId}. * * <p>See {@link #isEnabled} before attempting to use these APIs. */ @@ -318,10 +323,29 @@ public class EuiccManager { public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5; private final Context mContext; + private final int mCardId; /** @hide */ public EuiccManager(Context context) { mContext = context; + TelephonyManager tm = (TelephonyManager) + context.getSystemService(Context.TELEPHONY_SERVICE); + mCardId = tm.getCardIdForDefaultEuicc(); + } + + /** @hide */ + private EuiccManager(Context context, int cardId) { + mContext = context; + mCardId = cardId; + } + + /** + * Create a new EuiccManager object pinned to the given card ID. + * + * @return an EuiccManager that uses the given card ID for all calls. + */ + public EuiccManager createForCardId(int cardId) { + return new EuiccManager(mContext, cardId); } /** @@ -344,7 +368,8 @@ public class EuiccManager { * Returns the EID identifying the eUICC hardware. * * <p>Requires that the calling app has carrier privileges on the active subscription on the - * eUICC. + * current eUICC. A calling app with carrier privileges for one eUICC may not necessarily have + * access to the EID of another eUICC. * * @return the EID. May be null if {@link #isEnabled()} is false or the eUICC is not ready. */ @@ -354,7 +379,7 @@ public class EuiccManager { return null; } try { - return getIEuiccController().getEid(); + return getIEuiccController().getEid(mCardId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -377,7 +402,7 @@ public class EuiccManager { return EUICC_OTA_STATUS_UNAVAILABLE; } try { - return getIEuiccController().getOtaStatus(); + return getIEuiccController().getOtaStatus(mCardId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -387,10 +412,10 @@ public class EuiccManager { * Attempt to download the given {@link DownloadableSubscription}. * * <p>Requires the {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission, - * or the calling app must be authorized to manage both the currently-active subscription and - * the subscription to be downloaded according to the subscription metadata. Without the former, - * an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be returned in the callback - * intent to prompt the user to accept the download. + * or the calling app must be authorized to manage both the currently-active subscription on the + * current eUICC and the subscription to be downloaded according to the subscription metadata. + * Without the former, an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be + * returned in the callback intent to prompt the user to accept the download. * * @param subscription the subscription to download. * @param switchAfterDownload if true, the profile will be activated upon successful download. @@ -404,7 +429,7 @@ public class EuiccManager { return; } try { - getIEuiccController().downloadSubscription(subscription, switchAfterDownload, + getIEuiccController().downloadSubscription(mCardId, subscription, switchAfterDownload, mContext.getOpPackageName(), null /* resolvedBundle */, callbackIntent); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -471,7 +496,7 @@ public class EuiccManager { return; } try { - getIEuiccController().continueOperation(resolutionIntent, resolutionExtras); + getIEuiccController().continueOperation(mCardId, resolutionIntent, resolutionExtras); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -503,8 +528,8 @@ public class EuiccManager { return; } try { - getIEuiccController().getDownloadableSubscriptionMetadata( - subscription, mContext.getOpPackageName(), callbackIntent); + getIEuiccController().getDownloadableSubscriptionMetadata(mCardId, subscription, + mContext.getOpPackageName(), callbackIntent); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -533,7 +558,7 @@ public class EuiccManager { return; } try { - getIEuiccController().getDefaultDownloadableSubscriptionList( + getIEuiccController().getDefaultDownloadableSubscriptionList(mCardId, mContext.getOpPackageName(), callbackIntent); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -552,7 +577,7 @@ public class EuiccManager { return null; } try { - return getIEuiccController().getEuiccInfo(); + return getIEuiccController().getEuiccInfo(mCardId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -578,7 +603,7 @@ public class EuiccManager { return; } try { - getIEuiccController().deleteSubscription( + getIEuiccController().deleteSubscription(mCardId, subscriptionId, mContext.getOpPackageName(), callbackIntent); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -606,7 +631,7 @@ public class EuiccManager { return; } try { - getIEuiccController().switchToSubscription( + getIEuiccController().switchToSubscription(mCardId, subscriptionId, mContext.getOpPackageName(), callbackIntent); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -632,7 +657,7 @@ public class EuiccManager { return; } try { - getIEuiccController().updateSubscriptionNickname( + getIEuiccController().updateSubscriptionNickname(mCardId, subscriptionId, nickname, mContext.getOpPackageName(), callbackIntent); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -656,7 +681,7 @@ public class EuiccManager { return; } try { - getIEuiccController().eraseSubscriptions(callbackIntent); + getIEuiccController().eraseSubscriptions(mCardId, callbackIntent); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -686,7 +711,7 @@ public class EuiccManager { return; } try { - getIEuiccController().retainSubscriptionsForFactoryReset(callbackIntent); + getIEuiccController().retainSubscriptionsForFactoryReset(mCardId, callbackIntent); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java index df903cc270a2..397d5d9b4eb5 100644 --- a/telephony/java/android/telephony/ims/ImsCallSession.java +++ b/telephony/java/android/telephony/ims/ImsCallSession.java @@ -443,6 +443,13 @@ public class ImsCallSession { public void callSessionRttMessageReceived(String rttMessage) { // no-op } + + /** + * While in call, there has been a change in RTT audio indicator. + */ + public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) { + // no-op + } } private final IImsCallSession miSession; @@ -1397,6 +1404,16 @@ public class ImsCallSession { mListener.callSessionRttMessageReceived(rttMessage); } } + + /** + * While in call, there has been a change in RTT audio indicator. + */ + @Override + public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) { + if (mListener != null) { + mListener.callSessionRttAudioIndicatorChanged(profile); + } + } } /** diff --git a/telephony/java/android/telephony/ims/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/ImsCallSessionListener.java index a7f124a5b791..a4696a3f93aa 100644 --- a/telephony/java/android/telephony/ims/ImsCallSessionListener.java +++ b/telephony/java/android/telephony/ims/ImsCallSessionListener.java @@ -599,5 +599,18 @@ public class ImsCallSessionListener { throw new RuntimeException(e); } } + + /** + * While in call, there has been a change in RTT audio indicator. + * + * @param profile updated ImsStreamMediaProfile + */ + public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) { + try { + mListener.callSessionRttAudioIndicatorChanged(profile); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } } diff --git a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java index 52d72b5847b5..837ef54a2f24 100644 --- a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java +++ b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java @@ -97,6 +97,9 @@ public final class ImsStreamMediaProfile implements Parcelable { // Rtt related information /** @hide */ public int mRttMode; + // RTT Audio Speech Indicator + /** @hide */ + public boolean mHasRttAudioSpeech = false; /** @hide */ public ImsStreamMediaProfile(Parcel in) { @@ -197,7 +200,8 @@ public final class ImsStreamMediaProfile implements Parcelable { ", audioDirection=" + mAudioDirection + ", videoQuality=" + mVideoQuality + ", videoDirection=" + mVideoDirection + - ", rttMode=" + mRttMode + " }"; + ", rttMode=" + mRttMode + + ", hasRttAudioSpeech=" + mHasRttAudioSpeech + " }"; } @Override @@ -212,6 +216,7 @@ public final class ImsStreamMediaProfile implements Parcelable { out.writeInt(mVideoQuality); out.writeInt(mVideoDirection); out.writeInt(mRttMode); + out.writeBoolean(mHasRttAudioSpeech); } private void readFromParcel(Parcel in) { @@ -220,6 +225,7 @@ public final class ImsStreamMediaProfile implements Parcelable { mVideoQuality = in.readInt(); mVideoDirection = in.readInt(); mRttMode = in.readInt(); + mHasRttAudioSpeech = in.readBoolean(); } public static final Creator<ImsStreamMediaProfile> CREATOR = @@ -250,6 +256,10 @@ public final class ImsStreamMediaProfile implements Parcelable { mRttMode = rttMode; } + public void setRttAudioSpeech(boolean audioOn) { + mHasRttAudioSpeech = audioOn; + } + public int getAudioQuality() { return mAudioQuality; } @@ -269,4 +279,8 @@ public final class ImsStreamMediaProfile implements Parcelable { public int getRttMode() { return mRttMode; } + + public boolean getRttAudioSpeech() { + return mHasRttAudioSpeech; + } } diff --git a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl index f25b4b148605..d0b31e16a4f0 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl @@ -138,4 +138,10 @@ oneway interface IImsCallSessionListener { * @param rttMessage Received RTT message */ void callSessionRttMessageReceived(in String rttMessage); + + /* + * While in call, there has been a change in RTT audio indicator. + * @param profile updated ImsStreamMediaProfile + */ + void callSessionRttAudioIndicatorChanged(in ImsStreamMediaProfile profile); } diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java index 23de2fd3c32c..bc58e46806c2 100644 --- a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java +++ b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java @@ -591,5 +591,11 @@ public class ImsCallSessionImplBase extends IImsCallSession.Stub { public void callSessionRttMessageReceived(String rttMessage) throws RemoteException { mNewListener.callSessionRttMessageReceived(rttMessage); } + + @Override + public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) + throws RemoteException { + mNewListener.callSessionRttAudioIndicatorChanged(profile); + } } } diff --git a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl index a8e8b7dd03aa..bbb27af1ba36 100644 --- a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl +++ b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl @@ -152,4 +152,10 @@ oneway interface IImsCallSessionListener { * @param rttMessage Received RTT message */ void callSessionRttMessageReceived(in String rttMessage); + + /* + * While in call, there has been a change in RTT audio indicator. + * @param profile updated ImsStreamMediaProfile + */ + void callSessionRttAudioIndicatorChanged(in ImsStreamMediaProfile profile); } diff --git a/telephony/java/com/android/internal/telephony/IOns.aidl b/telephony/java/com/android/internal/telephony/IOns.aidl index d6779f1fb334..0e3d12b7f838 100755 --- a/telephony/java/com/android/internal/telephony/IOns.aidl +++ b/telephony/java/com/android/internal/telephony/IOns.aidl @@ -66,7 +66,7 @@ interface IOns { * @return true if request is accepted, else false. * */ - boolean setPreferredData(int subId, String callingPackage); + boolean setPreferredDataSubscriptionId(int subId, String callingPackage); /** * Get preferred opportunistic data subscription Id @@ -78,7 +78,7 @@ interface IOns { * subscription id * */ - int getPreferredData(String callingPackage); + int getPreferredDataSubscriptionId(String callingPackage); /** * Update availability of a list of networks in the current location. diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl index 00cf9c3577ec..3dbebe832fac 100644 --- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -17,14 +17,15 @@ package com.android.internal.telephony; import android.os.Bundle; -import android.telephony.ServiceState; -import android.telephony.SignalStrength; +import android.telephony.CallAttributes; import android.telephony.CellInfo; import android.telephony.DataConnectionRealTimeInfo; import android.telephony.PhoneCapability; import android.telephony.PhysicalChannelConfig; import android.telephony.PreciseCallState; import android.telephony.PreciseDataConnectionState; +import android.telephony.ServiceState; +import android.telephony.SignalStrength; import android.telephony.emergency.EmergencyNumber; oneway interface IPhoneStateListener { @@ -54,6 +55,7 @@ oneway interface IPhoneStateListener { void onPhoneCapabilityChanged(in PhoneCapability capability); void onPreferredDataSubIdChanged(in int subId); void onRadioPowerStateChanged(in int state); + void onCallAttributesChanged(in CallAttributes callAttributes); void onEmergencyNumberListChanged(in Map emergencyNumberList); void onCallDisconnectCauseChanged(in int disconnectCause, in int preciseDisconnectCause); } diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl index d169b7d04f5c..577ddbda50fa 100755 --- a/telephony/java/com/android/internal/telephony/ISub.aidl +++ b/telephony/java/com/android/internal/telephony/ISub.aidl @@ -200,7 +200,15 @@ interface ISub { * @hide * */ - int setPreferredData(int subId); + int setPreferredDataSubscriptionId(int subId); + + /** + * Get which subscription is preferred for cellular data. + * + * @hide + * + */ + int getPreferredDataSubscriptionId(); /** * Get User downloaded Profiles. diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl index dd40d560250d..14a36c8c840d 100644 --- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl +++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl @@ -24,22 +24,25 @@ import android.telephony.euicc.EuiccInfo; /** @hide */ interface IEuiccController { - oneway void continueOperation(in Intent resolutionIntent, in Bundle resolutionExtras); - oneway void getDownloadableSubscriptionMetadata(in DownloadableSubscription subscription, + oneway void continueOperation(int cardId, in Intent resolutionIntent, + in Bundle resolutionExtras); + oneway void getDownloadableSubscriptionMetadata(int cardId, + in DownloadableSubscription subscription, String callingPackage, in PendingIntent callbackIntent); - oneway void getDefaultDownloadableSubscriptionList( + oneway void getDefaultDownloadableSubscriptionList(int cardId, String callingPackage, in PendingIntent callbackIntent); - String getEid(); - int getOtaStatus(); - oneway void downloadSubscription(in DownloadableSubscription subscription, - boolean switchAfterDownload, String callingPackage, in Bundle resolvedBundle, in PendingIntent callbackIntent); - EuiccInfo getEuiccInfo(); - oneway void deleteSubscription(int subscriptionId, String callingPackage, + String getEid(int cardId); + int getOtaStatus(int cardId); + oneway void downloadSubscription(int cardId, in DownloadableSubscription subscription, + boolean switchAfterDownload, String callingPackage, in Bundle resolvedBundle, in PendingIntent callbackIntent); - oneway void switchToSubscription(int subscriptionId, String callingPackage, + EuiccInfo getEuiccInfo(int cardId); + oneway void deleteSubscription(int cardId, int subscriptionId, String callingPackage, in PendingIntent callbackIntent); - oneway void updateSubscriptionNickname(int subscriptionId, String nickname, + oneway void switchToSubscription(int cardId, int subscriptionId, String callingPackage, + in PendingIntent callbackIntent); + oneway void updateSubscriptionNickname(int cardId, int subscriptionId, String nickname, String callingPackage, in PendingIntent callbackIntent); - oneway void eraseSubscriptions(in PendingIntent callbackIntent); - oneway void retainSubscriptionsForFactoryReset(in PendingIntent callbackIntent); -}
\ No newline at end of file + oneway void eraseSubscriptions(int cardId, in PendingIntent callbackIntent); + oneway void retainSubscriptionsForFactoryReset(int cardId, in PendingIntent callbackIntent); +} diff --git a/tests/PackageWatchdog/Android.mk b/tests/PackageWatchdog/Android.mk index b53f27d28740..1c1c2a426d15 100644 --- a/tests/PackageWatchdog/Android.mk +++ b/tests/PackageWatchdog/Android.mk @@ -23,7 +23,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ junit \ frameworks-base-testutils \ android-support-test \ - services + services.core LOCAL_JAVA_LIBRARIES := \ android.test.runner diff --git a/tests/net/java/android/net/LinkPropertiesTest.java b/tests/net/java/android/net/LinkPropertiesTest.java index f82b380f5d43..932fee0c1956 100644 --- a/tests/net/java/android/net/LinkPropertiesTest.java +++ b/tests/net/java/android/net/LinkPropertiesTest.java @@ -18,6 +18,7 @@ package android.net; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -33,6 +34,9 @@ import android.support.test.runner.AndroidJUnit4; import android.system.OsConstants; import android.util.ArraySet; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; @@ -41,9 +45,6 @@ import java.util.Collections; import java.util.List; import java.util.Set; -import org.junit.Test; -import org.junit.runner.RunWith; - @RunWith(AndroidJUnit4.class) @SmallTest public class LinkPropertiesTest { @@ -504,6 +505,40 @@ public class LinkPropertiesTest { } @Test + public void testNat64Prefix() throws Exception { + LinkProperties lp = new LinkProperties(); + lp.addLinkAddress(LINKADDRV4); + lp.addLinkAddress(LINKADDRV6); + + assertNull(lp.getNat64Prefix()); + + IpPrefix p = new IpPrefix("64:ff9b::/96"); + lp.setNat64Prefix(p); + assertEquals(p, lp.getNat64Prefix()); + + p = new IpPrefix("2001:db8:a:b:1:2:3::/96"); + lp.setNat64Prefix(p); + assertEquals(p, lp.getNat64Prefix()); + + p = new IpPrefix("2001:db8:a:b:1:2::/80"); + try { + lp.setNat64Prefix(p); + } catch (IllegalArgumentException expected) { + } + + p = new IpPrefix("64:ff9b::/64"); + try { + lp.setNat64Prefix(p); + } catch (IllegalArgumentException expected) { + } + + assertEquals(new IpPrefix("2001:db8:a:b:1:2:3::/96"), lp.getNat64Prefix()); + + lp.setNat64Prefix(null); + assertNull(lp.getNat64Prefix()); + } + + @Test public void testIsProvisioned() { LinkProperties lp4 = new LinkProperties(); assertFalse("v4only:empty", lp4.isProvisioned()); @@ -815,7 +850,7 @@ public class LinkPropertiesTest { } @Test - public void testLinkPropertiesParcelable() { + public void testLinkPropertiesParcelable() throws Exception { LinkProperties source = new LinkProperties(); source.setInterfaceName(NAME); // set 2 link addresses @@ -833,6 +868,8 @@ public class LinkPropertiesTest { source.setMtu(MTU); + source.setNat64Prefix(new IpPrefix("2001:db8:1:2:64:64::/96")); + Parcel p = Parcel.obtain(); source.writeToParcel(p, /* flags */ 0); p.setDataPosition(0); diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp index 0580df60f32a..7783e108f674 100644 --- a/tools/aapt2/Android.bp +++ b/tools/aapt2/Android.bp @@ -115,6 +115,7 @@ cc_library_host_static { "optimize/MultiApkGenerator.cpp", "optimize/ResourceDeduper.cpp", "optimize/ResourceFilter.cpp", + "optimize/ResourcePathShortener.cpp", "optimize/VersionCollapser.cpp", "process/SymbolTable.cpp", "split/TableSplitter.cpp", diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp index b353ff00a23f..45719ef474cd 100644 --- a/tools/aapt2/LoadedApk.cpp +++ b/tools/aapt2/LoadedApk.cpp @@ -223,8 +223,17 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table io::IFile* file = iterator->Next(); std::string path = file->GetSource().path; + std::string output_path = path; + bool is_resource = path.find("res/") == 0; + if (is_resource) { + auto it = options.shortened_path_map.find(path); + if (it != options.shortened_path_map.end()) { + output_path = it->second; + } + } + // Skip resources that are not referenced if requested. - if (path.find("res/") == 0 && referenced_resources.find(path) == referenced_resources.end()) { + if (is_resource && referenced_resources.find(output_path) == referenced_resources.end()) { if (context->IsVerbose()) { context->GetDiagnostics()->Note(DiagMessage() << "Removing resource '" << path << "' from APK."); @@ -283,7 +292,8 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table return false; } } else { - if (!io::CopyFileToArchivePreserveCompression(context, file, path, writer)) { + if (!io::CopyFileToArchivePreserveCompression( + context, file, output_path, writer)) { return false; } } diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp index 328b0beda372..2e6af18c1948 100644 --- a/tools/aapt2/cmd/Optimize.cpp +++ b/tools/aapt2/cmd/Optimize.cpp @@ -41,6 +41,7 @@ #include "optimize/MultiApkGenerator.h" #include "optimize/ResourceDeduper.h" #include "optimize/ResourceFilter.h" +#include "optimize/ResourcePathShortener.h" #include "optimize/VersionCollapser.h" #include "split/TableSplitter.h" #include "util/Files.h" @@ -52,6 +53,7 @@ using ::android::ConfigDescription; using ::android::ResTable_config; using ::android::StringPiece; using ::android::base::ReadFileToString; +using ::android::base::WriteStringToFile; using ::android::base::StringAppendF; using ::android::base::StringPrintf; @@ -143,6 +145,21 @@ class Optimizer { return 1; } + if (options_.shorten_resource_paths) { + ResourcePathShortener shortener(options_.table_flattener_options.shortened_path_map); + if (!shortener.Consume(context_, apk->GetResourceTable())) { + context_->GetDiagnostics()->Error(DiagMessage() << "failed shortening resource paths"); + return 1; + } + if (options_.shortened_paths_map_path + && !WriteShortenedPathsMap(options_.table_flattener_options.shortened_path_map, + options_.shortened_paths_map_path.value())) { + context_->GetDiagnostics()->Error(DiagMessage() + << "failed to write shortened resource paths to file"); + return 1; + } + } + // Adjust the SplitConstraints so that their SDK version is stripped if it is less than or // equal to the minSdk. options_.split_constraints = @@ -264,6 +281,15 @@ class Optimizer { ArchiveEntry::kAlign, writer); } + bool WriteShortenedPathsMap(const std::map<std::string, std::string> &path_map, + const std::string &file_path) { + std::stringstream ss; + for (auto it = path_map.cbegin(); it != path_map.cend(); ++it) { + ss << it->first << " -> " << it->second << "\n"; + } + return WriteStringToFile(ss.str(), file_path); + } + OptimizeOptions options_; OptimizeContext* context_; }; diff --git a/tools/aapt2/cmd/Optimize.h b/tools/aapt2/cmd/Optimize.h index d07305bc3e04..7f4a3ed85364 100644 --- a/tools/aapt2/cmd/Optimize.h +++ b/tools/aapt2/cmd/Optimize.h @@ -55,6 +55,12 @@ struct OptimizeOptions { // Set of artifacts to keep when generating multi-APK splits. If the list is empty, all artifacts // are kept and will be written as output. std::unordered_set<std::string> kept_artifacts; + + // Whether or not to shorten resource paths in the APK. + bool shorten_resource_paths; + + // Path to the output map of original resource paths to shortened paths. + Maybe<std::string> shortened_paths_map_path; }; class OptimizeCommand : public Command { @@ -101,6 +107,12 @@ class OptimizeCommand : public Command { AddOptionalSwitch("--enable-resource-obfuscation", "Enables obfuscation of key string pool to single value", &options_.table_flattener_options.collapse_key_stringpool); + AddOptionalSwitch("--enable-resource-path-shortening", + "Enables shortening of the path of the resources inside the APK.", + &options_.shorten_resource_paths); + AddOptionalFlag("--resource-path-shortening-map", + "Path to output the map of old resource paths to shortened paths.", + &options_.shortened_paths_map_path); AddOptionalSwitch("-v", "Enables verbose logging", &verbose_); } @@ -109,6 +121,9 @@ class OptimizeCommand : public Command { private: OptimizeOptions options_; + bool WriteObfuscatedPathsMap(const std::map<std::string, std::string> &path_map, + const std::string &file_path); + Maybe<std::string> config_path_; Maybe<std::string> whitelist_path_; Maybe<std::string> resources_config_path_; @@ -122,4 +137,4 @@ class OptimizeCommand : public Command { }// namespace aapt -#endif //AAPT2_OPTIMIZE_H
\ No newline at end of file +#endif //AAPT2_OPTIMIZE_H diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp index c496ff0e159b..7d4c6f348403 100644 --- a/tools/aapt2/format/binary/BinaryResourceParser.cpp +++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp @@ -42,6 +42,19 @@ namespace aapt { namespace { +static std::u16string strcpy16_dtoh(const char16_t* src, size_t len) { + size_t utf16_len = strnlen16(src, len); + if (utf16_len == 0) { + return {}; + } + std::u16string dst; + dst.resize(utf16_len); + for (size_t i = 0; i < utf16_len; i++) { + dst[i] = util::DeviceToHost16(src[i]); + } + return dst; +} + // Visitor that converts a reference's resource ID to a resource name, given a mapping from // resource ID to resource name. class ReferenceIdToNameVisitor : public DescendingValueVisitor { @@ -176,12 +189,8 @@ bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) { } // Extract the package name. - size_t len = strnlen16((const char16_t*)package_header->name, arraysize(package_header->name)); - std::u16string package_name; - package_name.resize(len); - for (size_t i = 0; i < len; i++) { - package_name[i] = util::DeviceToHost16(package_header->name[i]); - } + std::u16string package_name = strcpy16_dtoh((const char16_t*)package_header->name, + arraysize(package_header->name)); ResourceTablePackage* package = table_->CreatePackage(util::Utf16ToUtf8(package_name), static_cast<uint8_t>(package_id)); @@ -435,6 +444,11 @@ bool BinaryResourceParser::ParseOverlayable(const ResChunk_header* chunk) { } auto overlayable = std::make_shared<Overlayable>(); + overlayable->name = util::Utf16ToUtf8(strcpy16_dtoh((const char16_t*)header->name, + arraysize(header->name))); + overlayable->actor = util::Utf16ToUtf8(strcpy16_dtoh((const char16_t*)header->actor, + arraysize(header->name))); + overlayable->source = source_.WithLine(0); ResChunkPullParser parser(GetChunkData(chunk), GetChunkDataLen(chunk)); diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp index 931d57b1c08a..c4ecbafc008b 100644 --- a/tools/aapt2/format/binary/TableFlattener.cpp +++ b/tools/aapt2/format/binary/TableFlattener.cpp @@ -217,9 +217,10 @@ class MapFlattenVisitor : public ValueVisitor { size_t entry_count_ = 0; }; -struct PolicyChunk { - uint32_t policy_flags; - std::set<ResourceId> ids; +struct OverlayableChunk { + std::string actor; + Source source; + std::map<OverlayableItem::PolicyFlags, std::set<ResourceId>> policy_ids; }; class PackageFlattener { @@ -421,8 +422,9 @@ class PackageFlattener { return sorted_entries; } - void FlattenOverlayable(BigBuffer* buffer) { - std::vector<PolicyChunk> policies; + bool FlattenOverlayable(BigBuffer* buffer) { + std::set<ResourceId> seen_ids; + std::map<std::string, OverlayableChunk> overlayable_chunks; CHECK(bool(package_->id)) << "package must have an ID set when flattening <overlayable>"; for (auto& type : package_->types) { @@ -433,79 +435,119 @@ class PackageFlattener { continue; } - OverlayableItem& overlayable = entry->overlayable_item.value(); - uint32_t policy_flags = OverlayableItem::Policy::kNone; - if (overlayable.policies & OverlayableItem::Policy::kPublic) { - policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC; - } - if (overlayable.policies & OverlayableItem::Policy::kSystem) { - policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION; - } - if (overlayable.policies & OverlayableItem::Policy::kVendor) { - policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION; - } - if (overlayable.policies & OverlayableItem::Policy::kProduct) { - policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION; - } - if (overlayable.policies & OverlayableItem::Policy::kProductServices) { - policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION; + OverlayableItem& item = entry->overlayable_item.value(); + + // Resource ids should only appear once in the resource table + ResourceId id = android::make_resid(package_->id.value(), type->id.value(), + entry->id.value()); + CHECK(seen_ids.find(id) == seen_ids.end()) + << "multiple overlayable definitions found for resource " + << ResourceName(package_->name, type->type, entry->name).to_string(); + seen_ids.insert(id); + + // Find the overlayable chunk with the specified name + OverlayableChunk* overlayable_chunk = nullptr; + auto iter = overlayable_chunks.find(item.overlayable->name); + if (iter == overlayable_chunks.end()) { + OverlayableChunk chunk{item.overlayable->actor, item.overlayable->source}; + overlayable_chunk = + &overlayable_chunks.insert({item.overlayable->name, chunk}).first->second; + } else { + OverlayableChunk& chunk = iter->second; + if (!(chunk.source == item.overlayable->source)) { + // The name of an overlayable set of resources must be unique + context_->GetDiagnostics()->Error(DiagMessage(item.overlayable->source) + << "duplicate overlayable name" + << item.overlayable->name << "'"); + context_->GetDiagnostics()->Error(DiagMessage(chunk.source) + << "previous declaration here"); + return false; + } + + CHECK(chunk.actor == item.overlayable->actor); + overlayable_chunk = &chunk; } - if (overlayable.policies == OverlayableItem::Policy::kNone) { + uint32_t policy_flags = 0; + if (item.policies == OverlayableItem::Policy::kNone) { // Encode overlayable entries defined without a policy as publicly overlayable policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC; - } - - // Find the overlayable policy chunk with the same policies as the entry - PolicyChunk* policy_chunk = nullptr; - for (PolicyChunk& policy : policies) { - if (policy.policy_flags == policy_flags) { - policy_chunk = &policy; - break; + } else { + if (item.policies & OverlayableItem::Policy::kPublic) { + policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC; + } + if (item.policies & OverlayableItem::Policy::kSystem) { + policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION; + } + if (item.policies & OverlayableItem::Policy::kVendor) { + policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION; + } + if (item.policies & OverlayableItem::Policy::kProduct) { + policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION; + } + if (item.policies & OverlayableItem::Policy::kProductServices) { + policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION; } } - // Create a new policy chunk if an existing one with the same policy cannot be found - if (policy_chunk == nullptr) { - PolicyChunk p; - p.policy_flags = policy_flags; - policies.push_back(p); - policy_chunk = &policies.back(); + auto policy = overlayable_chunk->policy_ids.find(policy_flags); + if (policy != overlayable_chunk->policy_ids.end()) { + policy->second.insert(id); + } else { + overlayable_chunk->policy_ids.insert( + std::make_pair(policy_flags, std::set<ResourceId>{id})); } - - policy_chunk->ids.insert(android::make_resid(package_->id.value(), type->id.value(), - entry->id.value())); } } - if (policies.empty()) { - // Only write the overlayable chunk if the APK has overlayable entries - return; - } - - ChunkWriter writer(buffer); - writer.StartChunk<ResTable_overlayable_header>(RES_TABLE_OVERLAYABLE_TYPE); - - // Write each policy block for the overlayable - for (PolicyChunk& policy : policies) { - ChunkWriter policy_writer(buffer); - ResTable_overlayable_policy_header* policy_type = - policy_writer.StartChunk<ResTable_overlayable_policy_header>( - RES_TABLE_OVERLAYABLE_POLICY_TYPE); - policy_type->policy_flags = util::HostToDevice32(policy.policy_flags); - policy_type->entry_count = util::HostToDevice32(static_cast<uint32_t>(policy.ids.size())); - - // Write the ids after the policy header - ResTable_ref* id_block = policy_writer.NextBlock<ResTable_ref>(policy.ids.size()); - for (const ResourceId& id : policy.ids) { - id_block->ident = util::HostToDevice32(id.id); - id_block++; + for (auto& overlayable_pair : overlayable_chunks) { + std::string name = overlayable_pair.first; + OverlayableChunk& overlayable = overlayable_pair.second; + + // Write the header of the overlayable chunk + ChunkWriter overlayable_writer(buffer); + auto* overlayable_type = + overlayable_writer.StartChunk<ResTable_overlayable_header>(RES_TABLE_OVERLAYABLE_TYPE); + if (name.size() >= arraysize(overlayable_type->name)) { + diag_->Error(DiagMessage() << "overlayable name '" << name + << "' exceeds maximum length (" + << arraysize(overlayable_type->name) + << " utf16 characters)"); + return false; } - - policy_writer.Finish(); + strcpy16_htod(overlayable_type->name, arraysize(overlayable_type->name), + util::Utf8ToUtf16(name)); + + if (overlayable.actor.size() >= arraysize(overlayable_type->actor)) { + diag_->Error(DiagMessage() << "overlayable name '" << overlayable.actor + << "' exceeds maximum length (" + << arraysize(overlayable_type->actor) + << " utf16 characters)"); + return false; + } + strcpy16_htod(overlayable_type->actor, arraysize(overlayable_type->actor), + util::Utf8ToUtf16(overlayable.actor)); + + // Write each policy block for the overlayable + for (auto& policy_ids : overlayable.policy_ids) { + ChunkWriter policy_writer(buffer); + auto* policy_type = policy_writer.StartChunk<ResTable_overlayable_policy_header>( + RES_TABLE_OVERLAYABLE_POLICY_TYPE); + policy_type->policy_flags = util::HostToDevice32(static_cast<uint32_t>(policy_ids.first)); + policy_type->entry_count = util::HostToDevice32(static_cast<uint32_t>( + policy_ids.second.size())); + // Write the ids after the policy header + auto* id_block = policy_writer.NextBlock<ResTable_ref>(policy_ids.second.size()); + for (const ResourceId& id : policy_ids.second) { + id_block->ident = util::HostToDevice32(id.id); + id_block++; + } + policy_writer.Finish(); + } + overlayable_writer.Finish(); } - writer.Finish(); + return true; } bool FlattenTypeSpec(ResourceTableType* type, std::vector<ResourceEntry*>* sorted_entries, diff --git a/tools/aapt2/format/binary/TableFlattener.h b/tools/aapt2/format/binary/TableFlattener.h index 635cb21f514c..71330e3fb74f 100644 --- a/tools/aapt2/format/binary/TableFlattener.h +++ b/tools/aapt2/format/binary/TableFlattener.h @@ -46,6 +46,9 @@ struct TableFlattenerOptions { // When true, sort the entries in the values string pool by priority and configuration. bool sort_stringpool_entries = true; + + // Map from original resource paths to shortened resource paths. + std::map<std::string, std::string> shortened_path_map; }; class TableFlattener : public IResourceTableConsumer { diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp index a5fb6fd6d7aa..18fecf60c977 100644 --- a/tools/aapt2/format/binary/TableFlattener_test.cpp +++ b/tools/aapt2/format/binary/TableFlattener_test.cpp @@ -657,36 +657,36 @@ TEST_F(TableFlattenerTest, FlattenOverlayable) { TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) { auto overlayable = std::make_shared<Overlayable>("TestName", "overlay://theme"); std::string name_zero = "com.app.test:integer/overlayable_zero_item"; - OverlayableItem overlayable_zero_item(overlayable); - overlayable_zero_item.policies |= OverlayableItem::Policy::kProduct; - overlayable_zero_item.policies |= OverlayableItem::Policy::kSystem; - overlayable_zero_item.policies |= OverlayableItem::Policy::kProductServices; + OverlayableItem overlayable_item_zero(overlayable); + overlayable_item_zero.policies |= OverlayableItem::Policy::kProduct; + overlayable_item_zero.policies |= OverlayableItem::Policy::kSystem; + overlayable_item_zero.policies |= OverlayableItem::Policy::kProductServices; std::string name_one = "com.app.test:integer/overlayable_one_item"; - OverlayableItem overlayable_one_item(overlayable); - overlayable_one_item.policies |= OverlayableItem::Policy::kPublic; - overlayable_one_item.policies |= OverlayableItem::Policy::kProductServices; + OverlayableItem overlayable_item_one(overlayable); + overlayable_item_one.policies |= OverlayableItem::Policy::kPublic; + overlayable_item_one.policies |= OverlayableItem::Policy::kProductServices; std::string name_two = "com.app.test:integer/overlayable_two_item"; - OverlayableItem overlayable_two_item(overlayable); - overlayable_two_item.policies |= OverlayableItem::Policy::kProduct; - overlayable_two_item.policies |= OverlayableItem::Policy::kSystem; - overlayable_two_item.policies |= OverlayableItem::Policy::kVendor; + OverlayableItem overlayable_item_two(overlayable); + overlayable_item_two.policies |= OverlayableItem::Policy::kProduct; + overlayable_item_two.policies |= OverlayableItem::Policy::kSystem; + overlayable_item_two.policies |= OverlayableItem::Policy::kVendor; std::string name_three = "com.app.test:integer/overlayable_three_item"; - OverlayableItem overlayable_three_item(overlayable); + OverlayableItem overlayable_item_three(overlayable); std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() .SetPackageId("com.app.test", 0x7f) .AddSimple(name_zero, ResourceId(0x7f020000)) - .SetOverlayable(name_zero, overlayable_zero_item) + .SetOverlayable(name_zero, overlayable_item_zero) .AddSimple(name_one, ResourceId(0x7f020001)) - .SetOverlayable(name_one, overlayable_one_item) + .SetOverlayable(name_one, overlayable_item_one) .AddSimple(name_two, ResourceId(0x7f020002)) - .SetOverlayable(name_two, overlayable_two_item) + .SetOverlayable(name_two, overlayable_item_two) .AddSimple(name_three, ResourceId(0x7f020003)) - .SetOverlayable(name_three, overlayable_three_item) + .SetOverlayable(name_three, overlayable_item_three) .Build(); ResourceTable output_table; @@ -724,6 +724,84 @@ TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) { ASSERT_TRUE(search_result.value().entry->overlayable_item); overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic); + EXPECT_EQ(overlayable_item.overlayable->name, "TestName"); + EXPECT_EQ(overlayable_item.overlayable->actor, "overlay://theme"); + EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic); +} + +TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) { + auto group = std::make_shared<Overlayable>("TestName", "overlay://theme"); + std::string name_zero = "com.app.test:integer/overlayable_zero"; + OverlayableItem overlayable_item_zero(group); + overlayable_item_zero.policies |= OverlayableItem::Policy::kProduct; + overlayable_item_zero.policies |= OverlayableItem::Policy::kSystem; + overlayable_item_zero.policies |= OverlayableItem::Policy::kProductServices; + + auto group_one = std::make_shared<Overlayable>("OtherName", "overlay://customization"); + std::string name_one = "com.app.test:integer/overlayable_one"; + OverlayableItem overlayable_item_one(group_one); + overlayable_item_one.policies |= OverlayableItem::Policy::kPublic; + overlayable_item_one.policies |= OverlayableItem::Policy::kProductServices; + + std::string name_two = "com.app.test:integer/overlayable_two"; + OverlayableItem overlayable_item_two(group); + overlayable_item_two.policies |= OverlayableItem::Policy::kProduct; + overlayable_item_two.policies |= OverlayableItem::Policy::kSystem; + overlayable_item_two.policies |= OverlayableItem::Policy::kVendor; + + std::string name_three = "com.app.test:integer/overlayable_three"; + OverlayableItem overlayable_item_three(group_one); + std::unique_ptr<ResourceTable> table = + test::ResourceTableBuilder() + .SetPackageId("com.app.test", 0x7f) + .AddSimple(name_zero, ResourceId(0x7f020000)) + .SetOverlayable(name_zero, overlayable_item_zero) + .AddSimple(name_one, ResourceId(0x7f020001)) + .SetOverlayable(name_one, overlayable_item_one) + .AddSimple(name_two, ResourceId(0x7f020002)) + .SetOverlayable(name_two, overlayable_item_two) + .AddSimple(name_three, ResourceId(0x7f020003)) + .SetOverlayable(name_three, overlayable_item_three) + .Build(); + ResourceTable output_table; + ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &output_table)); + auto search_result = output_table.FindResource(test::ParseNameOrDie(name_zero)); + ASSERT_TRUE(search_result); + ASSERT_THAT(search_result.value().entry, NotNull()); + ASSERT_TRUE(search_result.value().entry->overlayable_item); + OverlayableItem& result_overlayable = search_result.value().entry->overlayable_item.value(); + EXPECT_EQ(result_overlayable.overlayable->name, "TestName"); + EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://theme"); + EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kSystem + | OverlayableItem::Policy::kProduct + | OverlayableItem::Policy::kProductServices); + search_result = output_table.FindResource(test::ParseNameOrDie(name_one)); + ASSERT_TRUE(search_result); + ASSERT_THAT(search_result.value().entry, NotNull()); + ASSERT_TRUE(search_result.value().entry->overlayable_item); + result_overlayable = search_result.value().entry->overlayable_item.value(); + EXPECT_EQ(result_overlayable.overlayable->name, "OtherName"); + EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization"); + EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kPublic + | OverlayableItem::Policy::kProductServices); + search_result = output_table.FindResource(test::ParseNameOrDie(name_two)); + ASSERT_TRUE(search_result); + ASSERT_THAT(search_result.value().entry, NotNull()); + ASSERT_TRUE(search_result.value().entry->overlayable_item); + result_overlayable = search_result.value().entry->overlayable_item.value(); + EXPECT_EQ(result_overlayable.overlayable->name, "TestName"); + EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://theme"); + EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kSystem + | OverlayableItem::Policy::kProduct + | OverlayableItem::Policy::kVendor); + search_result = output_table.FindResource(test::ParseNameOrDie(name_three)); + ASSERT_TRUE(search_result); + ASSERT_THAT(search_result.value().entry, NotNull()); + ASSERT_TRUE(search_result.value().entry->overlayable_item); + result_overlayable = search_result.value().entry->overlayable_item.value(); + EXPECT_EQ(result_overlayable.overlayable->name, "OtherName"); + EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization"); + EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kPublic); } } // namespace aapt diff --git a/tools/aapt2/optimize/ResourcePathShortener.cpp b/tools/aapt2/optimize/ResourcePathShortener.cpp new file mode 100644 index 000000000000..c5df3dd00db9 --- /dev/null +++ b/tools/aapt2/optimize/ResourcePathShortener.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "optimize/ResourcePathShortener.h" + +#include <math.h> +#include <unordered_set> + +#include "androidfw/StringPiece.h" + +#include "ResourceTable.h" +#include "ValueVisitor.h" + + +static const std::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789-_"; + +namespace aapt { + +ResourcePathShortener::ResourcePathShortener( + std::map<std::string, std::string>& path_map_out) + : path_map_(path_map_out) { +} + +std::string ShortenFileName(const android::StringPiece& file_path, int output_length) { + std::size_t hash_num = std::hash<android::StringPiece>{}(file_path); + std::string result = ""; + // Convert to (modified) base64 so that it is a proper file path. + for (int i = 0; i < output_length; i++) { + uint8_t sextet = hash_num & 0x3f; + hash_num >>= 6; + result += base64_chars[sextet]; + } + return result; +} + + +// Calculate the optimal hash length such that an average of 10% of resources +// collide in their shortened path. +// Reference: http://matt.might.net/articles/counting-hash-collisions/ +int OptimalShortenedLength(int num_resources) { + int num_chars = 2; + double N = 64*64; // hash space when hash is 2 chars long + double max_collisions = num_resources * 0.1; + while (num_resources - N + N * pow((N - 1) / N, num_resources) > max_collisions) { + N *= 64; + num_chars++; + } + return num_chars; +} + +std::string GetShortenedPath(const android::StringPiece& shortened_filename, + const android::StringPiece& extension, int collision_count) { + std::string shortened_path = "res/" + shortened_filename.to_string(); + if (collision_count > 0) { + shortened_path += std::to_string(collision_count); + } + shortened_path += extension; + return shortened_path; +} + +bool ResourcePathShortener::Consume(IAaptContext* context, ResourceTable* table) { + // used to detect collisions + std::unordered_set<std::string> shortened_paths; + std::unordered_set<FileReference*> file_refs; + for (auto& package : table->packages) { + for (auto& type : package->types) { + for (auto& entry : type->entries) { + for (auto& config_value : entry->values) { + FileReference* file_ref = ValueCast<FileReference>(config_value->value.get()); + if (file_ref) { + file_refs.insert(file_ref); + } + } + } + } + } + int num_chars = OptimalShortenedLength(file_refs.size()); + for (auto& file_ref : file_refs) { + android::StringPiece res_subdir, actual_filename, extension; + util::ExtractResFilePathParts(*file_ref->path, &res_subdir, &actual_filename, &extension); + + std::string shortened_filename = ShortenFileName(*file_ref->path, num_chars); + int collision_count = 0; + std::string shortened_path = GetShortenedPath(shortened_filename, extension, collision_count); + while (shortened_paths.find(shortened_path) != shortened_paths.end()) { + collision_count++; + shortened_path = GetShortenedPath(shortened_filename, extension, collision_count); + } + shortened_paths.insert(shortened_path); + path_map_.insert({*file_ref->path, shortened_path}); + file_ref->path = table->string_pool.MakeRef(shortened_path, file_ref->path.GetContext()); + } + return true; +} + +} // namespace aapt diff --git a/tools/aapt2/optimize/ResourcePathShortener.h b/tools/aapt2/optimize/ResourcePathShortener.h new file mode 100644 index 000000000000..f1074ef083bd --- /dev/null +++ b/tools/aapt2/optimize/ResourcePathShortener.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AAPT_OPTIMIZE_RESOURCEPATHSHORTENER_H +#define AAPT_OPTIMIZE_RESOURCEPATHSHORTENER_H + +#include <map> + +#include "android-base/macros.h" + +#include "process/IResourceTableConsumer.h" + +namespace aapt { + +class ResourceTable; + +// Maps resources in the apk to shortened paths. +class ResourcePathShortener : public IResourceTableConsumer { + public: + explicit ResourcePathShortener(std::map<std::string, std::string>& path_map_out); + + bool Consume(IAaptContext* context, ResourceTable* table) override; + + private: + DISALLOW_COPY_AND_ASSIGN(ResourcePathShortener); + std::map<std::string, std::string>& path_map_; +}; + +} // namespace aapt + +#endif // AAPT_OPTIMIZE_RESOURCEPATHSHORTENER_H diff --git a/tools/aapt2/optimize/ResourcePathShortener_test.cpp b/tools/aapt2/optimize/ResourcePathShortener_test.cpp new file mode 100644 index 000000000000..88cadc76c336 --- /dev/null +++ b/tools/aapt2/optimize/ResourcePathShortener_test.cpp @@ -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. + */ + +#include "optimize/ResourcePathShortener.h" + +#include "ResourceTable.h" +#include "test/Test.h" + +using ::aapt::test::GetValue; +using ::testing::Not; +using ::testing::NotNull; +using ::testing::Eq; + +namespace aapt { + +TEST(ResourcePathShortenerTest, FileRefPathsChangedInResourceTable) { + std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); + + std::unique_ptr<ResourceTable> table = + test::ResourceTableBuilder() + .AddFileReference("android:drawable/xmlfile", "res/drawables/xmlfile.xml") + .AddFileReference("android:drawable/xmlfile2", "res/drawables/xmlfile2.xml") + .AddString("android:string/string", "res/should/still/be/the/same.png") + .Build(); + + std::map<std::string, std::string> path_map; + ASSERT_TRUE(ResourcePathShortener(path_map).Consume(context.get(), table.get())); + + // Expect that the path map is populated + ASSERT_THAT(path_map.find("res/drawables/xmlfile.xml"), Not(Eq(path_map.end()))); + ASSERT_THAT(path_map.find("res/drawables/xmlfile2.xml"), Not(Eq(path_map.end()))); + + // The file paths were changed + EXPECT_THAT(path_map.at("res/drawables/xmlfile.xml"), Not(Eq("res/drawables/xmlfile.xml"))); + EXPECT_THAT(path_map.at("res/drawables/xmlfile2.xml"), Not(Eq("res/drawables/xmlfile2.xml"))); + + // Different file paths should remain different + EXPECT_THAT(path_map["res/drawables/xmlfile.xml"], + Not(Eq(path_map["res/drawables/xmlfile2.xml"]))); + + FileReference* ref = + GetValue<FileReference>(table.get(), "android:drawable/xmlfile"); + ASSERT_THAT(ref, NotNull()); + // The map correctly points to the new location of the file + EXPECT_THAT(path_map["res/drawables/xmlfile.xml"], Eq(*ref->path)); + + // Strings should not be affected, only file paths + EXPECT_THAT( + *GetValue<String>(table.get(), "android:string/string")->value, + Eq("res/should/still/be/the/same.png")); + EXPECT_THAT(path_map.find("res/should/still/be/the/same.png"), Eq(path_map.end())); +} + +} // namespace aapt diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py index acf1f1e9902e..d1fe43ea0c7c 100644 --- a/tools/apilint/apilint.py +++ b/tools/apilint/apilint.py @@ -297,7 +297,7 @@ class V2Tokenizer(object): class V2LineParser(object): __slots__ = ["tokenized", "current", "len"] - MODIFIERS = set("public protected internal private abstract default static final transient volatile synchronized".split()) + MODIFIERS = set("public protected internal private abstract default static final transient volatile synchronized native operator sealed strictfp infix inline suspend vararg".split()) JAVA_LANG_TYPES = set("AbstractMethodError AbstractStringBuilder Appendable ArithmeticException ArrayIndexOutOfBoundsException ArrayStoreException AssertionError AutoCloseable Boolean BootstrapMethodError Byte Character CharSequence Class ClassCastException ClassCircularityError ClassFormatError ClassLoader ClassNotFoundException Cloneable CloneNotSupportedException Comparable Compiler Deprecated Double Enum EnumConstantNotPresentException Error Exception ExceptionInInitializerError Float FunctionalInterface IllegalAccessError IllegalAccessException IllegalArgumentException IllegalMonitorStateException IllegalStateException IllegalThreadStateException IncompatibleClassChangeError IndexOutOfBoundsException InheritableThreadLocal InstantiationError InstantiationException Integer InternalError InterruptedException Iterable LinkageError Long Math NegativeArraySizeException NoClassDefFoundError NoSuchFieldError NoSuchFieldException NoSuchMethodError NoSuchMethodException NullPointerException Number NumberFormatException Object OutOfMemoryError Override Package package-info.java Process ProcessBuilder ProcessEnvironment ProcessImpl Readable ReflectiveOperationException Runnable Runtime RuntimeException RuntimePermission SafeVarargs SecurityException SecurityManager Short StackOverflowError StackTraceElement StrictMath String StringBuffer StringBuilder StringIndexOutOfBoundsException SuppressWarnings System Thread ThreadDeath ThreadGroup ThreadLocal Throwable TypeNotPresentException UNIXProcess UnknownError UnsatisfiedLinkError UnsupportedClassVersionError UnsupportedOperationException VerifyError VirtualMachineError Void".split()) def __init__(self, raw): @@ -355,7 +355,7 @@ class V2LineParser(object): self.parse_eof() def parse_into_field(self, field): - kind = self.parse_token("field") + kind = self.parse_one_of("field", "property") field.split = [kind] annotations = self.parse_annotations() if "@Deprecated" in annotations: @@ -442,13 +442,19 @@ class V2LineParser(object): return None def parse_type(self): + self.parse_annotations() type = self.parse_token() + if type[-1] == '.': + self.parse_annotations() + type += self.parse_token() if type in V2LineParser.JAVA_LANG_TYPES: type = "java.lang." + type self.parse_matching_paren("<", ">") while True: t = self.lookahead() - if t == "[]": + if t == "@": + self.parse_annotation() + elif t == "[]": type += self.parse_token() elif self.parse_kotlin_nullability() is not None: pass # discard nullability for now @@ -478,17 +484,23 @@ class V2LineParser(object): self.parse_token(",") def parse_arg(self): + self.parse_if("vararg") # kotlin vararg self.parse_annotations() type = self.parse_arg_type() l = self.lookahead() if l != "," and l != ")": - self.parse_token() # kotlin argument name + if self.lookahead() != '=': + self.parse_token() # kotlin argument name if self.parse_if('='): # kotlin default value - (self.parse_matching_paren('(', ')') or - self.parse_matching_paren('{', '}') or - self.parse_token() and self.parse_matching_paren('(', ')')) + self.parse_expression() return type + def parse_expression(self): + while not self.lookahead() in [')', ',', ';']: + (self.parse_matching_paren('(', ')') or + self.parse_matching_paren('{', '}') or + self.parse_token()) + def parse_throws(self): ret = [] if self.parse_if("throws"): @@ -516,7 +528,7 @@ class V2LineParser(object): def parse_annotation_default(self): if self.parse_if("default"): - self.parse_value() + self.parse_expression() def parse_value(self): if self.lookahead() == "{": @@ -599,7 +611,7 @@ def _parse_stream_to_generator(f): clazz.ctors.append(Method(clazz, line, raw, blame, sig_format=sig_format)) elif raw.startswith(" method"): clazz.methods.append(Method(clazz, line, raw, blame, sig_format=sig_format)) - elif raw.startswith(" field"): + elif raw.startswith(" field") or raw.startswith(" property"): clazz.fields.append(Field(clazz, line, raw, blame, sig_format=sig_format)) elif raw.startswith(" }") and clazz: yield clazz @@ -942,7 +954,7 @@ def verify_fields(clazz): else: error(clazz, f, "F2", "Bare fields must be marked final, or add accessors if mutable") - if not "static" in f.split: + if "static" not in f.split and "property" not in f.split: if not re.match("[a-z]([a-zA-Z]+)?", f.name): error(clazz, f, "S1", "Non-static fields must be named using myField style") @@ -1650,7 +1662,7 @@ def verify_method_name_not_kotlin_operator(clazz): binary.add(op) for m in clazz.methods: - if 'static' in m.split: + if 'static' in m.split or 'operator' in m.split: continue # https://kotlinlang.org/docs/reference/operator-overloading.html#unary-prefix-operators diff --git a/tools/apilint/apilint_test.py b/tools/apilint/apilint_test.py index 081e98defa17..fde61a902341 100644 --- a/tools/apilint/apilint_test.py +++ b/tools/apilint/apilint_test.py @@ -225,11 +225,12 @@ class V2ParserTests(unittest.TestCase): self.assertEquals('pkg.SuppressLint', cls.fullname) def test_parse_method(self): - m = self._method("method @Deprecated public static <T> Class<T>[][] name(" + m = self._method("method @Deprecated public static native <T> Class<T>[][] name(" + "Class<T[]>[][], Class<T[][][]>[][]...) throws Exception, T;") self.assertTrue('static' in m.split) self.assertTrue('public' in m.split) self.assertTrue('method' in m.split) + self.assertTrue('native' in m.split) self.assertTrue('deprecated' in m.split) self.assertEquals('java.lang.Class[][]', m.typ) self.assertEquals('name', m.name) @@ -248,6 +249,7 @@ class V2ParserTests(unittest.TestCase): self._method('method abstract String category() default "";', cls=cls) self._method('method abstract boolean deepExport() default false;', cls=cls) self._method('method abstract ViewDebug.FlagToString[] flagMapping() default {};', cls=cls) + self._method('method abstract ViewDebug.FlagToString[] flagMapping() default (double)java.lang.Float.NEGATIVE_INFINITY;', cls=cls) def test_parse_string_field(self): f = self._field('field @Deprecated public final String SOME_NAME = "value";') @@ -286,5 +288,44 @@ class V2ParserTests(unittest.TestCase): self._method("method <T> T name(T a = 1, T b = A(1), Lambda f = { false }, N? n = null, " + """double c = (1/0), float d = 1.0f, String s = "heyo", char c = 'a');""") + def test_kotlin_operator(self): + self._method('method public operator void unaryPlus(androidx.navigation.NavDestination);') + self._method('method public static operator androidx.navigation.NavDestination get(androidx.navigation.NavGraph, @IdRes int id);') + self._method('method public static operator <T> T get(androidx.navigation.NavigatorProvider, kotlin.reflect.KClass<T> clazz);') + + def test_kotlin_property(self): + self._field('property public VM value;') + self._field('property public final String? action;') + + def test_kotlin_varargs(self): + self._method('method public void error(int p = "42", Integer int2 = "null", int p1 = "42", vararg String args);') + + def test_kotlin_default_values(self): + self._method('method public void foo(String! = null, String! = "Hello World", int = 42);') + self._method('method void method(String, String firstArg = "hello", int secondArg = "42", String thirdArg = "world");') + self._method('method void method(String, String firstArg = "hello", int secondArg = "42");') + self._method('method void method(String, String firstArg = "hello");') + self._method('method void edit(android.Type, boolean commit = false, Function1<? super Editor,kotlin.Unit> action);') + self._method('method <K, V> LruCache<K,V> lruCache(int maxSize, Function2<? super K,? super V,java.lang.Integer> sizeOf = { _, _ -> 1 }, Function1<? extends V> create = { (V)null }, Function4<kotlin.Unit> onEntryRemoved = { _, _, _, _ -> });') + self._method('method android.Bitmap? drawToBitmap(android.View, android.Config config = android.graphics.Bitmap.Config.ARGB_8888);') + self._method('method void emptyLambda(Function0<kotlin.Unit> sizeOf = {});') + self._method('method void method1(int p = 42, Integer? int2 = null, int p1 = 42, String str = "hello world", java.lang.String... args);') + self._method('method void method2(int p, int int2 = (2 * int) * some.other.pkg.Constants.Misc.SIZE);') + self._method('method void method3(String str, int p, int int2 = double(int) + str.length);') + self._method('method void print(test.pkg.Foo foo = test.pkg.Foo());') + + def test_type_use_annotation(self): + self._method('method public static int codePointAt(char @NonNull [], int);') + self._method('method @NonNull public java.util.Set<java.util.Map.@NonNull Entry<K,V>> entrySet();') + + m = self._method('method @NonNull public java.lang.annotation.@NonNull Annotation @NonNull [] getAnnotations();') + self.assertEquals('java.lang.annotation.Annotation[]', m.typ) + + m = self._method('method @NonNull public abstract java.lang.annotation.@NonNull Annotation @NonNull [] @NonNull [] getParameterAnnotations();') + self.assertEquals('java.lang.annotation.Annotation[][]', m.typ) + + m = self._method('method @NonNull public @NonNull String @NonNull [] split(@NonNull String, int);') + self.assertEquals('java.lang.String[]', m.typ) + if __name__ == "__main__": unittest.main() diff --git a/tools/signedconfig/prod_public.pem b/tools/signedconfig/prod_public.pem new file mode 100644 index 000000000000..8c10215eb083 --- /dev/null +++ b/tools/signedconfig/prod_public.pem @@ -0,0 +1,5 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+lky6wKyGL6lE1VrD0YTMHwb0Xwc ++tzC8MvnrzVxodvTpVY/jV7V+Zktcx+pry43XPABFRXtbhTo+qykhyBA1g== +-----END PUBLIC KEY----- + diff --git a/tools/signedconfig/verify_b64.sh b/tools/signedconfig/verify_b64.sh index 8e1f58ce7b45..a4ac6a816d14 100755 --- a/tools/signedconfig/verify_b64.sh +++ b/tools/signedconfig/verify_b64.sh @@ -7,4 +7,30 @@ # The arg values can be taken from the debug log for SignedConfigService when verbose logging is # enabled. -openssl dgst -sha256 -verify $(dirname $0)/debug_public.pem -signature <(echo $2 | base64 -d) <(echo $1 | base64 -d) +function verify() { + D=${1} + S=${2} + K=${3} + echo Trying ${K} + openssl dgst -sha256 -verify $(dirname $0)/${K} -signature <(echo ${S} | base64 -d) <(echo ${D} | base64 -d) +} + + +PROD_KEY_NAME=prod_public.pem +DEBUG_KEY_NAME=debug_public.pem +SIGNATURE="$2" +DATA="$1" + +echo DATA: ${DATA} +echo SIGNATURE: ${SIGNATURE} + +if verify "${DATA}" "${SIGNATURE}" "${PROD_KEY_NAME}"; then + echo Verified with ${PROD_KEY_NAME} + exit 0 +fi + +if verify "${DATA}" "${SIGNATURE}" "${DEBUG_KEY_NAME}"; then + echo Verified with ${DEBUG_KEY_NAME} + exit 0 +fi +exit 1 diff --git a/wifi/java/android/net/wifi/DppStatusCallback.java b/wifi/java/android/net/wifi/DppStatusCallback.java deleted file mode 100644 index fa2ab30ef479..000000000000 --- a/wifi/java/android/net/wifi/DppStatusCallback.java +++ /dev/null @@ -1,166 +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 android.net.wifi; - -import android.annotation.IntDef; -import android.annotation.SystemApi; -import android.os.Handler; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * DPP Status Callback. Use this callback to get status updates (success, failure, progress) - * from the DPP operation started with {@link WifiManager#startDppAsConfiguratorInitiator(String, - * int, int, Handler, DppStatusCallback)} or {@link WifiManager#startDppAsEnrolleeInitiator(String, - * Handler, DppStatusCallback)} - * @hide - */ -@SystemApi -public abstract class DppStatusCallback { - /** - * DPP Success event: Configuration sent (Configurator mode). - */ - public static final int DPP_EVENT_SUCCESS_CONFIGURATION_SENT = 0; - - /** @hide */ - @IntDef(prefix = { "DPP_EVENT_SUCCESS_" }, value = { - DPP_EVENT_SUCCESS_CONFIGURATION_SENT, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface DppSuccessStatusCode {} - - /** - * DPP Progress event: Initial authentication with peer succeeded. - */ - public static final int DPP_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0; - - /** - * DPP Progress event: Peer requires more time to process bootstrapping. - */ - public static final int DPP_EVENT_PROGRESS_RESPONSE_PENDING = 1; - - /** @hide */ - @IntDef(prefix = { "DPP_EVENT_PROGRESS_" }, value = { - DPP_EVENT_PROGRESS_AUTHENTICATION_SUCCESS, - DPP_EVENT_PROGRESS_RESPONSE_PENDING, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface DppProgressStatusCode {} - - /** - * DPP Failure event: Scanned QR code is either not a DPP URI, or the DPP URI has errors. - */ - public static final int DPP_EVENT_FAILURE_INVALID_URI = -1; - - /** - * DPP Failure event: Bootstrapping/Authentication initialization process failure. - */ - public static final int DPP_EVENT_FAILURE_AUTHENTICATION = -2; - - /** - * DPP Failure event: Both devices are implementing the same role and are incompatible. - */ - public static final int DPP_EVENT_FAILURE_NOT_COMPATIBLE = -3; - - /** - * DPP Failure event: Configuration process has failed due to malformed message. - */ - public static final int DPP_EVENT_FAILURE_CONFIGURATION = -4; - - /** - * DPP Failure event: DPP request while in another DPP exchange. - */ - public static final int DPP_EVENT_FAILURE_BUSY = -5; - - /** - * DPP Failure event: No response from the peer. - */ - public static final int DPP_EVENT_FAILURE_TIMEOUT = -6; - - /** - * DPP Failure event: General protocol failure. - */ - public static final int DPP_EVENT_FAILURE = -7; - - /** - * DPP Failure event: Feature or option is not supported. - */ - public static final int DPP_EVENT_FAILURE_NOT_SUPPORTED = -8; - - /** - * DPP Failure event: Invalid network provided to DPP configurator. - * Network must either be WPA3-Personal (SAE) or WPA2-Personal (PSK). - */ - public static final int DPP_EVENT_FAILURE_INVALID_NETWORK = -9; - - - /** @hide */ - @IntDef(prefix = {"DPP_EVENT_FAILURE_"}, value = { - DPP_EVENT_FAILURE_INVALID_URI, - DPP_EVENT_FAILURE_AUTHENTICATION, - DPP_EVENT_FAILURE_NOT_COMPATIBLE, - DPP_EVENT_FAILURE_CONFIGURATION, - DPP_EVENT_FAILURE_BUSY, - DPP_EVENT_FAILURE_TIMEOUT, - DPP_EVENT_FAILURE, - DPP_EVENT_FAILURE_NOT_SUPPORTED, - DPP_EVENT_FAILURE_INVALID_NETWORK, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface DppFailureStatusCode { - } - - /** - * Called when local DPP Enrollee successfully receives a new Wi-Fi configuration from the - * peer DPP configurator. This callback marks the successful end of the DPP current DPP - * session, and no further callbacks will be called. This callback is the successful outcome - * of a DPP flow starting with {@link WifiManager#startDppAsEnrolleeInitiator(String, Handler, - * DppStatusCallback)}. - * - * @param newNetworkId New Wi-Fi configuration with a network ID received from the configurator - */ - public abstract void onEnrolleeSuccess(int newNetworkId); - - /** - * Called when a DPP success event takes place, except for when configuration is received from - * an external Configurator. The callback onSuccessConfigReceived will be used in this case. - * This callback marks the successful end of the current DPP session, and no further - * callbacks will be called. This callback is the successful outcome of a DPP flow starting with - * {@link WifiManager#startDppAsConfiguratorInitiator(String, int, int, Handler, - * DppStatusCallback)}. - * - * @param code DPP success status code. - */ - public abstract void onConfiguratorSuccess(@DppSuccessStatusCode int code); - - /** - * Called when a DPP Failure event takes place. This callback marks the unsuccessful end of the - * current DPP session, and no further callbacks will be called. - * - * @param code DPP failure status code. - */ - public abstract void onFailure(@DppFailureStatusCode int code); - - /** - * Called when DPP events that indicate progress take place. Can be used by UI elements - * to show progress. - * - * @param code DPP progress status code. - */ - public abstract void onProgress(@DppProgressStatusCode int code); -} diff --git a/wifi/java/android/net/wifi/EasyConnectStatusCallback.java b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java new file mode 100644 index 000000000000..3b4a6cd7508c --- /dev/null +++ b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi; + +import android.annotation.IntDef; +import android.annotation.SystemApi; +import android.os.Handler; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Easy Connect (DPP) Status Callback. Use this callback to get status updates (success, failure, + * progress) from the Easy Connect operation started with + * {@link WifiManager#startEasyConnectAsConfiguratorInitiator(String, + * int, int, Handler, EasyConnectStatusCallback)} or + * {@link WifiManager#startEasyConnectAsEnrolleeInitiator(String, + * Handler, EasyConnectStatusCallback)} + * + * @hide + */ +@SystemApi +public abstract class EasyConnectStatusCallback { + /** + * Easy Connect Success event: Configuration sent (Configurator mode). + */ + public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT = 0; + + /** @hide */ + @IntDef(prefix = {"EASY_CONNECT_EVENT_SUCCESS_"}, value = { + EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EasyConnectSuccessStatusCode { + } + + /** + * Easy Connect Progress event: Initial authentication with peer succeeded. + */ + public static final int EASY_CONNECT_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0; + + /** + * Easy Connect Progress event: Peer requires more time to process bootstrapping. + */ + public static final int EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING = 1; + + /** @hide */ + @IntDef(prefix = {"EASY_CONNECT_EVENT_PROGRESS_"}, value = { + EASY_CONNECT_EVENT_PROGRESS_AUTHENTICATION_SUCCESS, + EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EasyConnectProgressStatusCode { + } + + /** + * Easy Connect Failure event: Scanned QR code is either not a Easy Connect URI, or the Easy + * Connect URI has errors. + */ + public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_URI = -1; + + /** + * Easy Connect Failure event: Bootstrapping/Authentication initialization process failure. + */ + public static final int EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION = -2; + + /** + * Easy Connect Failure event: Both devices are implementing the same role and are incompatible. + */ + public static final int EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE = -3; + + /** + * Easy Connect Failure event: Configuration process has failed due to malformed message. + */ + public static final int EASY_CONNECT_EVENT_FAILURE_CONFIGURATION = -4; + + /** + * Easy Connect Failure event: Easy Connect request while in another Easy Connect exchange. + */ + public static final int EASY_CONNECT_EVENT_FAILURE_BUSY = -5; + + /** + * Easy Connect Failure event: No response from the peer. + */ + public static final int EASY_CONNECT_EVENT_FAILURE_TIMEOUT = -6; + + /** + * Easy Connect Failure event: General protocol failure. + */ + public static final int EASY_CONNECT_EVENT_FAILURE = -7; + + /** + * Easy Connect Failure event: Feature or option is not supported. + */ + public static final int EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED = -8; + + /** + * Easy Connect Failure event: Invalid network provided to Easy Connect configurator. + * Network must either be WPA3-Personal (SAE) or WPA2-Personal (PSK). + */ + public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK = -9; + + + /** @hide */ + @IntDef(prefix = {"EASY_CONNECT_EVENT_FAILURE_"}, value = { + EASY_CONNECT_EVENT_FAILURE_INVALID_URI, + EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION, + EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE, + EASY_CONNECT_EVENT_FAILURE_CONFIGURATION, + EASY_CONNECT_EVENT_FAILURE_BUSY, + EASY_CONNECT_EVENT_FAILURE_TIMEOUT, + EASY_CONNECT_EVENT_FAILURE, + EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED, + EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EasyConnectFailureStatusCode { + } + + /** + * Called when local Easy Connect Enrollee successfully receives a new Wi-Fi configuration from + * the + * peer Easy Connect configurator. This callback marks the successful end of the Easy Connect + * current Easy Connect + * session, and no further callbacks will be called. This callback is the successful outcome + * of a Easy Connect flow starting with + * {@link WifiManager#startEasyConnectAsEnrolleeInitiator(String, + * Handler, + * EasyConnectStatusCallback)}. + * + * @param newNetworkId New Wi-Fi configuration with a network ID received from the configurator + */ + public abstract void onEnrolleeSuccess(int newNetworkId); + + /** + * Called when a Easy Connect success event takes place, except for when configuration is + * received from + * an external Configurator. The callback onSuccessConfigReceived will be used in this case. + * This callback marks the successful end of the current Easy Connect session, and no further + * callbacks will be called. This callback is the successful outcome of a Easy Connect flow + * starting with + * {@link WifiManager#startEasyConnectAsConfiguratorInitiator(String, int, int, Handler, + * EasyConnectStatusCallback)}. + * + * @param code Easy Connect success status code. + */ + public abstract void onConfiguratorSuccess(@EasyConnectSuccessStatusCode int code); + + /** + * Called when a Easy Connect Failure event takes place. This callback marks the unsuccessful + * end of the + * current Easy Connect session, and no further callbacks will be called. + * + * @param code Easy Connect failure status code. + */ + public abstract void onFailure(@EasyConnectFailureStatusCode int code); + + /** + * Called when Easy Connect events that indicate progress take place. Can be used by UI elements + * to show progress. + * + * @param code Easy Connect progress status code. + */ + public abstract void onProgress(@EasyConnectProgressStatusCode int code); +} diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java index af5ad512a424..840af5d5cd06 100644 --- a/wifi/java/android/net/wifi/WifiInfo.java +++ b/wifi/java/android/net/wifi/WifiInfo.java @@ -93,12 +93,22 @@ public class WifiInfo implements Parcelable { private int mRssi; /** - * Link speed in Mbps + * The unit in which links speeds are expressed. */ public static final String LINK_SPEED_UNITS = "Mbps"; private int mLinkSpeed; /** + * Tx(transmit) Link speed in Mbps + */ + private int mTxLinkSpeed; + + /** + * Rx(receive) Link speed in Mbps + */ + private int mRxLinkSpeed; + + /** * Frequency in MHz */ public static final String FREQUENCY_UNITS = "MHz"; @@ -192,6 +202,8 @@ public class WifiInfo implements Parcelable { setNetworkId(-1); setRssi(INVALID_RSSI); setLinkSpeed(-1); + setTxLinkSpeedMbps(-1); + setRxLinkSpeedMbps(-1); setFrequency(-1); setMeteredHint(false); setEphemeral(false); @@ -219,6 +231,8 @@ public class WifiInfo implements Parcelable { mNetworkId = source.mNetworkId; mRssi = source.mRssi; mLinkSpeed = source.mLinkSpeed; + mTxLinkSpeed = source.mTxLinkSpeed; + mRxLinkSpeed = source.mRxLinkSpeed; mFrequency = source.mFrequency; mIpAddress = source.mIpAddress; mMacAddress = source.mMacAddress; @@ -313,7 +327,7 @@ public class WifiInfo implements Parcelable { /** * Returns the current link speed in {@link #LINK_SPEED_UNITS}. - * @return the link speed. + * @return the link speed or -1 if there is no valid value. * @see #LINK_SPEED_UNITS */ public int getLinkSpeed() { @@ -323,7 +337,39 @@ public class WifiInfo implements Parcelable { /** @hide */ @UnsupportedAppUsage public void setLinkSpeed(int linkSpeed) { - this.mLinkSpeed = linkSpeed; + mLinkSpeed = linkSpeed; + } + + /** + * Returns the current transmit link speed in Mbps. + * @return the Tx link speed or -1 if there is no valid value. + */ + public int getTxLinkSpeedMbps() { + return mTxLinkSpeed; + } + + /** + * Update the last transmitted packet bit rate in Mbps. + * @hide + */ + public void setTxLinkSpeedMbps(int txLinkSpeed) { + mTxLinkSpeed = txLinkSpeed; + } + + /** + * Returns the current receive link speed in Mbps. + * @return the Rx link speed or -1 if there is no valid value. + */ + public int getRxLinkSpeedMbps() { + return mRxLinkSpeed; + } + + /** + * Update the last received packet bit rate in Mbps. + * @hide + */ + public void setRxLinkSpeedMbps(int rxLinkSpeed) { + mRxLinkSpeed = rxLinkSpeed; } /** @@ -529,17 +575,19 @@ public class WifiInfo implements Parcelable { StringBuffer sb = new StringBuffer(); String none = "<none>"; - sb.append("SSID: ").append(mWifiSsid == null ? WifiSsid.NONE : mWifiSsid). - append(", BSSID: ").append(mBSSID == null ? none : mBSSID). - append(", MAC: ").append(mMacAddress == null ? none : mMacAddress). - append(", Supplicant state: "). - append(mSupplicantState == null ? none : mSupplicantState). - append(", RSSI: ").append(mRssi). - append(", Link speed: ").append(mLinkSpeed).append(LINK_SPEED_UNITS). - append(", Frequency: ").append(mFrequency).append(FREQUENCY_UNITS). - append(", Net ID: ").append(mNetworkId). - append(", Metered hint: ").append(mMeteredHint). - append(", score: ").append(Integer.toString(score)); + sb.append("SSID: ").append(mWifiSsid == null ? WifiSsid.NONE : mWifiSsid) + .append(", BSSID: ").append(mBSSID == null ? none : mBSSID) + .append(", MAC: ").append(mMacAddress == null ? none : mMacAddress) + .append(", Supplicant state: ") + .append(mSupplicantState == null ? none : mSupplicantState) + .append(", RSSI: ").append(mRssi) + .append(", Link speed: ").append(mLinkSpeed).append(LINK_SPEED_UNITS) + .append(", Tx Link speed: ").append(mTxLinkSpeed).append(LINK_SPEED_UNITS) + .append(", Rx Link speed: ").append(mRxLinkSpeed).append(LINK_SPEED_UNITS) + .append(", Frequency: ").append(mFrequency).append(FREQUENCY_UNITS) + .append(", Net ID: ").append(mNetworkId) + .append(", Metered hint: ").append(mMeteredHint) + .append(", score: ").append(Integer.toString(score)); return sb.toString(); } @@ -553,6 +601,8 @@ public class WifiInfo implements Parcelable { dest.writeInt(mNetworkId); dest.writeInt(mRssi); dest.writeInt(mLinkSpeed); + dest.writeInt(mTxLinkSpeed); + dest.writeInt(mRxLinkSpeed); dest.writeInt(mFrequency); if (mIpAddress != null) { dest.writeByte((byte)1); @@ -593,6 +643,8 @@ public class WifiInfo implements Parcelable { info.setNetworkId(in.readInt()); info.setRssi(in.readInt()); info.setLinkSpeed(in.readInt()); + info.setTxLinkSpeedMbps(in.readInt()); + info.setRxLinkSpeedMbps(in.readInt()); info.setFrequency(in.readInt()); if (in.readByte() == 1) { try { diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 517bf3b47a7a..559f4ad8264e 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -4586,93 +4586,100 @@ public class WifiManager { } } - /* DPP - Device Provisioning Protocol AKA "Easy Connect" */ + /* Easy Connect - AKA Device Provisioning Protocol (DPP) */ /** - * DPP Network role: Station. + * Easy Connect Network role: Station. + * * @hide */ @SystemApi - public static final int DPP_NETWORK_ROLE_STA = 0; + public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; /** - * DPP Network role: Access Point. + * Easy Connect Network role: Access Point. + * * @hide */ @SystemApi - public static final int DPP_NETWORK_ROLE_AP = 1; + public static final int EASY_CONNECT_NETWORK_ROLE_AP = 1; /** @hide */ - @IntDef(prefix = {"DPP_NETWORK_ROLE_"}, value = { - DPP_NETWORK_ROLE_STA, - DPP_NETWORK_ROLE_AP, + @IntDef(prefix = {"EASY_CONNECT_NETWORK_ROLE_"}, value = { + EASY_CONNECT_NETWORK_ROLE_STA, + EASY_CONNECT_NETWORK_ROLE_AP, }) @Retention(RetentionPolicy.SOURCE) - public @interface DppNetworkRole {} + public @interface EasyConnectNetworkRole { + } /** - * Start DPP in Configurator-Initiator role. The current device will initiate DPP bootstrapping - * with a peer, and configure the peer with the SSID and password of the specified network using - * the DPP protocol on an encrypted link. + * Start Easy Connect (DPP) in Configurator-Initiator role. The current device will initiate + * Easy Connect bootstrapping with a peer, and configure the peer with the SSID and password of + * the specified network using the Easy Connect protocol on an encrypted link. * - * @param enrolleeUri URI of the Enrollee obtained separately (e.g. QR code scanning) - * @param selectedNetworkId Selected network ID to be sent to the peer + * @param enrolleeUri URI of the Enrollee obtained separately (e.g. QR code scanning) + * @param selectedNetworkId Selected network ID to be sent to the peer * @param enrolleeNetworkRole The network role of the enrollee - * @param callback Callback for status updates - * @param handler The handler on whose thread to execute the callbacks. Null for main thread. + * @param callback Callback for status updates + * @param handler The handler on whose thread to execute the callbacks. Null for + * main thread. * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) - public void startDppAsConfiguratorInitiator(@NonNull String enrolleeUri, - int selectedNetworkId, @DppNetworkRole int enrolleeNetworkRole, - @Nullable Handler handler, @NonNull DppStatusCallback callback) { + public void startEasyConnectAsConfiguratorInitiator(@NonNull String enrolleeUri, + int selectedNetworkId, @EasyConnectNetworkRole int enrolleeNetworkRole, + @Nullable Handler handler, @NonNull EasyConnectStatusCallback callback) { Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper(); Binder binder = new Binder(); try { mService.startDppAsConfiguratorInitiator(binder, enrolleeUri, selectedNetworkId, - enrolleeNetworkRole, new DppCallbackProxy(looper, callback)); + enrolleeNetworkRole, new EasyConnectCallbackProxy(looper, callback)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** - * Start DPP in Enrollee-Initiator role. The current device will initiate DPP bootstrapping - * with a peer, and receive the SSID and password from the peer configurator. + * Start Easy Connect (DPP) in Enrollee-Initiator role. The current device will initiate Easy + * Connect bootstrapping with a peer, and receive the SSID and password from the peer + * configurator. * * @param configuratorUri URI of the Configurator obtained separately (e.g. QR code scanning) - * @param callback Callback for status updates - * @param handler The handler on whose thread to execute the callbacks. Null for main thread. + * @param callback Callback for status updates + * @param handler The handler on whose thread to execute the callbacks. Null for main + * thread. * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) - public void startDppAsEnrolleeInitiator(@NonNull String configuratorUri, - @Nullable Handler handler, @NonNull DppStatusCallback callback) { + public void startEasyConnectAsEnrolleeInitiator(@NonNull String configuratorUri, + @Nullable Handler handler, @NonNull EasyConnectStatusCallback callback) { Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper(); Binder binder = new Binder(); try { mService.startDppAsEnrolleeInitiator(binder, configuratorUri, - new DppCallbackProxy(looper, callback)); + new EasyConnectCallbackProxy(looper, callback)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** - * Stop or abort a current DPP session. + * Stop or abort a current Easy Connect (DPP) session. + * * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) - public void stopDppSession() { + public void stopEasyConnectSession() { try { /* Request lower layers to stop/abort and clear resources */ mService.stopDppSession(); @@ -4682,48 +4689,50 @@ public class WifiManager { } /** - * Helper class to support DPP callbacks + * Helper class to support Easy Connect (DPP) callbacks + * * @hide */ @SystemApi - private static class DppCallbackProxy extends IDppCallback.Stub { + private static class EasyConnectCallbackProxy extends IDppCallback.Stub { private final Handler mHandler; - private final DppStatusCallback mDppStatusCallback; + private final EasyConnectStatusCallback mEasyConnectStatusCallback; - DppCallbackProxy(Looper looper, DppStatusCallback dppStatusCallback) { + EasyConnectCallbackProxy(Looper looper, + EasyConnectStatusCallback easyConnectStatusCallback) { mHandler = new Handler(looper); - mDppStatusCallback = dppStatusCallback; + mEasyConnectStatusCallback = easyConnectStatusCallback; } @Override public void onSuccessConfigReceived(int newNetworkId) { - Log.d(TAG, "DPP onSuccessConfigReceived callback"); + Log.d(TAG, "Easy Connect onSuccessConfigReceived callback"); mHandler.post(() -> { - mDppStatusCallback.onEnrolleeSuccess(newNetworkId); + mEasyConnectStatusCallback.onEnrolleeSuccess(newNetworkId); }); } @Override public void onSuccess(int status) { - Log.d(TAG, "DPP onSuccess callback"); + Log.d(TAG, "Easy Connect onSuccess callback"); mHandler.post(() -> { - mDppStatusCallback.onConfiguratorSuccess(status); + mEasyConnectStatusCallback.onConfiguratorSuccess(status); }); } @Override public void onFailure(int status) { - Log.d(TAG, "DPP onFailure callback"); + Log.d(TAG, "Easy Connect onFailure callback"); mHandler.post(() -> { - mDppStatusCallback.onFailure(status); + mEasyConnectStatusCallback.onFailure(status); }); } @Override public void onProgress(int status) { - Log.d(TAG, "DPP onProgress callback"); + Log.d(TAG, "Easy Connect onProgress callback"); mHandler.post(() -> { - mDppStatusCallback.onProgress(status); + mEasyConnectStatusCallback.onProgress(status); }); } } |