diff options
262 files changed, 6173 insertions, 1475 deletions
diff --git a/Android.mk b/Android.mk index 6f9680347655..bd04214e427e 100644 --- a/Android.mk +++ b/Android.mk @@ -251,6 +251,8 @@ LOCAL_SRC_FILES += \ core/java/android/os/storage/IObbActionListener.aidl \ core/java/android/security/IKeystoreService.aidl \ core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl \ + core/java/android/service/autofill/IAutoFillManagerService.aidl \ + core/java/android/service/autofill/IAutoFillService.aidl \ core/java/android/service/carrier/ICarrierService.aidl \ core/java/android/service/carrier/ICarrierMessagingCallback.aidl \ core/java/android/service/carrier/ICarrierMessagingService.aidl \ diff --git a/api/current.txt b/api/current.txt index f89488df3d4a..a267ae5afd21 100644 --- a/api/current.txt +++ b/api/current.txt @@ -18,6 +18,7 @@ package android { field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS"; field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE"; field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET"; + field public static final java.lang.String BIND_AUTO_FILL = "android.permission.BIND_AUTO_FILL"; field public static final deprecated java.lang.String BIND_CARRIER_MESSAGING_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_SERVICE"; field public static final java.lang.String BIND_CARRIER_SERVICES = "android.permission.BIND_CARRIER_SERVICES"; field public static final java.lang.String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE"; @@ -28,6 +29,7 @@ package android { field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD"; field public static final java.lang.String BIND_MIDI_DEVICE_SERVICE = "android.permission.BIND_MIDI_DEVICE_SERVICE"; field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE"; + field public static final java.lang.String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE"; field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"; field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE"; field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE"; @@ -3461,6 +3463,7 @@ package android.app { method public boolean dispatchTrackballEvent(android.view.MotionEvent); method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]); method public void enterPictureInPictureMode(); + method public void enterPictureInPictureMode(float); method public android.view.View findViewById(int); method public void finish(); method public void finishActivity(int); @@ -3632,6 +3635,7 @@ package android.app { method public void setIntent(android.content.Intent); method public final void setMediaController(android.media.session.MediaController); method public void setOverlayWithDecorCaptionEnabled(boolean); + method public void setPictureInPictureAspectRatio(float); method public final deprecated void setProgress(int); method public final deprecated void setProgressBarIndeterminate(boolean); method public final deprecated void setProgressBarIndeterminateVisibility(boolean); @@ -32078,10 +32082,10 @@ package android.provider { field public static final java.lang.String EXTRA_ERROR = "error"; field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF"; field public static final java.lang.String EXTRA_INFO = "info"; + field public static final java.lang.String EXTRA_INITIAL_URI = "android.provider.extra.INITIAL_URI"; field public static final java.lang.String EXTRA_LOADING = "loading"; field public static final java.lang.String EXTRA_ORIENTATION = "android.provider.extra.ORIENTATION"; field public static final java.lang.String EXTRA_PROMPT = "android.provider.extra.PROMPT"; - field public static final java.lang.String EXTRA_INITIAL_URI = "android.provider.extra.INITIAL_URI"; field public static final java.lang.String PROVIDER_INTERFACE = "android.content.action.DOCUMENTS_PROVIDER"; } @@ -34632,6 +34636,20 @@ package android.security.keystore { } +package android.service.autofill { + + public abstract class AutoFillService extends android.app.Service { + ctor public AutoFillService(); + method public final android.os.IBinder onBind(android.content.Intent); + method public void onNewSession(java.lang.String, android.os.Bundle, int, android.app.assist.AssistStructure); + method public void onReady(); + method public void onSessionFinished(java.lang.String); + method public void onShutdown(); + field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService"; + } + +} + package android.service.carrier { public class CarrierIdentifier implements android.os.Parcelable { @@ -34834,6 +34852,21 @@ package android.service.media { package android.service.notification { + public final class Adjustment implements android.os.Parcelable { + ctor public Adjustment(java.lang.String, java.lang.String, int, android.os.Bundle, java.lang.CharSequence, android.net.Uri, int); + ctor protected Adjustment(android.os.Parcel); + method public int describeContents(); + method public java.lang.CharSequence getExplanation(); + method public int getImportance(); + method public java.lang.String getKey(); + method public java.lang.String getPackage(); + method public android.net.Uri getReference(); + method public android.os.Bundle getSignals(); + method public int getUser(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR; + } + public final class Condition implements android.os.Parcelable { ctor public Condition(android.net.Uri, java.lang.String, int); ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int, int, int); @@ -34880,6 +34913,15 @@ package android.service.notification { field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService"; } + public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService { + ctor public NotificationAssistantService(); + method public final void adjustNotification(android.service.notification.Adjustment); + method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>); + method public final android.os.IBinder onBind(android.content.Intent); + method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean); + field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService"; + } + public abstract class NotificationListenerService extends android.app.Service { ctor public NotificationListenerService(); method public final void cancelAllNotifications(); @@ -34901,6 +34943,7 @@ package android.service.notification { method public void onNotificationRankingUpdate(android.service.notification.NotificationListenerService.RankingMap); method public void onNotificationRemoved(android.service.notification.StatusBarNotification); method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap); + method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, int); method public final void requestInterruptionFilter(int); method public final void requestListenerHints(int); method public static void requestRebind(android.content.ComponentName); @@ -34915,6 +34958,25 @@ package android.service.notification { field public static final int INTERRUPTION_FILTER_NONE = 3; // 0x3 field public static final int INTERRUPTION_FILTER_PRIORITY = 2; // 0x2 field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0 + field public static final int REASON_APP_CANCEL = 8; // 0x8 + field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9 + field public static final int REASON_CHANNEL_BANNED = 17; // 0x11 + field public static final int REASON_DELEGATE_CANCEL = 2; // 0x2 + field public static final int REASON_DELEGATE_CANCEL_ALL = 3; // 0x3 + field public static final int REASON_DELEGATE_CLICK = 1; // 0x1 + field public static final int REASON_DELEGATE_ERROR = 4; // 0x4 + field public static final int REASON_GROUP_OPTIMIZATION = 13; // 0xd + field public static final int REASON_GROUP_SUMMARY_CANCELED = 12; // 0xc + field public static final int REASON_LISTENER_CANCEL = 10; // 0xa + field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb + field public static final int REASON_PACKAGE_BANNED = 7; // 0x7 + field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5 + field public static final int REASON_PACKAGE_SUSPENDED = 14; // 0xe + field public static final int REASON_PROFILE_TURNED_OFF = 15; // 0xf + field public static final int REASON_SNOOZED = 18; // 0x12 + field public static final int REASON_UNAUTOBUNDLED = 16; // 0x10 + field public static final int REASON_USER_STOPPED = 6; // 0x6 + field public static final int REASON_USER_SWITCH = 19; // 0x13 field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService"; field public static final int SUPPRESSED_EFFECT_SCREEN_OFF = 1; // 0x1 field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 2; // 0x2 @@ -34941,7 +35003,7 @@ package android.service.notification { } public class StatusBarNotification implements android.os.Parcelable { - ctor public StatusBarNotification(java.lang.String, java.lang.String, int, java.lang.String, int, int, int, android.app.Notification, android.os.UserHandle, long); + ctor public deprecated StatusBarNotification(java.lang.String, java.lang.String, int, java.lang.String, int, int, int, android.app.Notification, android.os.UserHandle, long); ctor public StatusBarNotification(android.os.Parcel); method public android.service.notification.StatusBarNotification clone(); method public int describeContents(); @@ -34949,6 +35011,7 @@ package android.service.notification { method public int getId(); method public java.lang.String getKey(); method public android.app.Notification getNotification(); + method public android.app.NotificationChannel getNotificationChannel(); method public java.lang.String getOverrideGroupKey(); method public java.lang.String getPackageName(); method public long getPostTime(); @@ -36516,6 +36579,7 @@ package android.telecom { method public void receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile); method public void setCallDataUsage(long); field public static final int SESSION_EVENT_CAMERA_FAILURE = 5; // 0x5 + field public static final int SESSION_EVENT_CAMERA_PERMISSION_ERROR = 7; // 0x7 field public static final int SESSION_EVENT_CAMERA_READY = 6; // 0x6 field public static final int SESSION_EVENT_RX_PAUSE = 1; // 0x1 field public static final int SESSION_EVENT_RX_RESUME = 2; // 0x2 @@ -37534,6 +37598,7 @@ package android.telephony { public class TelephonyManager { method public boolean canChangeDtmfToneLength(); + method public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle); method public android.telephony.TelephonyManager createForSubscriptionId(int); method public java.util.List<android.telephony.CellInfo> getAllCellInfo(); method public int getCallState(); @@ -37556,6 +37621,7 @@ package android.telephony { method public int getNetworkType(); method public int getPhoneCount(); method public int getPhoneType(); + method public android.telephony.ServiceState getServiceState(); method public java.lang.String getSimCountryIso(); method public java.lang.String getSimOperator(); method public java.lang.String getSimOperatorName(); @@ -40510,6 +40576,45 @@ package android.util { method public abstract void setValue(T, float); } + public final class Half { + method public static short abs(short); + method public static short ceil(short); + method public static short copySign(short, short); + method public static boolean equals(short, short); + method public static short floor(short); + method public static int getExponent(short); + method public static int getSign(short); + method public static int getSignificand(short); + method public static boolean greater(short, short); + method public static boolean greaterEquals(short, short); + method public static boolean isInfinite(short); + method public static boolean isNaN(short); + method public static boolean isNormalized(short); + method public static boolean less(short, short); + method public static boolean lessEquals(short, short); + method public static short max(short, short); + method public static short min(short, short); + method public static short round(short); + method public static float toFloat(short); + method public static java.lang.String toHexString(short); + method public static java.lang.String toString(short); + method public static short trunc(short); + method public static short valueOf(float); + field public static final short EPSILON = 5120; // 0x1400 + field public static final short LOWEST_VALUE = -1025; // 0xfffffbff + field public static final int MAX_EXPONENT = 15; // 0xf + field public static final short MAX_VALUE = 31743; // 0x7bff + field public static final int MIN_EXPONENT = -14; // 0xfffffff2 + field public static final short MIN_NORMAL = 1024; // 0x400 + field public static final short MIN_VALUE = 1; // 0x1 + field public static final short NEGATIVE_INFINITY = -1024; // 0xfffffc00 + field public static final short NEGATIVE_ZERO = -32768; // 0xffff8000 + field public static final short NaN = 32256; // 0x7e00 + field public static final short POSITIVE_INFINITY = 31744; // 0x7c00 + field public static final short POSITIVE_ZERO = 0; // 0x0 + field public static final int SIZE = 16; // 0x10 + } + public abstract class IntProperty<T> extends android.util.Property { ctor public IntProperty(java.lang.String); method public final void set(T, java.lang.Integer); diff --git a/api/system-current.txt b/api/system-current.txt index baac8105ae4e..4f58ed0a1739 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -28,6 +28,7 @@ package android { field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS"; field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE"; field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET"; + field public static final java.lang.String BIND_AUTO_FILL = "android.permission.BIND_AUTO_FILL"; field public static final deprecated java.lang.String BIND_CARRIER_MESSAGING_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_SERVICE"; field public static final java.lang.String BIND_CARRIER_SERVICES = "android.permission.BIND_CARRIER_SERVICES"; field public static final java.lang.String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE"; @@ -41,6 +42,7 @@ package android { field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET"; field public static final java.lang.String BIND_MIDI_DEVICE_SERVICE = "android.permission.BIND_MIDI_DEVICE_SERVICE"; field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE"; + field public static final java.lang.String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE"; field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"; field public static final java.lang.String BIND_PRINT_RECOMMENDATION_SERVICE = "android.permission.BIND_PRINT_RECOMMENDATION_SERVICE"; field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE"; @@ -130,6 +132,7 @@ package android { field public static final java.lang.String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS"; field public static final java.lang.String MANAGE_APP_OPS_RESTRICTIONS = "android.permission.MANAGE_APP_OPS_RESTRICTIONS"; field public static final java.lang.String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS"; + field public static final java.lang.String MANAGE_AUTO_FILL = "android.permission.MANAGE_AUTO_FILL"; field public static final java.lang.String MANAGE_CA_CERTIFICATES = "android.permission.MANAGE_CA_CERTIFICATES"; field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS"; field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS"; @@ -3577,6 +3580,7 @@ package android.app { method public boolean dispatchTrackballEvent(android.view.MotionEvent); method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]); method public void enterPictureInPictureMode(); + method public void enterPictureInPictureMode(float); method public android.view.View findViewById(int); method public void finish(); method public void finishActivity(int); @@ -3750,6 +3754,7 @@ package android.app { method public void setIntent(android.content.Intent); method public final void setMediaController(android.media.session.MediaController); method public void setOverlayWithDecorCaptionEnabled(boolean); + method public void setPictureInPictureAspectRatio(float); method public final deprecated void setProgress(int); method public final deprecated void setProgressBarIndeterminate(boolean); method public final deprecated void setProgressBarIndeterminateVisibility(boolean); @@ -34765,10 +34770,10 @@ package android.provider { field public static final java.lang.String EXTRA_ERROR = "error"; field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF"; field public static final java.lang.String EXTRA_INFO = "info"; + field public static final java.lang.String EXTRA_INITIAL_URI = "android.provider.extra.INITIAL_URI"; field public static final java.lang.String EXTRA_LOADING = "loading"; field public static final java.lang.String EXTRA_ORIENTATION = "android.provider.extra.ORIENTATION"; field public static final java.lang.String EXTRA_PROMPT = "android.provider.extra.PROMPT"; - field public static final java.lang.String EXTRA_INITIAL_URI = "android.provider.extra.INITIAL_URI"; field public static final java.lang.String PROVIDER_INTERFACE = "android.content.action.DOCUMENTS_PROVIDER"; } @@ -37424,6 +37429,20 @@ package android.security.keystore { } +package android.service.autofill { + + public abstract class AutoFillService extends android.app.Service { + ctor public AutoFillService(); + method public final android.os.IBinder onBind(android.content.Intent); + method public void onNewSession(java.lang.String, android.os.Bundle, int, android.app.assist.AssistStructure); + method public void onReady(); + method public void onSessionFinished(java.lang.String); + method public void onShutdown(); + field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService"; + } + +} + package android.service.carrier { public class CarrierIdentifier implements android.os.Parcelable { @@ -37687,6 +37706,15 @@ package android.service.notification { field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService"; } + public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService { + ctor public NotificationAssistantService(); + method public final void adjustNotification(android.service.notification.Adjustment); + method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>); + method public final android.os.IBinder onBind(android.content.Intent); + method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean); + field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService"; + } + public abstract class NotificationListenerService extends android.app.Service { ctor public NotificationListenerService(); method public final void cancelAllNotifications(); @@ -37710,6 +37738,7 @@ package android.service.notification { method public void onNotificationRankingUpdate(android.service.notification.NotificationListenerService.RankingMap); method public void onNotificationRemoved(android.service.notification.StatusBarNotification); method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap); + method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, int); method public void registerAsSystemService(android.content.Context, android.content.ComponentName, int) throws android.os.RemoteException; method public final void requestInterruptionFilter(int); method public final void requestListenerHints(int); @@ -37727,6 +37756,25 @@ package android.service.notification { field public static final int INTERRUPTION_FILTER_NONE = 3; // 0x3 field public static final int INTERRUPTION_FILTER_PRIORITY = 2; // 0x2 field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0 + field public static final int REASON_APP_CANCEL = 8; // 0x8 + field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9 + field public static final int REASON_CHANNEL_BANNED = 17; // 0x11 + field public static final int REASON_DELEGATE_CANCEL = 2; // 0x2 + field public static final int REASON_DELEGATE_CANCEL_ALL = 3; // 0x3 + field public static final int REASON_DELEGATE_CLICK = 1; // 0x1 + field public static final int REASON_DELEGATE_ERROR = 4; // 0x4 + field public static final int REASON_GROUP_OPTIMIZATION = 13; // 0xd + field public static final int REASON_GROUP_SUMMARY_CANCELED = 12; // 0xc + field public static final int REASON_LISTENER_CANCEL = 10; // 0xa + field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb + field public static final int REASON_PACKAGE_BANNED = 7; // 0x7 + field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5 + field public static final int REASON_PACKAGE_SUSPENDED = 14; // 0xe + field public static final int REASON_PROFILE_TURNED_OFF = 15; // 0xf + field public static final int REASON_SNOOZED = 18; // 0x12 + field public static final int REASON_UNAUTOBUNDLED = 16; // 0x10 + field public static final int REASON_USER_STOPPED = 6; // 0x6 + field public static final int REASON_USER_SWITCH = 19; // 0x13 field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService"; field public static final int SUPPRESSED_EFFECT_SCREEN_OFF = 1; // 0x1 field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 2; // 0x2 @@ -37754,39 +37802,8 @@ package android.service.notification { field public static final android.os.Parcelable.Creator<android.service.notification.NotificationListenerService.RankingMap> CREATOR; } - public abstract class NotificationRankerService extends android.service.notification.NotificationListenerService { - ctor public NotificationRankerService(); - method public final void adjustNotification(android.service.notification.Adjustment); - method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>); - method public final android.os.IBinder onBind(android.content.Intent); - method public void onNotificationActionClick(java.lang.String, long, int); - method public void onNotificationClick(java.lang.String, long); - method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean); - method public void onNotificationRemoved(java.lang.String, long, int); - method public void onNotificationVisibilityChanged(java.lang.String, long, boolean); - field public static final int REASON_APP_CANCEL = 8; // 0x8 - field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9 - field public static final int REASON_CHANNEL_BANNED = 17; // 0x11 - field public static final int REASON_DELEGATE_CANCEL = 2; // 0x2 - field public static final int REASON_DELEGATE_CANCEL_ALL = 3; // 0x3 - field public static final int REASON_DELEGATE_CLICK = 1; // 0x1 - field public static final int REASON_DELEGATE_ERROR = 4; // 0x4 - field public static final int REASON_GROUP_OPTIMIZATION = 13; // 0xd - field public static final int REASON_GROUP_SUMMARY_CANCELED = 12; // 0xc - field public static final int REASON_LISTENER_CANCEL = 10; // 0xa - field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb - field public static final int REASON_PACKAGE_BANNED = 7; // 0x7 - field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5 - field public static final int REASON_PACKAGE_SUSPENDED = 14; // 0xe - field public static final int REASON_PROFILE_TURNED_OFF = 15; // 0xf - field public static final int REASON_SNOOZED = 18; // 0x12 - field public static final int REASON_UNAUTOBUNDLED = 16; // 0x10 - field public static final int REASON_USER_STOPPED = 6; // 0x6 - field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationRankerService"; - } - public class StatusBarNotification implements android.os.Parcelable { - ctor public StatusBarNotification(java.lang.String, java.lang.String, int, java.lang.String, int, int, int, android.app.Notification, android.os.UserHandle, long); + ctor public deprecated StatusBarNotification(java.lang.String, java.lang.String, int, java.lang.String, int, int, int, android.app.Notification, android.os.UserHandle, long); ctor public StatusBarNotification(android.os.Parcel); method public android.service.notification.StatusBarNotification clone(); method public int describeContents(); @@ -37794,6 +37811,7 @@ package android.service.notification { method public int getId(); method public java.lang.String getKey(); method public android.app.Notification getNotification(); + method public android.app.NotificationChannel getNotificationChannel(); method public java.lang.String getOverrideGroupKey(); method public java.lang.String getPackageName(); method public long getPostTime(); @@ -39449,6 +39467,7 @@ package android.telecom { method public void receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile); method public void setCallDataUsage(long); field public static final int SESSION_EVENT_CAMERA_FAILURE = 5; // 0x5 + field public static final int SESSION_EVENT_CAMERA_PERMISSION_ERROR = 7; // 0x7 field public static final int SESSION_EVENT_CAMERA_READY = 6; // 0x6 field public static final int SESSION_EVENT_RX_PAUSE = 1; // 0x1 field public static final int SESSION_EVENT_RX_RESUME = 2; // 0x2 @@ -40681,6 +40700,7 @@ package android.telephony { method public boolean canChangeDtmfToneLength(); method public int checkCarrierPrivilegesForPackage(java.lang.String); method public int checkCarrierPrivilegesForPackageAnyPhone(java.lang.String); + method public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle); method public android.telephony.TelephonyManager createForSubscriptionId(int); method public void dial(java.lang.String); method public boolean disableDataConnectivity(); @@ -40718,6 +40738,7 @@ package android.telephony { method public int getNetworkType(); method public int getPhoneCount(); method public int getPhoneType(); + method public android.telephony.ServiceState getServiceState(); method public java.lang.String getSimCountryIso(); method public java.lang.String getSimOperator(); method public java.lang.String getSimOperatorName(); @@ -43722,6 +43743,45 @@ package android.util { method public abstract void setValue(T, float); } + public final class Half { + method public static short abs(short); + method public static short ceil(short); + method public static short copySign(short, short); + method public static boolean equals(short, short); + method public static short floor(short); + method public static int getExponent(short); + method public static int getSign(short); + method public static int getSignificand(short); + method public static boolean greater(short, short); + method public static boolean greaterEquals(short, short); + method public static boolean isInfinite(short); + method public static boolean isNaN(short); + method public static boolean isNormalized(short); + method public static boolean less(short, short); + method public static boolean lessEquals(short, short); + method public static short max(short, short); + method public static short min(short, short); + method public static short round(short); + method public static float toFloat(short); + method public static java.lang.String toHexString(short); + method public static java.lang.String toString(short); + method public static short trunc(short); + method public static short valueOf(float); + field public static final short EPSILON = 5120; // 0x1400 + field public static final short LOWEST_VALUE = -1025; // 0xfffffbff + field public static final int MAX_EXPONENT = 15; // 0xf + field public static final short MAX_VALUE = 31743; // 0x7bff + field public static final int MIN_EXPONENT = -14; // 0xfffffff2 + field public static final short MIN_NORMAL = 1024; // 0x400 + field public static final short MIN_VALUE = 1; // 0x1 + field public static final short NEGATIVE_INFINITY = -1024; // 0xfffffc00 + field public static final short NEGATIVE_ZERO = -32768; // 0xffff8000 + field public static final short NaN = 32256; // 0x7e00 + field public static final short POSITIVE_INFINITY = 31744; // 0x7c00 + field public static final short POSITIVE_ZERO = 0; // 0x0 + field public static final int SIZE = 16; // 0x10 + } + public abstract class IntProperty<T> extends android.util.Property { ctor public IntProperty(java.lang.String); method public final void set(T, java.lang.Integer); diff --git a/api/test-current.txt b/api/test-current.txt index f7f5e50d89e7..ad56e8f4b056 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -18,6 +18,7 @@ package android { field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS"; field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE"; field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET"; + field public static final java.lang.String BIND_AUTO_FILL = "android.permission.BIND_AUTO_FILL"; field public static final deprecated java.lang.String BIND_CARRIER_MESSAGING_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_SERVICE"; field public static final java.lang.String BIND_CARRIER_SERVICES = "android.permission.BIND_CARRIER_SERVICES"; field public static final java.lang.String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE"; @@ -28,6 +29,7 @@ package android { field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD"; field public static final java.lang.String BIND_MIDI_DEVICE_SERVICE = "android.permission.BIND_MIDI_DEVICE_SERVICE"; field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE"; + field public static final java.lang.String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE"; field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"; field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE"; field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE"; @@ -3463,6 +3465,7 @@ package android.app { method public boolean dispatchTrackballEvent(android.view.MotionEvent); method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]); method public void enterPictureInPictureMode(); + method public void enterPictureInPictureMode(float); method public android.view.View findViewById(int); method public void finish(); method public void finishActivity(int); @@ -3634,6 +3637,7 @@ package android.app { method public void setIntent(android.content.Intent); method public final void setMediaController(android.media.session.MediaController); method public void setOverlayWithDecorCaptionEnabled(boolean); + method public void setPictureInPictureAspectRatio(float); method public final deprecated void setProgress(int); method public final deprecated void setProgressBarIndeterminate(boolean); method public final deprecated void setProgressBarIndeterminateVisibility(boolean); @@ -32164,10 +32168,10 @@ package android.provider { field public static final java.lang.String EXTRA_ERROR = "error"; field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF"; field public static final java.lang.String EXTRA_INFO = "info"; + field public static final java.lang.String EXTRA_INITIAL_URI = "android.provider.extra.INITIAL_URI"; field public static final java.lang.String EXTRA_LOADING = "loading"; field public static final java.lang.String EXTRA_ORIENTATION = "android.provider.extra.ORIENTATION"; field public static final java.lang.String EXTRA_PROMPT = "android.provider.extra.PROMPT"; - field public static final java.lang.String EXTRA_INITIAL_URI = "android.provider.extra.INITIAL_URI"; field public static final java.lang.String PROVIDER_INTERFACE = "android.content.action.DOCUMENTS_PROVIDER"; } @@ -32768,6 +32772,7 @@ package android.provider { field public static final java.lang.String ALLOWED_GEOLOCATION_ORIGINS = "allowed_geolocation_origins"; field public static final deprecated java.lang.String ALLOW_MOCK_LOCATION = "mock_location"; field public static final java.lang.String ANDROID_ID = "android_id"; + field public static final java.lang.String AUTO_FILL_SERVICE = "auto_fill_service"; field public static final deprecated java.lang.String BACKGROUND_DATA = "background_data"; field public static final deprecated java.lang.String BLUETOOTH_ON = "bluetooth_on"; field public static final android.net.Uri CONTENT_URI; @@ -34721,6 +34726,20 @@ package android.security.keystore { } +package android.service.autofill { + + public abstract class AutoFillService extends android.app.Service { + ctor public AutoFillService(); + method public final android.os.IBinder onBind(android.content.Intent); + method public void onNewSession(java.lang.String, android.os.Bundle, int, android.app.assist.AssistStructure); + method public void onReady(); + method public void onSessionFinished(java.lang.String); + method public void onShutdown(); + field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService"; + } + +} + package android.service.carrier { public class CarrierIdentifier implements android.os.Parcelable { @@ -34923,6 +34942,21 @@ package android.service.media { package android.service.notification { + public final class Adjustment implements android.os.Parcelable { + ctor public Adjustment(java.lang.String, java.lang.String, int, android.os.Bundle, java.lang.CharSequence, android.net.Uri, int); + ctor protected Adjustment(android.os.Parcel); + method public int describeContents(); + method public java.lang.CharSequence getExplanation(); + method public int getImportance(); + method public java.lang.String getKey(); + method public java.lang.String getPackage(); + method public android.net.Uri getReference(); + method public android.os.Bundle getSignals(); + method public int getUser(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR; + } + public final class Condition implements android.os.Parcelable { ctor public Condition(android.net.Uri, java.lang.String, int); ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int, int, int); @@ -34969,6 +35003,15 @@ package android.service.notification { field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService"; } + public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService { + ctor public NotificationAssistantService(); + method public final void adjustNotification(android.service.notification.Adjustment); + method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>); + method public final android.os.IBinder onBind(android.content.Intent); + method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean); + field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService"; + } + public abstract class NotificationListenerService extends android.app.Service { ctor public NotificationListenerService(); method public final void cancelAllNotifications(); @@ -34990,6 +35033,7 @@ package android.service.notification { method public void onNotificationRankingUpdate(android.service.notification.NotificationListenerService.RankingMap); method public void onNotificationRemoved(android.service.notification.StatusBarNotification); method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap); + method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, int); method public final void requestInterruptionFilter(int); method public final void requestListenerHints(int); method public static void requestRebind(android.content.ComponentName); @@ -35004,6 +35048,25 @@ package android.service.notification { field public static final int INTERRUPTION_FILTER_NONE = 3; // 0x3 field public static final int INTERRUPTION_FILTER_PRIORITY = 2; // 0x2 field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0 + field public static final int REASON_APP_CANCEL = 8; // 0x8 + field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9 + field public static final int REASON_CHANNEL_BANNED = 17; // 0x11 + field public static final int REASON_DELEGATE_CANCEL = 2; // 0x2 + field public static final int REASON_DELEGATE_CANCEL_ALL = 3; // 0x3 + field public static final int REASON_DELEGATE_CLICK = 1; // 0x1 + field public static final int REASON_DELEGATE_ERROR = 4; // 0x4 + field public static final int REASON_GROUP_OPTIMIZATION = 13; // 0xd + field public static final int REASON_GROUP_SUMMARY_CANCELED = 12; // 0xc + field public static final int REASON_LISTENER_CANCEL = 10; // 0xa + field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb + field public static final int REASON_PACKAGE_BANNED = 7; // 0x7 + field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5 + field public static final int REASON_PACKAGE_SUSPENDED = 14; // 0xe + field public static final int REASON_PROFILE_TURNED_OFF = 15; // 0xf + field public static final int REASON_SNOOZED = 18; // 0x12 + field public static final int REASON_UNAUTOBUNDLED = 16; // 0x10 + field public static final int REASON_USER_STOPPED = 6; // 0x6 + field public static final int REASON_USER_SWITCH = 19; // 0x13 field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService"; field public static final int SUPPRESSED_EFFECT_SCREEN_OFF = 1; // 0x1 field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 2; // 0x2 @@ -35030,7 +35093,7 @@ package android.service.notification { } public class StatusBarNotification implements android.os.Parcelable { - ctor public StatusBarNotification(java.lang.String, java.lang.String, int, java.lang.String, int, int, int, android.app.Notification, android.os.UserHandle, long); + ctor public deprecated StatusBarNotification(java.lang.String, java.lang.String, int, java.lang.String, int, int, int, android.app.Notification, android.os.UserHandle, long); ctor public StatusBarNotification(android.os.Parcel); method public android.service.notification.StatusBarNotification clone(); method public int describeContents(); @@ -35038,6 +35101,7 @@ package android.service.notification { method public int getId(); method public java.lang.String getKey(); method public android.app.Notification getNotification(); + method public android.app.NotificationChannel getNotificationChannel(); method public java.lang.String getOverrideGroupKey(); method public java.lang.String getPackageName(); method public long getPostTime(); @@ -36605,6 +36669,7 @@ package android.telecom { method public void receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile); method public void setCallDataUsage(long); field public static final int SESSION_EVENT_CAMERA_FAILURE = 5; // 0x5 + field public static final int SESSION_EVENT_CAMERA_PERMISSION_ERROR = 7; // 0x7 field public static final int SESSION_EVENT_CAMERA_READY = 6; // 0x6 field public static final int SESSION_EVENT_RX_PAUSE = 1; // 0x1 field public static final int SESSION_EVENT_RX_RESUME = 2; // 0x2 @@ -37623,6 +37688,7 @@ package android.telephony { public class TelephonyManager { method public boolean canChangeDtmfToneLength(); + method public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle); method public android.telephony.TelephonyManager createForSubscriptionId(int); method public java.util.List<android.telephony.CellInfo> getAllCellInfo(); method public int getCallState(); @@ -37645,6 +37711,7 @@ package android.telephony { method public int getNetworkType(); method public int getPhoneCount(); method public int getPhoneType(); + method public android.telephony.ServiceState getServiceState(); method public java.lang.String getSimCountryIso(); method public java.lang.String getSimOperator(); method public java.lang.String getSimOperatorName(); @@ -40602,6 +40669,45 @@ package android.util { method public abstract void setValue(T, float); } + public final class Half { + method public static short abs(short); + method public static short ceil(short); + method public static short copySign(short, short); + method public static boolean equals(short, short); + method public static short floor(short); + method public static int getExponent(short); + method public static int getSign(short); + method public static int getSignificand(short); + method public static boolean greater(short, short); + method public static boolean greaterEquals(short, short); + method public static boolean isInfinite(short); + method public static boolean isNaN(short); + method public static boolean isNormalized(short); + method public static boolean less(short, short); + method public static boolean lessEquals(short, short); + method public static short max(short, short); + method public static short min(short, short); + method public static short round(short); + method public static float toFloat(short); + method public static java.lang.String toHexString(short); + method public static java.lang.String toString(short); + method public static short trunc(short); + method public static short valueOf(float); + field public static final short EPSILON = 5120; // 0x1400 + field public static final short LOWEST_VALUE = -1025; // 0xfffffbff + field public static final int MAX_EXPONENT = 15; // 0xf + field public static final short MAX_VALUE = 31743; // 0x7bff + field public static final int MIN_EXPONENT = -14; // 0xfffffff2 + field public static final short MIN_NORMAL = 1024; // 0x400 + field public static final short MIN_VALUE = 1; // 0x1 + field public static final short NEGATIVE_INFINITY = -1024; // 0xfffffc00 + field public static final short NEGATIVE_ZERO = -32768; // 0xffff8000 + field public static final short NaN = 32256; // 0x7e00 + field public static final short POSITIVE_INFINITY = 31744; // 0x7c00 + field public static final short POSITIVE_ZERO = 0; // 0x0 + field public static final int SIZE = 16; // 0x10 + } + public abstract class IntProperty<T> extends android.util.Property { ctor public IntProperty(java.lang.String); method public final void set(T, java.lang.Integer); diff --git a/compiled-classes-phone b/compiled-classes-phone index 8428e41fab4a..33e3e0437aa6 100644 --- a/compiled-classes-phone +++ b/compiled-classes-phone @@ -3610,9 +3610,9 @@ android.service.notification.NotificationListenerService$NotificationListenerWra android.service.notification.NotificationListenerService$Ranking android.service.notification.NotificationListenerService$RankingMap android.service.notification.NotificationListenerService$RankingMap$1 -android.service.notification.NotificationRankerService -android.service.notification.NotificationRankerService$MyHandler -android.service.notification.NotificationRankerService$NotificationRankingServiceWrapper +android.service.notification.NotificationAssistantService +android.service.notification.NotificationAssistantService$MyHandler +android.service.notification.NotificationAssistantService$NotificationRankingServiceWrapper android.service.notification.NotificationRankingUpdate android.service.notification.NotificationRankingUpdate$1 android.service.notification.StatusBarNotification diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java index 0552d34d5215..58f5a787a355 100644 --- a/core/java/android/app/ActionBar.java +++ b/core/java/android/app/ActionBar.java @@ -1494,22 +1494,22 @@ public abstract class ActionBar { public void onFocusChange(View v, boolean hasFocus) { if (!hasFocus) { v.setOnFocusChangeListener(null); - final View focused = mFocusRoot.findFocus(); - if (focused != null) { - focused.setOnFocusChangeListener(this); - } else { - mFocusRoot.post(this); - } + mFocusRoot.post(this); } } @Override public void run() { - if (mContainer != null) { - mContainer.setTouchscreenBlocksFocus(true); - } - if (mToolbar != null) { - mToolbar.setTouchscreenBlocksFocus(true); + final View focused = mFocusRoot.findFocus(); + if (focused != null) { + focused.setOnFocusChangeListener(this); + } else { + if (mContainer != null) { + mContainer.setTouchscreenBlocksFocus(true); + } + if (mToolbar != null) { + mToolbar.setTouchscreenBlocksFocus(true); + } } } } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index b3e2f57e2b33..b38133901b30 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1929,6 +1929,32 @@ public class Activity extends ContextThemeWrapper } /** + * Puts the activity in picture-in-picture mode with a given aspect ratio. + * @see android.R.attr#supportsPictureInPicture + * + * @param aspectRatio the new aspect ratio of the picture-in-picture. + */ + public void enterPictureInPictureMode(float aspectRatio) { + try { + ActivityManagerNative.getDefault().enterPictureInPictureModeWithAspectRatio(mToken, + aspectRatio); + } catch (RemoteException e) { + } + } + + /** + * Updates the aspect ratio of the current picture-in-picture activity. + * + * @param aspectRatio the new aspect ratio of the picture-in-picture. + */ + public void setPictureInPictureAspectRatio(float aspectRatio) { + try { + ActivityManagerNative.getDefault().setPictureInPictureAspectRatio(mToken, aspectRatio); + } catch (RemoteException e) { + } + } + + /** * Called by the system when the device configuration changes while your * activity is running. Note that this will <em>only</em> be called if * you have selected configurations you would like to handle with the diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index df970a47de21..4a39e4a4cb50 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -102,7 +102,7 @@ public class ActivityManager { } @Override - public void onUidGone(int uid) { + public void onUidGone(int uid, boolean disabled) { mListener.onUidImportance(uid, RunningAppProcessInfo.IMPORTANCE_GONE); } @@ -111,7 +111,7 @@ public class ActivityManager { } @Override - public void onUidIdle(int uid) { + public void onUidIdle(int uid, boolean disabled) { } } diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index e56fe0f20982..38e35722604c 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -194,4 +194,9 @@ public abstract class ActivityManagerInternal { * @return {@code true} if system is ready, {@code false} otherwise. */ public abstract boolean isSystemReady(); + + /** + * Called when the trusted state of Keyguard has changed. + */ + public abstract void notifyKeyguardTrustedChanged(); } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 3e8d90bf62ed..a3414f458ef3 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -80,6 +80,11 @@ import android.os.SystemProperties; import android.os.Trace; import android.os.TransactionTooLargeException; import android.os.UserHandle; +import android.provider.BlockedNumberContract; +import android.provider.CalendarContract; +import android.provider.CallLog; +import android.provider.ContactsContract; +import android.provider.Downloads; import android.provider.Settings; import android.security.NetworkSecurityPolicy; import android.security.net.config.NetworkSecurityConfigProvider; @@ -5823,6 +5828,22 @@ public final class ActivityThread { final String auths[] = holder.info.authority.split(";"); final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid); + if (provider != null) { + // If this provider is hosted by the core OS and cannot be upgraded, + // then I guess we're okay doing blocking calls to it. + for (String auth : auths) { + switch (auth) { + case ContactsContract.AUTHORITY: + case CallLog.AUTHORITY: + case CallLog.SHADOW_AUTHORITY: + case BlockedNumberContract.AUTHORITY: + case CalendarContract.AUTHORITY: + case Downloads.Impl.AUTHORITY: + Binder.allowBlocking(provider.asBinder()); + } + } + } + final ProviderClientRecord pcr = new ProviderClientRecord( auths, provider, localProvider, holder); for (String auth : auths) { @@ -5971,7 +5992,6 @@ public final class ActivityThread { retHolder = prc.holder; } } - return retHolder; } diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 80267205de83..9fe34c0e39bc 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -499,7 +499,7 @@ interface IActivityManager { boolean supportsLocalVoiceInteraction() = 365; void notifyPinnedStackAnimationEnded() = 366; void removeStack(int stackId) = 367; - void setLenientBackgroundCheck(boolean enabled) = 368; + void makePackageIdle(String packageName, int userId) = 368; int getMemoryTrimLevel() = 369; /** * Resizes the pinned stack. @@ -562,7 +562,9 @@ interface IActivityManager { boolean updateDisplayOverrideConfiguration(in Configuration values, int displayId) = 401; void unregisterTaskStackListener(ITaskStackListener listener) = 402; void moveStackToDisplay(int stackId, int displayId) = 403; + void enterPictureInPictureModeWithAspectRatio(in IBinder token, float aspectRatio) = 404; + void setPictureInPictureAspectRatio(in IBinder token, float aspectRatio) = 405; // Please keep these transaction codes the same -- they are also // sent by C++ code. when a new method is added, use the next available transaction id. -}
\ No newline at end of file +} diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index ee81c582cc5d..15b99c6e4f7f 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -94,8 +94,8 @@ interface INotificationManager void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim); void setInterruptionFilter(String pkg, int interruptionFilter); - void applyAdjustmentFromRankerService(in INotificationListener token, in Adjustment adjustment); - void applyAdjustmentsFromRankerService(in INotificationListener token, in List<Adjustment> adjustments); + void applyAdjustmentFromAssistantService(in INotificationListener token, in Adjustment adjustment); + void applyAdjustmentsFromAssistantService(in INotificationListener token, in List<Adjustment> adjustments); ComponentName getEffectsSuppressor(); boolean matchesCallFilter(in Bundle extras); diff --git a/core/java/android/app/IUiModeManager.aidl b/core/java/android/app/IUiModeManager.aidl index cae54b6c0611..848464cf3823 100644 --- a/core/java/android/app/IUiModeManager.aidl +++ b/core/java/android/app/IUiModeManager.aidl @@ -53,6 +53,16 @@ interface IUiModeManager { int getNightMode(); /** + * Sets whith theme overlays to use within /vendor/overlay. + */ + void setTheme(String theme); + + /** + * Gets whith theme overlays to use within /vendor/overlay. + */ + String getTheme(); + + /** * Tells if UI mode is locked or not. */ boolean isUiModeLocked(); diff --git a/core/java/android/app/IUidObserver.aidl b/core/java/android/app/IUidObserver.aidl index fa8d0c95e054..64cb9b17f923 100644 --- a/core/java/android/app/IUidObserver.aidl +++ b/core/java/android/app/IUidObserver.aidl @@ -26,7 +26,7 @@ oneway interface IUidObserver { /** * Report that there are no longer any processes running for a uid. */ - void onUidGone(int uid); + void onUidGone(int uid, boolean disabled); /** * Report that a uid is now active (no longer idle). @@ -37,5 +37,5 @@ oneway interface IUidObserver { * Report that a uid is idle -- it has either been running in the background for * a sufficient period of time, or all of its processes have gone away. */ - void onUidIdle(int uid); + void onUidIdle(int uid, boolean disabled); } diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 2d15beb1486f..45831a303bec 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -826,7 +826,8 @@ public class ResourcesManager { for (int i = mResourceImpls.size() - 1; i >= 0; i--) { ResourcesKey key = mResourceImpls.keyAt(i); - ResourcesImpl r = mResourceImpls.valueAt(i).get(); + WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i); + ResourcesImpl r = weakImplRef != null ? weakImplRef.get() : null; if (r != null) { if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources " + r + " config to: " + config); @@ -890,8 +891,9 @@ public class ResourcesManager { final int implCount = mResourceImpls.size(); for (int i = 0; i < implCount; i++) { - final ResourcesImpl impl = mResourceImpls.valueAt(i).get(); final ResourcesKey key = mResourceImpls.keyAt(i); + final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i); + final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null; if (impl != null && key.mResDir.equals(assetPath)) { if (!ArrayUtils.contains(key.mLibDirs, libAsset)) { final int newLibAssetCount = 1 + diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index 0046b0e4a8bf..2e2172919dbc 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -241,6 +241,35 @@ public class UiModeManager { } /** + * Sets the vendor theme overlay property, then triggers a reboot. + * @hide + */ + public void setTheme(String theme) { + if (mService != null) { + try { + mService.setTheme(theme); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Gets the vendor theme overlay property. + * @hide + */ + public String getTheme() { + if (mService != null) { + try { + return mService.getTheme(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return null; + } + + /** * Returns the currently configured night mode. * <p> * May be one of: diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java index 353c6400ffd7..3de159a6cff2 100644 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ b/core/java/android/bluetooth/BluetoothA2dp.java @@ -25,6 +25,7 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.media.AudioManager; +import android.os.Binder; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; @@ -572,7 +573,7 @@ public final class BluetoothA2dp implements BluetoothProfile { if (DBG) Log.d(TAG, "Proxy object connected"); try { mServiceLock.writeLock().lock(); - mService = IBluetoothA2dp.Stub.asInterface(service); + mService = IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service)); } finally { mServiceLock.writeLock().unlock(); } diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java index 74302f27ec1d..9dfc4b442fa4 100755 --- a/core/java/android/bluetooth/BluetoothA2dpSink.java +++ b/core/java/android/bluetooth/BluetoothA2dpSink.java @@ -20,6 +20,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -481,7 +482,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothA2dpSink.Stub.asInterface(service); + mService = IBluetoothA2dpSink.Stub.asInterface(Binder.allowBlocking(service)); if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.A2DP_SINK, diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java index a395aa470f5b..0261b1b35e2d 100644 --- a/core/java/android/bluetooth/BluetoothAvrcpController.java +++ b/core/java/android/bluetooth/BluetoothAvrcpController.java @@ -22,6 +22,7 @@ import android.content.Intent; import android.content.ServiceConnection; import android.media.MediaMetadata; import android.media.session.PlaybackState; +import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -284,7 +285,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothAvrcpController.Stub.asInterface(service); + mService = IBluetoothAvrcpController.Stub.asInterface(Binder.allowBlocking(service)); if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.AVRCP_CONTROLLER, diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index f46a3b3c733a..28421ebc4cca 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -20,6 +20,7 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.ComponentName; import android.content.Context; +import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -1037,7 +1038,7 @@ public final class BluetoothHeadset implements BluetoothProfile { @Override public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothHeadset.Stub.asInterface(service); + mService = IBluetoothHeadset.Stub.asInterface(Binder.allowBlocking(service)); mHandler.sendMessage(mHandler.obtainMessage( MESSAGE_HEADSET_SERVICE_CONNECTED)); } diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java index 93790feecd11..c7c64c4391c2 100644 --- a/core/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java @@ -20,6 +20,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; @@ -1122,7 +1123,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothHeadsetClient.Stub.asInterface(service); + mService = IBluetoothHeadsetClient.Stub.asInterface(Binder.allowBlocking(service)); if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.HEADSET_CLIENT, diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java index 4949c24372a4..8d77888193b6 100644 --- a/core/java/android/bluetooth/BluetoothHealth.java +++ b/core/java/android/bluetooth/BluetoothHealth.java @@ -20,6 +20,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Binder; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -522,7 +523,7 @@ public final class BluetoothHealth implements BluetoothProfile { private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothHealth.Stub.asInterface(service); + mService = IBluetoothHealth.Stub.asInterface(Binder.allowBlocking(service)); if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.HEALTH, BluetoothHealth.this); diff --git a/core/java/android/bluetooth/BluetoothInputDevice.java b/core/java/android/bluetooth/BluetoothInputDevice.java index 252e3d28a25e..e3288f3c7c88 100644 --- a/core/java/android/bluetooth/BluetoothInputDevice.java +++ b/core/java/android/bluetooth/BluetoothInputDevice.java @@ -22,6 +22,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -479,7 +480,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothInputDevice.Stub.asInterface(service); + mService = IBluetoothInputDevice.Stub.asInterface(Binder.allowBlocking(service)); if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.INPUT_DEVICE, BluetoothInputDevice.this); diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java index 7f57acf3dc63..2e73051ee610 100644 --- a/core/java/android/bluetooth/BluetoothMap.java +++ b/core/java/android/bluetooth/BluetoothMap.java @@ -371,7 +371,7 @@ public final class BluetoothMap implements BluetoothProfile { private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) log("Proxy object connected"); - mService = IBluetoothMap.Stub.asInterface(service); + mService = IBluetoothMap.Stub.asInterface(Binder.allowBlocking(service)); if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.MAP, BluetoothMap.this); } diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java index 744f9421b239..2a026a91e8e6 100644 --- a/core/java/android/bluetooth/BluetoothPan.java +++ b/core/java/android/bluetooth/BluetoothPan.java @@ -22,6 +22,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -368,7 +369,7 @@ public final class BluetoothPan implements BluetoothProfile { private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "BluetoothPAN Proxy object connected"); - mPanService = IBluetoothPan.Stub.asInterface(service); + mPanService = IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service)); if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.PAN, diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java index eab4c6f5130c..9f00e1aaa3a1 100644 --- a/core/java/android/bluetooth/BluetoothPbapClient.java +++ b/core/java/android/bluetooth/BluetoothPbapClient.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.RemoteException; +import android.os.Binder; import android.os.IBinder; import android.util.Log; @@ -288,7 +289,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("Proxy object connected"); } - mService = IBluetoothPbapClient.Stub.asInterface(service); + mService = IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service)); if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.PBAP_CLIENT, BluetoothPbapClient.this); } diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java index e70c936e253e..89c1bf8f9aa2 100644 --- a/core/java/android/bluetooth/BluetoothSap.java +++ b/core/java/android/bluetooth/BluetoothSap.java @@ -24,6 +24,7 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.RemoteException; +import android.os.Binder; import android.os.IBinder; import android.os.ServiceManager; import android.util.Log; @@ -393,7 +394,7 @@ public final class BluetoothSap implements BluetoothProfile { private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) log("Proxy object connected"); - mService = IBluetoothSap.Stub.asInterface(service); + mService = IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service)); if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.SAP, BluetoothSap.this); } diff --git a/core/java/android/bluetooth/IBluetoothHeadset.aidl b/core/java/android/bluetooth/IBluetoothHeadset.aidl index 6ad442b6138d..dde0ac67bbb5 100755 --- a/core/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/core/java/android/bluetooth/IBluetoothHeadset.aidl @@ -54,7 +54,7 @@ interface IBluetoothHeadset { boolean getAudioRouteAllowed(); boolean startScoUsingVirtualVoiceCall(in BluetoothDevice device); boolean stopScoUsingVirtualVoiceCall(in BluetoothDevice device); - void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type); + oneway void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type); void clccResponse(int index, int direction, int status, int mode, boolean mpty, String number, int type); boolean enableWBS(); diff --git a/core/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl b/core/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl index 96c59e239151..541583ff5535 100755 --- a/core/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl +++ b/core/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl @@ -24,7 +24,7 @@ import android.os.IBinder; * * {@hide} */ -interface IBluetoothProfileServiceConnection { +oneway interface IBluetoothProfileServiceConnection { void onServiceConnected(in ComponentName comp, in IBinder service); void onServiceDisconnected(in ComponentName comp); } diff --git a/core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl b/core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl index feccdce57b98..0da4e8843282 100644 --- a/core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl +++ b/core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl @@ -21,7 +21,7 @@ package android.bluetooth; * * {@hide} */ -interface IBluetoothStateChangeCallback +oneway interface IBluetoothStateChangeCallback { void onBluetoothStateChange(boolean on); } diff --git a/core/java/android/bluetooth/OobData.java b/core/java/android/bluetooth/OobData.java index f53ca94a1d5c..9e87230c686e 100644 --- a/core/java/android/bluetooth/OobData.java +++ b/core/java/android/bluetooth/OobData.java @@ -30,10 +30,24 @@ import android.util.Log; * @hide */ public class OobData implements Parcelable { + private byte[] leBluetoothDeviceAddress; private byte[] securityManagerTk; private byte[] leSecureConnectionsConfirmation; private byte[] leSecureConnectionsRandom; + public byte[] getLeBluetoothDeviceAddress() { + return leBluetoothDeviceAddress; + } + + /** + * Sets the LE Bluetooth Device Address value to be used during LE pairing. + * The value shall be 7 bytes. Please see Bluetooth CSSv6, Part A 1.16 for + * a detailed description. + */ + public void setLeBluetoothDeviceAddress(byte[] leBluetoothDeviceAddress) { + this.leBluetoothDeviceAddress = leBluetoothDeviceAddress; + } + public byte[] getSecurityManagerTk() { return securityManagerTk; } @@ -66,6 +80,7 @@ public class OobData implements Parcelable { public OobData() { } private OobData(Parcel in) { + leBluetoothDeviceAddress = in.createByteArray(); securityManagerTk = in.createByteArray(); leSecureConnectionsConfirmation = in.createByteArray(); leSecureConnectionsRandom = in.createByteArray(); @@ -77,6 +92,7 @@ public class OobData implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { + out.writeByteArray(leBluetoothDeviceAddress); out.writeByteArray(securityManagerTk); out.writeByteArray(leSecureConnectionsConfirmation); out.writeByteArray(leSecureConnectionsRandom); diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java index 4769bd02a5dc..439e1ff48c17 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -422,6 +422,7 @@ final class ContentProviderProxy implements IContentProvider if (reply.readInt() != 0) { BulkCursorDescriptor d = BulkCursorDescriptor.CREATOR.createFromParcel(reply); + Binder.copyAllowBlocking(mRemote, (d.cursor != null) ? d.cursor.asBinder() : null); adaptor.initialize(d); } else { adaptor.close(); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 322cc7b9becc..3964e0a8bbd9 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3338,6 +3338,14 @@ public abstract class Context { public static final String VOICE_INTERACTION_MANAGER_SERVICE = "voiceinteraction"; /** + * Official published name of the (internal) auto-fill service. + * + * @hide + * @see #getSystemService + */ + public static final String AUTO_FILL_MANAGER_SERVICE = "autofill"; + + /** * Use with {@link #getSystemService} to access the * {@link com.android.server.voiceinteraction.SoundTriggerService}. * diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 1f013ae02a87..2aa7ac6ff5b6 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -162,6 +162,12 @@ public abstract class PackageManagerInternal { public abstract boolean isPackageDataProtected(int userId, String packageName); /** + * Returns {@code true} if a given package is installed as ephemeral. Otherwise, returns + * {@code false}. + */ + public abstract boolean isPackageEphemeral(int userId, String packageName); + + /** * Gets whether the package was ever launched. * @param packageName The package name. * @param userId The user for which to check. diff --git a/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl b/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl index d2c3d7555e70..3fe645c59a30 100644 --- a/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl +++ b/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl @@ -25,7 +25,7 @@ import android.hardware.location.IActivityRecognitionHardware; * * @hide */ -interface IActivityRecognitionHardwareClient { +oneway interface IActivityRecognitionHardwareClient { /** * Hardware Activity-Recognition availability event. * diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index 7b7533b16410..01369791fd38 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -75,36 +75,35 @@ public class Binder implements IBinder { /** * Control whether dump() calls are allowed. */ - private static String sDumpDisabled = null; + private static volatile String sDumpDisabled = null; /** * Global transaction tracker instance for this process. */ - private static TransactionTracker sTransactionTracker = null; + private static volatile TransactionTracker sTransactionTracker = null; // Transaction tracking code. /** * Flag indicating whether we should be tracing transact calls. - * */ - private static boolean sTracingEnabled = false; + private static volatile boolean sTracingEnabled = false; /** * Enable Binder IPC tracing. * * @hide */ - public static void enableTracing() { + public static void enableTracing() { sTracingEnabled = true; - }; + } /** * Disable Binder IPC tracing. * * @hide */ - public static void disableTracing() { + public static void disableTracing() { sTracingEnabled = false; } @@ -128,6 +127,59 @@ public class Binder implements IBinder { return sTransactionTracker; } + /** {@hide} */ + static volatile boolean sWarnOnBlocking = false; + + /** + * Warn if any blocking binder transactions are made out from this process. + * This is typically only useful for the system process, to prevent it from + * blocking on calls to external untrusted code. Instead, all outgoing calls + * that require a result must be sent as {@link IBinder#FLAG_ONEWAY} calls + * which deliver results through a callback interface. + * + * @hide + */ + public static void setWarnOnBlocking(boolean warnOnBlocking) { + sWarnOnBlocking = warnOnBlocking; + } + + /** + * Allow blocking calls on the given interface, overriding the requested + * value of {@link #setWarnOnBlocking(boolean)}. + * <p> + * This should only be rarely called when you are <em>absolutely sure</em> + * the remote interface is a built-in system component that can never be + * upgraded. In particular, this <em>must never</em> be called for + * interfaces hosted by package that could be upgraded or replaced, + * otherwise you risk system instability if that remote interface wedges. + * + * @hide + */ + public static IBinder allowBlocking(IBinder binder) { + try { + if (binder instanceof BinderProxy) { + ((BinderProxy) binder).mWarnOnBlocking = false; + } else if (binder != null + && binder.queryLocalInterface(binder.getInterfaceDescriptor()) == null) { + Log.w(TAG, "Unable to allow blocking on interface " + binder); + } + } catch (RemoteException ignored) { + } + return binder; + } + + /** + * Inherit the current {@link #allowBlocking(IBinder)} value from one given + * interface to another. + * + * @hide + */ + public static void copyAllowBlocking(IBinder fromBinder, IBinder toBinder) { + if (fromBinder instanceof BinderProxy && toBinder instanceof BinderProxy) { + ((BinderProxy) toBinder).mWarnOnBlocking = ((BinderProxy) fromBinder).mWarnOnBlocking; + } + } + /* mObject is used by native code, do not remove or rename */ private long mObject; private IInterface mOwner; @@ -322,9 +374,7 @@ public class Binder implements IBinder { * re-enabled. */ public static void setDumpDisabled(String msg) { - synchronized (Binder.class) { - sDumpDisabled = msg; - } + sDumpDisabled = msg; } /** @@ -400,10 +450,7 @@ public class Binder implements IBinder { } void doDump(FileDescriptor fd, PrintWriter pw, String[] args) { - final String disabled; - synchronized (Binder.class) { - disabled = sDumpDisabled; - } + final String disabled = sDumpDisabled; if (disabled == null) { try { dump(fd, pw, args); @@ -612,6 +659,9 @@ public class Binder implements IBinder { } final class BinderProxy implements IBinder { + // Assume the process-wide default value when created + volatile boolean mWarnOnBlocking = Binder.sWarnOnBlocking; + public native boolean pingBinder(); public native boolean isBinderAlive(); @@ -621,6 +671,15 @@ final class BinderProxy implements IBinder { public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { Binder.checkParcel(this, code, data, "Unreasonably large binder buffer"); + + if (mWarnOnBlocking && ((flags & FLAG_ONEWAY) == 0)) { + // For now, avoid spamming the log by disabling after we've logged + // about this interface at least once + mWarnOnBlocking = false; + Log.w(Binder.TAG, "Outgoing transactions from this process must be FLAG_ONEWAY", + new Throwable()); + } + final boolean tracingEnabled = Binder.isTracingEnabled(); if (tracingEnabled) { final Throwable tr = new Throwable(); diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java index 640105ce8f98..e11494d5cd41 100644 --- a/core/java/android/os/ServiceManager.java +++ b/core/java/android/os/ServiceManager.java @@ -36,7 +36,8 @@ public final class ServiceManager { } // Find the service manager - sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject()); + sServiceManager = ServiceManagerNative + .asInterface(Binder.allowBlocking(BinderInternal.getContextObject())); return sServiceManager; } @@ -52,7 +53,7 @@ public final class ServiceManager { if (service != null) { return service; } else { - return getIServiceManager().getService(name); + return Binder.allowBlocking(getIServiceManager().getService(name)); } } catch (RemoteException e) { Log.e(TAG, "error in getService", e); @@ -117,7 +118,7 @@ public final class ServiceManager { if (service != null) { return service; } else { - return getIServiceManager().checkService(name); + return Binder.allowBlocking(getIServiceManager().checkService(name)); } } catch (RemoteException e) { Log.e(TAG, "error in checkService", e); diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java index b826584009c1..a280e594525e 100644 --- a/core/java/android/provider/Downloads.java +++ b/core/java/android/provider/Downloads.java @@ -41,6 +41,8 @@ public final class Downloads { public static final class Impl implements BaseColumns { private Impl() {} + public static final String AUTHORITY = "downloads"; + /** * The permission to access the download manager */ diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 22336c603d2b..79b42ba4c675 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4715,6 +4715,13 @@ public final class Settings { public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service"; /** + * The currently selected auto-fill service flattened ComponentName. + * @hide + */ + @TestApi + public static final String AUTO_FILL_SERVICE = "auto_fill_service"; + + /** * bluetooth HCI snoop log configuration * @hide */ @@ -6114,6 +6121,15 @@ public final class Settings { public static final String ASSIST_DISCLOSURE_ENABLED = "assist_disclosure_enabled"; /** + * Name of the service components that the current user has explicitly allowed to + * see and assist with all of the user's notifications. + * + * @hide + */ + public static final String ENABLED_NOTIFICATION_ASSISTANT = + "enabled_notification_assistant"; + + /** * Names of the service components that the current user has explicitly allowed to * see all of the user's notifications, separated by ':'. * @@ -8613,13 +8629,6 @@ public final class Settings { public static final String ALWAYS_FINISH_ACTIVITIES = "always_finish_activities"; /** - * @hide - * If not 0, the activity manager will implement a looser version of background - * check that is more compatible with existing apps. - */ - public static final String LENIENT_BACKGROUND_CHECK = "lenient_background_check"; - - /** * Use Dock audio output for media: * 0 = disabled * 1 = enabled diff --git a/core/java/android/service/autofill/AutoFillService.java b/core/java/android/service/autofill/AutoFillService.java new file mode 100644 index 000000000000..14ce009bedc6 --- /dev/null +++ b/core/java/android/service/autofill/AutoFillService.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.service.autofill; + +import android.annotation.SdkConstant; +import android.app.Service; +import android.app.assist.AssistStructure; +import android.content.Intent; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.util.Log; + +import com.android.internal.os.HandlerCaller; +import com.android.internal.os.SomeArgs; + +/** + * Top-level service of the current auto-fill service for a given user. + */ +// TODO: expand documentation +public abstract class AutoFillService extends Service { + + private static final String TAG = "AutoFillService"; + private static final boolean DEBUG = true; // TODO: set to false once stable + + /** + * The {@link Intent} that must be declared as handled by the service. + * To be supported, the service must also require the + * {@link android.Manifest.permission#BIND_AUTO_FILL} permission so + * that other applications can not abuse it. + */ + @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) + public static final String SERVICE_INTERFACE = "android.service.autofill.AutoFillService"; + + private static final int MSG_READY = 1; + private static final int MSG_NEW_SESSION = 2; + private static final int MSG_SESSION_FINISHED = 3; + private static final int MSG_SHUTDOWN = 4; + + // TODO: add metadata? + + private final IAutoFillService mInterface = new IAutoFillService.Stub() { + @Override + public void ready() { + mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_READY)); + } + + @Override + public void newSession(String token, Bundle data, int flags, + AssistStructure structure) { + mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOOO(MSG_NEW_SESSION, + flags, token, data, structure)); + } + + @Override + public void finishSession(String token) { + mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_SESSION_FINISHED, token)); + } + + @Override + public void shutdown() { + mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_SHUTDOWN)); + } + }; + + private final HandlerCaller.Callback mHandlerCallback = new HandlerCaller.Callback() { + + @Override + public void executeMessage(Message msg) { + switch (msg.what) { + case MSG_READY: { + onReady(); + break; + } case MSG_NEW_SESSION: { + final SomeArgs args = (SomeArgs) msg.obj; + final int flags = args.argi1; + final String token = (String) args.arg1; + final Bundle data = (Bundle) args.arg2; + final AssistStructure assistStructure = (AssistStructure) args.arg3; + onNewSession(token, data, flags, assistStructure); + break; + } case MSG_SESSION_FINISHED: { + final String token = (String) msg.obj; + onSessionFinished(token); + break; + } case MSG_SHUTDOWN: { + onShutdown(); + break; + } default: { + Log.w(TAG, "MyCallbacks received invalid message type: " + msg); + } + } + } + }; + + private HandlerCaller mHandlerCaller; + + @Override + public void onCreate() { + super.onCreate(); + + mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(), mHandlerCallback, true); + } + + @Override + public final IBinder onBind(Intent intent) { + if (SERVICE_INTERFACE.equals(intent.getAction())) { + return mInterface.asBinder(); + } + return null; + } + + /** + * Called during service initialization to tell you when the system is ready + * to receive interaction from it. + * + * <p>You should generally do initialization here rather than in {@link #onCreate}. + * + * <p>Sub-classes should call it first, since it sets the reference to the sytem-server service. + */ + // TODO: rename to onConnected() / add onDisconnected()? + public void onReady() { + if (DEBUG) Log.d(TAG, "onReady()"); + } + + /** + * Called to receive data from the application that the user was requested auto-fill for. + * + * @param token unique token identifying the auto-fill session, it should be used when providing + * the auto-filled fields. + * @param data Arbitrary data supplied by the app through + * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}. + * May be {@code null} if data has been disabled by the user or device policy. + * @param startFlags currently always 0. + * @param structure If available, the structure definition of all windows currently + * displayed by the app. May be {@code null} if auto-fill data has been disabled by the user + * or device policy; will be an empty stub if the application has disabled auto-fill + * by marking its window as secure. + */ + @SuppressWarnings("unused") + // TODO: take the factory approach where this method return a session, and move the callback + // methods (like autofill()) to the session. + public void onNewSession(String token, Bundle data, int startFlags, AssistStructure structure) { + if (DEBUG) Log.d(TAG, "onNewSession(): token=" + token); + } + + /** + * Called when an auto-fill session is finished. + */ + @SuppressWarnings("unused") + public void onSessionFinished(String token) { + if (DEBUG) Log.d(TAG, "onSessionFinished(): token=" + token); + } + + /** + * Called during service de-initialization to tell you when the system is shutting the + * service down. + * + * <p> At this point this service may no longer be an active {@link AutoFillService}. + */ + public void onShutdown() { + if (DEBUG) Log.d(TAG, "onShutdown()"); + } +} diff --git a/core/java/android/service/autofill/AutoFillServiceInfo.java b/core/java/android/service/autofill/AutoFillServiceInfo.java new file mode 100644 index 000000000000..fe2161521b82 --- /dev/null +++ b/core/java/android/service/autofill/AutoFillServiceInfo.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.service.autofill; + +import android.Manifest; +import android.app.AppGlobals; +import android.content.ComponentName; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; +import android.os.RemoteException; + +/** @hide */ +public final class AutoFillServiceInfo { + + private static ServiceInfo getServiceInfoOrThrow(ComponentName comp, int userHandle) + throws PackageManager.NameNotFoundException { + try { + final ServiceInfo si = + AppGlobals.getPackageManager().getServiceInfo(comp, 0, userHandle); + if (si != null) { + return si; + } + } catch (RemoteException e) { + } + throw new PackageManager.NameNotFoundException(comp.toString()); + } + + private String mParseError; + + private ServiceInfo mServiceInfo; + + private AutoFillServiceInfo(ServiceInfo si) { + if (si == null) { + mParseError = "Service not available"; + return; + } + if (!Manifest.permission.BIND_AUTO_FILL.equals(si.permission)) { + mParseError = "Service does not require permission " + + Manifest.permission.BIND_AUTO_FILL; + return; + } + + mServiceInfo = si; + } + + public AutoFillServiceInfo(ComponentName comp, int userHandle) + throws PackageManager.NameNotFoundException { + this(getServiceInfoOrThrow(comp, userHandle)); + } + + public String getParseError() { + return mParseError; + } + + public ServiceInfo getServiceInfo() { + return mServiceInfo; + } +} diff --git a/core/java/android/service/autofill/IAutoFillManagerService.aidl b/core/java/android/service/autofill/IAutoFillManagerService.aidl new file mode 100644 index 000000000000..2c0623413be6 --- /dev/null +++ b/core/java/android/service/autofill/IAutoFillManagerService.aidl @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.autofill; + +import android.os.Bundle; + +/** + * Intermediator between apps being auto-filled and auto-fill service implementations. + * + * {@hide} + */ +interface IAutoFillManagerService { + + /** + * Starts an auto-fill session for the top activities for a given user. + * + * It's used to start a new session from system affordances. + * + * @param userId user handle. + * @param args the bundle to pass as arguments to the voice interaction session. + * @param flags flags indicating optional session behavior. + * @param activityToken optional token of activity that needs to be on top. + * + * @return session token, or null if session was not created (for example, if the activity's + * user does not have an auto-fill service associated with). + */ + // TODO: pass callback providing an onAutoFill() method + String startSession(int userId, in Bundle args, int flags, IBinder activityToken); + + /** + * Finishes an auto-fill session. + * + * @param userId user handle. + * @param token session token. + * + * @return true if session existed and was finished. + */ + boolean finishSession(int userId, String token); + +} diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl new file mode 100644 index 000000000000..73d8d5db5004 --- /dev/null +++ b/core/java/android/service/autofill/IAutoFillService.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.autofill; + +import android.os.Bundle; +import android.app.assist.AssistStructure; + +/** + * @hide + */ +oneway interface IAutoFillService { + void ready(); + void newSession(String token, in Bundle data, int flags, in AssistStructure structure); + void finishSession(String token); + void shutdown(); +} diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java index 4a956c6a1d72..4b272e95f315 100644 --- a/core/java/android/service/notification/Adjustment.java +++ b/core/java/android/service/notification/Adjustment.java @@ -22,11 +22,8 @@ import android.os.Parcel; import android.os.Parcelable; /** - * Ranking updates from the Ranker. - * - * @hide + * Ranking updates from the Assistant. */ -@SystemApi public final class Adjustment implements Parcelable { private final String mPackage; private final String mKey; diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl index 8c359012bc51..f639c0de2044 100644 --- a/core/java/android/service/notification/INotificationListener.aidl +++ b/core/java/android/service/notification/INotificationListener.aidl @@ -28,7 +28,7 @@ oneway interface INotificationListener void onNotificationPosted(in IStatusBarNotificationHolder notificationHolder, in NotificationRankingUpdate update); void onNotificationRemoved(in IStatusBarNotificationHolder notificationHolder, - in NotificationRankingUpdate update); + in NotificationRankingUpdate update, int reason); void onNotificationRankingUpdate(in NotificationRankingUpdate update); void onListenerHintsChanged(int hints); void onInterruptionFilterChanged(int interruptionFilter); @@ -38,5 +38,4 @@ oneway interface INotificationListener void onNotificationVisibilityChanged(String key, long time, boolean visible); void onNotificationClick(String key, long time); void onNotificationActionClick(String key, long time, int actionIndex); - void onNotificationRemovedReason(String key, long time, int reason); } diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java new file mode 100644 index 000000000000..4e00c643d749 --- /dev/null +++ b/core/java/android/service/notification/NotificationAssistantService.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.notification; + +import android.annotation.SdkConstant; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.util.Log; +import com.android.internal.os.SomeArgs; + +import java.util.List; + +/** + * A service that helps the user manage notifications. + */ +public abstract class NotificationAssistantService extends NotificationListenerService { + private static final String TAG = "NotificationAssistants"; + + /** + * The {@link Intent} that must be declared as handled by the service. + */ + @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) + public static final String SERVICE_INTERFACE + = "android.service.notification.NotificationAssistantService"; + + private Handler mHandler; + + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(base); + mHandler = new MyHandler(getContext().getMainLooper()); + } + + @Override + public final IBinder onBind(Intent intent) { + if (mWrapper == null) { + mWrapper = new NotificationAssistantServiceWrapper(); + } + return mWrapper; + } + + /** + * A notification was posted by an app. Called before alert. + * + * @param sbn the new notification + * @param importance the initial importance of the notification. + * @param user true if the initial importance reflects an explicit user preference. + * @return an adjustment or null to take no action, within 100ms. + */ + abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn, + int importance, boolean user); + + /** + * Updates a notification. N.B. this won’t cause + * an existing notification to alert, but might allow a future update to + * this notification to alert. + * + * @param adjustment the adjustment with an explanation + */ + public final void adjustNotification(Adjustment adjustment) { + if (!isBound()) return; + try { + getNotificationInterface().applyAdjustmentFromAssistantService(mWrapper, adjustment); + } catch (android.os.RemoteException ex) { + Log.v(TAG, "Unable to contact notification manager", ex); + } + } + + /** + * Updates existing notifications. Re-ranking won't occur until all adjustments are applied. + * N.B. this won’t cause an existing notification to alert, but might allow a future update to + * these notifications to alert. + * + * @param adjustments a list of adjustments with explanations + */ + public final void adjustNotifications(List<Adjustment> adjustments) { + if (!isBound()) return; + try { + getNotificationInterface().applyAdjustmentsFromAssistantService(mWrapper, adjustments); + } catch (android.os.RemoteException ex) { + Log.v(TAG, "Unable to contact notification manager", ex); + } + } + + private class NotificationAssistantServiceWrapper extends NotificationListenerWrapper { + @Override + public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder, + int importance, boolean user) { + StatusBarNotification sbn; + try { + sbn = sbnHolder.get(); + } catch (RemoteException e) { + Log.w(TAG, "onNotificationEnqueued: Error receiving StatusBarNotification", e); + return; + } + + SomeArgs args = SomeArgs.obtain(); + args.arg1 = sbn; + args.argi1 = importance; + args.argi2 = user ? 1 : 0; + mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_ENQUEUED, + args).sendToTarget(); + } + } + + private final class MyHandler extends Handler { + public static final int MSG_ON_NOTIFICATION_ENQUEUED = 1; + + public MyHandler(Looper looper) { + super(looper, null, false); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_ON_NOTIFICATION_ENQUEUED: { + SomeArgs args = (SomeArgs) msg.obj; + StatusBarNotification sbn = (StatusBarNotification) args.arg1; + final int importance = args.argi1; + final boolean user = args.argi2 == 1; + args.recycle(); + Adjustment adjustment = onNotificationEnqueued(sbn, importance, user); + if (adjustment != null) { + adjustNotification(adjustment); + } + } break; + } + } + } +} diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index e606ebfbfee6..45011eb49905 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -77,6 +77,7 @@ import java.util.List; * </p> */ public abstract class NotificationListenerService extends Service { + // TAG = "NotificationListenerService[MySubclass]" private final String TAG = NotificationListenerService.class.getSimpleName() + "[" + getClass().getSimpleName() + "]"; @@ -146,6 +147,48 @@ public abstract class NotificationListenerService extends Service { public static final int SUPPRESSED_EFFECT_SCREEN_ON = NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON; + + // Notification cancellation reasons + + /** Notification was canceled by the status bar reporting a click. */ + public static final int REASON_DELEGATE_CLICK = 1; + /** Notification was canceled by the status bar reporting a user dismissal. */ + public static final int REASON_DELEGATE_CANCEL = 2; + /** Notification was canceled by the status bar reporting a user dismiss all. */ + public static final int REASON_DELEGATE_CANCEL_ALL = 3; + /** Notification was canceled by the status bar reporting an inflation error. */ + public static final int REASON_DELEGATE_ERROR = 4; + /** Notification was canceled by the package manager modifying the package. */ + public static final int REASON_PACKAGE_CHANGED = 5; + /** Notification was canceled by the owning user context being stopped. */ + public static final int REASON_USER_STOPPED = 6; + /** Notification was canceled by the user banning the package. */ + public static final int REASON_PACKAGE_BANNED = 7; + /** Notification was canceled by the app canceling this specific notification. */ + public static final int REASON_APP_CANCEL = 8; + /** Notification was canceled by the app cancelling all its notifications. */ + public static final int REASON_APP_CANCEL_ALL = 9; + /** Notification was canceled by a listener reporting a user dismissal. */ + public static final int REASON_LISTENER_CANCEL = 10; + /** Notification was canceled by a listener reporting a user dismiss all. */ + public static final int REASON_LISTENER_CANCEL_ALL = 11; + /** Notification was canceled because it was a member of a canceled group. */ + public static final int REASON_GROUP_SUMMARY_CANCELED = 12; + /** Notification was canceled because it was an invisible member of a group. */ + public static final int REASON_GROUP_OPTIMIZATION = 13; + /** Notification was canceled by the device administrator suspending the package. */ + public static final int REASON_PACKAGE_SUSPENDED = 14; + /** Notification was canceled by the owning managed profile being turned off. */ + public static final int REASON_PROFILE_TURNED_OFF = 15; + /** Autobundled summary notification was canceled because its group was unbundled */ + public static final int REASON_UNAUTOBUNDLED = 16; + /** Notification was canceled by the user banning the channel. */ + public static final int REASON_CHANNEL_BANNED = 17; + /** Notification was snoozed. */ + public static final int REASON_SNOOZED = 18; + /** Notification no longer visible because of user switch */ + public static final int REASON_USER_SWITCH = 19; + /** * The full trim of the StatusBarNotification including all its features. * @@ -282,6 +325,32 @@ public abstract class NotificationListenerService extends Service { onNotificationRemoved(sbn); } + + /** + * Implement this method to learn when notifications are removed and why. + * <p> + * This might occur because the user has dismissed the notification using system UI (or another + * notification listener) or because the app has withdrawn the notification. + * <p> + * NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the + * result from {@link StatusBarNotification#getNotification} may be missing some heavyweight + * fields such as {@link android.app.Notification#contentView} and + * {@link android.app.Notification#largeIcon}. However, all other fields on + * {@link StatusBarNotification}, sufficient to match this call with a prior call to + * {@link #onNotificationPosted(StatusBarNotification)}, will be intact. + * + ** @param sbn A data structure encapsulating at least the original information (tag and id) + * and source (package name) used to post the {@link android.app.Notification} that + * was just removed. + * @param rankingMap The current ranking map that can be used to retrieve ranking information + * for active notifications. + * @param reason see {@link #REASON_LISTENER_CANCEL}, etc. + */ + public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap, + int reason) { + onNotificationRemoved(sbn, rankingMap); + } + /** * Implement this method to learn about when the listener is enabled and connected to * the notification manager. You are safe to call {@link #getActiveNotifications()} @@ -927,7 +996,7 @@ public abstract class NotificationListenerService extends Service { @Override public void onNotificationRemoved(IStatusBarNotificationHolder sbnHolder, - NotificationRankingUpdate update) { + NotificationRankingUpdate update, int reason) { StatusBarNotification sbn; try { sbn = sbnHolder.get(); @@ -941,6 +1010,7 @@ public abstract class NotificationListenerService extends Service { SomeArgs args = SomeArgs.obtain(); args.arg1 = sbn; args.arg2 = mRankingMap; + args.arg3 = reason; mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_REMOVED, args).sendToTarget(); } @@ -1003,12 +1073,6 @@ public abstract class NotificationListenerService extends Service { throws RemoteException { // no-op in the listener } - - @Override - public void onNotificationRemovedReason(String key, long time, int reason) - throws RemoteException { - // no-op in the listener - } } private void applyUpdateLocked(NotificationRankingUpdate update) { @@ -1413,8 +1477,9 @@ public abstract class NotificationListenerService extends Service { SomeArgs args = (SomeArgs) msg.obj; StatusBarNotification sbn = (StatusBarNotification) args.arg1; RankingMap rankingMap = (RankingMap) args.arg2; + int reason = (int) args.arg3; args.recycle(); - onNotificationRemoved(sbn, rankingMap); + onNotificationRemoved(sbn, rankingMap, reason); } break; case MSG_ON_LISTENER_CONNECTED: { diff --git a/core/java/android/service/notification/NotificationRankerService.java b/core/java/android/service/notification/NotificationRankerService.java deleted file mode 100644 index 928d5d847365..000000000000 --- a/core/java/android/service/notification/NotificationRankerService.java +++ /dev/null @@ -1,349 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.service.notification; - -import android.annotation.SdkConstant; -import android.annotation.SystemApi; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.RemoteException; -import android.util.Log; -import com.android.internal.os.SomeArgs; - -import java.util.List; - -/** - * A service that helps the user manage notifications. This class is only used to - * extend the framework service and may not be implemented by non-framework components. - * @hide - */ -@SystemApi -public abstract class NotificationRankerService extends NotificationListenerService { - private static final String TAG = "NotificationRankers"; - - /** - * The {@link Intent} that must be declared as handled by the service. - */ - @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) - public static final String SERVICE_INTERFACE - = "android.service.notification.NotificationRankerService"; - - /** Notification was canceled by the status bar reporting a click. */ - public static final int REASON_DELEGATE_CLICK = 1; - - /** Notification was canceled by the status bar reporting a user dismissal. */ - public static final int REASON_DELEGATE_CANCEL = 2; - - /** Notification was canceled by the status bar reporting a user dismiss all. */ - public static final int REASON_DELEGATE_CANCEL_ALL = 3; - - /** Notification was canceled by the status bar reporting an inflation error. */ - public static final int REASON_DELEGATE_ERROR = 4; - - /** Notification was canceled by the package manager modifying the package. */ - public static final int REASON_PACKAGE_CHANGED = 5; - - /** Notification was canceled by the owning user context being stopped. */ - public static final int REASON_USER_STOPPED = 6; - - /** Notification was canceled by the user banning the package. */ - public static final int REASON_PACKAGE_BANNED = 7; - - /** Notification was canceled by the app canceling this specific notification. */ - public static final int REASON_APP_CANCEL = 8; - - /** Notification was canceled by the app cancelling all its notifications. */ - public static final int REASON_APP_CANCEL_ALL = 9; - - /** Notification was canceled by a listener reporting a user dismissal. */ - public static final int REASON_LISTENER_CANCEL = 10; - - /** Notification was canceled by a listener reporting a user dismiss all. */ - public static final int REASON_LISTENER_CANCEL_ALL = 11; - - /** Notification was canceled because it was a member of a canceled group. */ - public static final int REASON_GROUP_SUMMARY_CANCELED = 12; - - /** Notification was canceled because it was an invisible member of a group. */ - public static final int REASON_GROUP_OPTIMIZATION = 13; - - /** Notification was canceled by the device administrator suspending the package. */ - public static final int REASON_PACKAGE_SUSPENDED = 14; - - /** Notification was canceled by the owning managed profile being turned off. */ - public static final int REASON_PROFILE_TURNED_OFF = 15; - - /** Autobundled summary notification was canceled because its group was unbundled */ - public static final int REASON_UNAUTOBUNDLED = 16; - - /** Notification was canceled by the user banning the channel. */ - public static final int REASON_CHANNEL_BANNED = 17; - - /** Notification was snoozed. */ - public static final int REASON_SNOOZED = 18; - - private Handler mHandler; - - /** @hide */ - @Override - public void registerAsSystemService(Context context, ComponentName componentName, - int currentUser) { - throw new UnsupportedOperationException("the ranker lifecycle is managed by the system."); - } - - /** @hide */ - @Override - public void unregisterAsSystemService() { - throw new UnsupportedOperationException("the ranker lifecycle is managed by the system."); - } - - @Override - protected void attachBaseContext(Context base) { - super.attachBaseContext(base); - mHandler = new MyHandler(getContext().getMainLooper()); - } - - @Override - public final IBinder onBind(Intent intent) { - if (mWrapper == null) { - mWrapper = new NotificationRankingServiceWrapper(); - } - return mWrapper; - } - - /** - * A notification was posted by an app. Called before alert. - * - * @param sbn the new notification - * @param importance the initial importance of the notification. - * @param user true if the initial importance reflects an explicit user preference. - * @return an adjustment or null to take no action, within 100ms. - */ - abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn, - int importance, boolean user); - - /** - * The visibility of a notification has changed. - * - * @param key the notification key - * @param time milliseconds since midnight, January 1, 1970 UTC. - * @param visible true if the notification became visible, false if hidden. - */ - public void onNotificationVisibilityChanged(String key, long time, boolean visible) - { - // Do nothing, Override this to collect visibility statistics. - } - - /** - * The user clicked on a notification. - * - * @param key the notification key - * @param time milliseconds since midnight, January 1, 1970 UTC. - */ - public void onNotificationClick(String key, long time) - { - // Do nothing, Override this to collect click statistics - } - - /** - * The user clicked on a notification action. - * - * @param key the notification key - * @param time milliseconds since midnight, January 1, 1970 UTC. - * @param actionIndex the index of the action button that was pressed. - */ - public void onNotificationActionClick(String key, long time, int actionIndex) - { - // Do nothing, Override this to collect action button click statistics - } - - /** - * A notification was removed. - - * @param key the notification key - * @param time milliseconds since midnight, January 1, 1970 UTC. - * @param reason see {@link #REASON_LISTENER_CANCEL}, etc. - */ - public void onNotificationRemoved(String key, long time, int reason) { - // Do nothing, Override this to collect dismissal statistics - } - - /** - * Updates a notification. N.B. this won’t cause - * an existing notification to alert, but might allow a future update to - * this notification to alert. - * - * @param adjustment the adjustment with an explanation - */ - public final void adjustNotification(Adjustment adjustment) { - if (!isBound()) return; - try { - getNotificationInterface().applyAdjustmentFromRankerService(mWrapper, adjustment); - } catch (android.os.RemoteException ex) { - Log.v(TAG, "Unable to contact notification manager", ex); - } - } - - /** - * Updates existing notifications. Re-ranking won't occur until all adjustments are applied. - * N.B. this won’t cause an existing notification to alert, but might allow a future update to - * these notifications to alert. - * - * @param adjustments a list of adjustments with explanations - */ - public final void adjustNotifications(List<Adjustment> adjustments) { - if (!isBound()) return; - try { - getNotificationInterface().applyAdjustmentsFromRankerService(mWrapper, adjustments); - } catch (android.os.RemoteException ex) { - Log.v(TAG, "Unable to contact notification manager", ex); - } - } - - private class NotificationRankingServiceWrapper extends NotificationListenerWrapper { - @Override - public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder, - int importance, boolean user) { - StatusBarNotification sbn; - try { - sbn = sbnHolder.get(); - } catch (RemoteException e) { - Log.w(TAG, "onNotificationEnqueued: Error receiving StatusBarNotification", e); - return; - } - - SomeArgs args = SomeArgs.obtain(); - args.arg1 = sbn; - args.argi1 = importance; - args.argi2 = user ? 1 : 0; - mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_ENQUEUED, - args).sendToTarget(); - } - - @Override - public void onNotificationVisibilityChanged(String key, long time, boolean visible) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = key; - args.arg2 = time; - args.argi1 = visible ? 1 : 0; - mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_VISIBILITY_CHANGED, - args).sendToTarget(); - } - - @Override - public void onNotificationClick(String key, long time) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = key; - args.arg2 = time; - mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_CLICK, - args).sendToTarget(); - } - - @Override - public void onNotificationActionClick(String key, long time, int actionIndex) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = key; - args.arg2 = time; - args.argi1 = actionIndex; - mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_ACTION_CLICK, - args).sendToTarget(); - } - - @Override - public void onNotificationRemovedReason(String key, long time, int reason) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = key; - args.arg2 = time; - args.argi1 = reason; - mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_REMOVED_REASON, - args).sendToTarget(); - } - } - - private final class MyHandler extends Handler { - public static final int MSG_ON_NOTIFICATION_ENQUEUED = 1; - public static final int MSG_ON_NOTIFICATION_VISIBILITY_CHANGED = 2; - public static final int MSG_ON_NOTIFICATION_CLICK = 3; - public static final int MSG_ON_NOTIFICATION_ACTION_CLICK = 4; - public static final int MSG_ON_NOTIFICATION_REMOVED_REASON = 5; - - public MyHandler(Looper looper) { - super(looper, null, false); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_ON_NOTIFICATION_ENQUEUED: { - SomeArgs args = (SomeArgs) msg.obj; - StatusBarNotification sbn = (StatusBarNotification) args.arg1; - final int importance = args.argi1; - final boolean user = args.argi2 == 1; - args.recycle(); - Adjustment adjustment = onNotificationEnqueued(sbn, importance, user); - if (adjustment != null) { - adjustNotification(adjustment); - } - } break; - - case MSG_ON_NOTIFICATION_VISIBILITY_CHANGED: { - SomeArgs args = (SomeArgs) msg.obj; - final String key = (String) args.arg1; - final long time = (long) args.arg2; - final boolean visible = args.argi1 == 1; - args.recycle(); - onNotificationVisibilityChanged(key, time, visible); - } break; - - case MSG_ON_NOTIFICATION_CLICK: { - SomeArgs args = (SomeArgs) msg.obj; - final String key = (String) args.arg1; - final long time = (long) args.arg2; - args.recycle(); - onNotificationClick(key, time); - } break; - - case MSG_ON_NOTIFICATION_ACTION_CLICK: { - SomeArgs args = (SomeArgs) msg.obj; - final String key = (String) args.arg1; - final long time = (long) args.arg2; - final int actionIndex = args.argi1; - args.recycle(); - onNotificationActionClick(key, time, actionIndex); - } break; - - case MSG_ON_NOTIFICATION_REMOVED_REASON: { - SomeArgs args = (SomeArgs) msg.obj; - final String key = (String) args.arg1; - final long time = (long) args.arg2; - final int reason = args.argi1; - args.recycle(); - onNotificationRemoved(key, time, reason); - } break; - } - } - } -} diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index be0b47cc58ac..dfb6b8639912 100644 --- a/core/java/android/service/notification/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -17,6 +17,7 @@ package android.service.notification; import android.app.Notification; +import android.app.NotificationChannel; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -42,25 +43,21 @@ public class StatusBarNotification implements Parcelable { private final Notification notification; private final UserHandle user; private final long postTime; + private final NotificationChannel channel; private Context mContext; // used for inflation & icon expansion /** @hide */ - public StatusBarNotification(String pkg, String opPkg, int id, String tag, int uid, - int initialPid, int score, Notification notification, UserHandle user) { - this(pkg, opPkg, id, tag, uid, initialPid, score, notification, user, - System.currentTimeMillis()); - } - - /** @hide */ - public StatusBarNotification(String pkg, String opPkg, int id, String tag, int uid, - int initialPid, Notification notification, UserHandle user, String overrideGroupKey, - long postTime) { + public StatusBarNotification(String pkg, String opPkg, NotificationChannel channel, int id, + String tag, int uid, int initialPid, Notification notification, UserHandle user, + String overrideGroupKey, long postTime) { if (pkg == null) throw new NullPointerException(); if (notification == null) throw new NullPointerException(); + if (channel == null) throw new IllegalArgumentException(); this.pkg = pkg; this.opPkg = opPkg; + this.channel = channel; this.id = id; this.tag = tag; this.uid = uid; @@ -73,6 +70,7 @@ public class StatusBarNotification implements Parcelable { this.groupKey = groupKey(); } + @Deprecated public StatusBarNotification(String pkg, String opPkg, int id, String tag, int uid, int initialPid, int score, Notification notification, UserHandle user, long postTime) { @@ -90,6 +88,7 @@ public class StatusBarNotification implements Parcelable { this.postTime = postTime; this.key = key(); this.groupKey = groupKey(); + this.channel = null; } public StatusBarNotification(Parcel in) { @@ -113,6 +112,7 @@ public class StatusBarNotification implements Parcelable { } this.key = key(); this.groupKey = groupKey(); + this.channel = NotificationChannel.CREATOR.createFromParcel(in); } private String key() { @@ -182,6 +182,7 @@ public class StatusBarNotification implements Parcelable { } else { out.writeInt(0); } + this.channel.writeToParcel(out, flags); } public int describeContents() { @@ -208,14 +209,14 @@ public class StatusBarNotification implements Parcelable { public StatusBarNotification cloneLight() { final Notification no = new Notification(); this.notification.cloneInto(no, false); // light copy - return new StatusBarNotification(this.pkg, this.opPkg, + return new StatusBarNotification(this.pkg, this.opPkg, this.channel, this.id, this.tag, this.uid, this.initialPid, no, this.user, this.overrideGroupKey, this.postTime); } @Override public StatusBarNotification clone() { - return new StatusBarNotification(this.pkg, this.opPkg, + return new StatusBarNotification(this.pkg, this.opPkg, this.channel, this.id, this.tag, this.uid, this.initialPid, this.notification.clone(), this.user, this.overrideGroupKey, this.postTime); } @@ -335,6 +336,13 @@ public class StatusBarNotification implements Parcelable { } /** + * Returns the channel this notification was posted to. + */ + public NotificationChannel getNotificationChannel() { + return channel; + } + + /** * @hide */ public Context getPackageContext(Context context) { diff --git a/core/java/android/util/Half.java b/core/java/android/util/Half.java index f4eb1328ffe3..08fb948a1bb6 100644 --- a/core/java/android/util/Half.java +++ b/core/java/android/util/Half.java @@ -27,20 +27,64 @@ package android.util; * <ul> * <li>Sign bit: 1 bit</li> * <li>Exponent width: 5 bits</li> - * <li>Mantissa: 10 bits</li> + * <li>Significand: 10 bits</li> * </ul> * - * <p>The format is laid out thusly:</p> + * <p>The format is laid out as follows:</p> * <pre> * 1 11111 1111111111 * ^ --^-- -----^---- - * sign | |_______ mantissa + * sign | |_______ significand * | * -- exponent * </pre> * - * @hide + * <p>Half-precision floating points can be useful to save memory and/or + * bandwidth at the expense of range and precision when compared to single-precision + * floating points (fp32).</p> + * <p>To help you decide whether fp16 is the right storage type for you need, please + * refer to the table below that shows the available precision throughout the range of + * possible values. The <em>precision</em> column indicates the step size between two + * consecutive numbers in a specific part of the range.</p> + * + * <table summary="Precision of fp16 across the range"> + * <tr><th>Range start</th><th>Precision</th></tr> + * <tr><td>0</td><td>1 ⁄ 16,777,216</td></tr> + * <tr><td>1 ⁄ 16,384</td><td>1 ⁄ 16,777,216</td></tr> + * <tr><td>1 ⁄ 8,192</td><td>1 ⁄ 8,388,608</td></tr> + * <tr><td>1 ⁄ 4,096</td><td>1 ⁄ 4,194,304</td></tr> + * <tr><td>1 ⁄ 2,048</td><td>1 ⁄ 2,097,152</td></tr> + * <tr><td>1 ⁄ 1,024</td><td>1 ⁄ 1,048,576</td></tr> + * <tr><td>1 ⁄ 512</td><td>1 ⁄ 524,288</td></tr> + * <tr><td>1 ⁄ 256</td><td>1 ⁄ 262,144</td></tr> + * <tr><td>1 ⁄ 128</td><td>1 ⁄ 131,072</td></tr> + * <tr><td>1 ⁄ 64</td><td>1 ⁄ 65,536</td></tr> + * <tr><td>1 ⁄ 32</td><td>1 ⁄ 32,768</td></tr> + * <tr><td>1 ⁄ 16</td><td>1 ⁄ 16,384</td></tr> + * <tr><td>1 ⁄ 8</td><td>1 ⁄ 8,192</td></tr> + * <tr><td>1 ⁄ 4</td><td>1 ⁄ 4,096</td></tr> + * <tr><td>1 ⁄ 2</td><td>1 ⁄ 2,048</td></tr> + * <tr><td>1</td><td>1 ⁄ 1,024</td></tr> + * <tr><td>2</td><td>1 ⁄ 512</td></tr> + * <tr><td>4</td><td>1 ⁄ 256</td></tr> + * <tr><td>8</td><td>1 ⁄ 128</td></tr> + * <tr><td>16</td><td>1 ⁄ 64</td></tr> + * <tr><td>32</td><td>1 ⁄ 32</td></tr> + * <tr><td>64</td><td>1 ⁄ 16</td></tr> + * <tr><td>128</td><td>1 ⁄ 8</td></tr> + * <tr><td>256</td><td>1 ⁄ 4</td></tr> + * <tr><td>512</td><td>1 ⁄ 2</td></tr> + * <tr><td>1,024</td><td>1</td></tr> + * <tr><td>2,048</td><td>2</td></tr> + * <tr><td>4,096</td><td>4</td></tr> + * <tr><td>8,192</td><td>8</td></tr> + * <tr><td>16,384</td><td>16</td></tr> + * <tr><td>32,768</td><td>32</td></tr> + * </table> + * + * <p>This table shows that numbers higher than 1024 lose all fractional precision.</p> */ +@SuppressWarnings("SimplifiableIfStatement") public final class Half { /** * The number of bits used to represent a half-precision float value. @@ -59,7 +103,7 @@ public final class Half { /** * Maximum exponent a finite half-precision float may have. */ - public static final short MAX_EXPONENT = 15; + public static final int MAX_EXPONENT = 15; /** * Maximum positive finite value a half-precision float may have. */ @@ -67,7 +111,7 @@ public final class Half { /** * Minimum exponent a normalized half-precision float may have. */ - public static final short MIN_EXPONENT = -14; + public static final int MIN_EXPONENT = -14; /** * Smallest positive normal value a half-precision float may have. */ @@ -97,41 +141,345 @@ public final class Half { */ public static final short POSITIVE_ZERO = (short) 0x0000; - private static final int FP16_SIGN_SHIFT = 15; - private static final int FP16_EXPONENT_SHIFT = 10; - private static final int FP16_EXPONENT_MASK = 0x1f; - private static final int FP16_MANTISSA_MASK = 0x3ff; - private static final int FP16_EXPONENT_BIAS = 15; + private static final int FP16_SIGN_SHIFT = 15; + private static final int FP16_SIGN_MASK = 0x8000; + private static final int FP16_EXPONENT_SHIFT = 10; + private static final int FP16_EXPONENT_MASK = 0x1f; + private static final int FP16_SIGNIFICAND_MASK = 0x3ff; + private static final int FP16_EXPONENT_BIAS = 15; + private static final int FP16_COMBINED = 0x7fff; + private static final int FP16_EXPONENT_MAX = 0x7c00; - private static final int FP32_SIGN_SHIFT = 31; - private static final int FP32_EXPONENT_SHIFT = 23; - private static final int FP32_EXPONENT_MASK = 0xff; - private static final int FP32_MANTISSA_MASK = 0x7fffff; - private static final int FP32_EXPONENT_BIAS = 127; + private static final int FP32_SIGN_SHIFT = 31; + private static final int FP32_EXPONENT_SHIFT = 23; + private static final int FP32_EXPONENT_MASK = 0xff; + private static final int FP32_SIGNIFICAND_MASK = 0x7fffff; + private static final int FP32_EXPONENT_BIAS = 127; - private static final int FP32_DENORMAL_MAGIC = 126 << 23; - private static final float FP32_DENORMAL_FLOAT = - Float.intBitsToFloat(FP32_DENORMAL_MAGIC); + private static final int FP32_DENORMAL_MAGIC = 126 << 23; + private static final float FP32_DENORMAL_FLOAT = Float.intBitsToFloat(FP32_DENORMAL_MAGIC); private Half() { } /** + * Returns the first parameter with the sign of the second parameter. + * This method treats NaNs as having a sign. + * + * @param magnitude A half-precision float value providing the magnitude of the result + * @param sign A half-precision float value providing the sign of the result + * @return A value with the magnitude of the first parameter and the sign + * of the second parameter + */ + public static short copySign(short magnitude, short sign) { + return (short) ((sign & FP16_SIGN_MASK) | (magnitude & FP16_COMBINED)); + } + + /** + * Returns the absolute value of the specified half-precision float. + * Special values are handled in the following ways: + * <ul> + * <li>If the specified half-precision float is NaN, the result is NaN</li> + * <li>If the specified half-precision float is zero (negative or positive), + * the result is positive zero (see {@link #POSITIVE_ZERO})</li> + * <li>If the specified half-precision float is infinity (negative or positive), + * the result is positive infinity (see {@link #POSITIVE_INFINITY})</li> + * </ul> + * + * @param h A half-precision float value + * @return The absolute value of the specified half-precision float + */ + public static short abs(short h) { + return (short) (h & FP16_COMBINED); + } + + /** + * Returns the closest integral half-precision float value to the specified + * half-precision float value. Special values are handled in the + * following ways: + * <ul> + * <li>If the specified half-precision float is NaN, the result is NaN</li> + * <li>If the specified half-precision float is infinity (negative or positive), + * the result is infinity (with the same sign)</li> + * <li>If the specified half-precision float is zero (negative or positive), + * the result is zero (with the same sign)</li> + * </ul> + * + * @param h A half-precision float value + * @return The value of the specified half-precision float rounded to the nearest + * half-precision float value + */ + public static short round(short h) { + int bits = h & 0xffff; + int e = bits & 0x7fff; + int result = bits; + + if (e < 0x3c00) { + result &= FP16_SIGN_MASK; + result |= (0x3c00 & (e >= 0x3800 ? 0xffff : 0x0)); + } else if (e < 0x6400) { + e = 25 - (e >> 10); + int mask = (1 << e) - 1; + result += (1 << (e - 1)); + result &= ~mask; + } + + return (short) result; + } + + /** + * Returns the smallest half-precision float value toward negative infinity + * greater than or equal to the specified half-precision float value. + * Special values are handled in the following ways: + * <ul> + * <li>If the specified half-precision float is NaN, the result is NaN</li> + * <li>If the specified half-precision float is infinity (negative or positive), + * the result is infinity (with the same sign)</li> + * <li>If the specified half-precision float is zero (negative or positive), + * the result is zero (with the same sign)</li> + * </ul> + * + * @param h A half-precision float value + * @return The smallest half-precision float value toward negative infinity + * greater than or equal to the specified half-precision float value + */ + public static short ceil(short h) { + int bits = h & 0xffff; + int e = bits & 0x7fff; + int result = bits; + + if (e < 0x3c00) { + result &= FP16_SIGN_MASK; + result |= 0x3c00 & -(~(bits >> 15) & (e != 0 ? 1 : 0)); + } else if (e < 0x6400) { + e = 25 - (e >> 10); + int mask = (1 << e) - 1; + result += mask & ((bits >> 15) - 1); + result &= ~mask; + } + + return (short) result; + } + + /** + * Returns the largest half-precision float value toward positive infinity + * less than or equal to the specified half-precision float value. + * Special values are handled in the following ways: + * <ul> + * <li>If the specified half-precision float is NaN, the result is NaN</li> + * <li>If the specified half-precision float is infinity (negative or positive), + * the result is infinity (with the same sign)</li> + * <li>If the specified half-precision float is zero (negative or positive), + * the result is zero (with the same sign)</li> + * </ul> + * + * @param h A half-precision float value + * @return The largest half-precision float value toward positive infinity + * less than or equal to the specified half-precision float value + */ + public static short floor(short h) { + int bits = h & 0xffff; + int e = bits & 0x7fff; + int result = bits; + + if (e < 0x3c00) { + result &= FP16_SIGN_MASK; + result |= 0x3c00 & (bits > 0x8000 ? 0xffff : 0x0); + } else if (e < 0x6400) { + e = 25 - (e >> 10); + int mask = (1 << e) - 1; + result += mask & -(bits >> 15); + result &= ~mask; + } + + return (short) result; + } + + /** + * Returns the truncated half-precision float value of the specified + * half-precision float value. Special values are handled in the following ways: + * <ul> + * <li>If the specified half-precision float is NaN, the result is NaN</li> + * <li>If the specified half-precision float is infinity (negative or positive), + * the result is infinity (with the same sign)</li> + * <li>If the specified half-precision float is zero (negative or positive), + * the result is zero (with the same sign)</li> + * </ul> + * + * @param h A half-precision float value + * @return The truncated half-precision float value of the specified + * half-precision float value + */ + public static short trunc(short h) { + int bits = h & 0xffff; + int e = bits & 0x7fff; + int result = bits; + + if (e < 0x3c00) { + result &= FP16_SIGN_MASK; + } else if (e < 0x6400) { + e = 25 - (e >> 10); + int mask = (1 << e) - 1; + result &= ~mask; + } + + return (short) result; + } + + /** + * Returns the smaller of two half-precision float values (the value closest + * to negative infinity). Special values are handled in the following ways: + * <ul> + * <li>If either value is NaN, the result is NaN</li> + * <li>{@link #NEGATIVE_ZERO} is smaller than {@link #POSITIVE_ZERO}</li> + * </ul> + * + * @param x The first half-precision value + * @param y The second half-precision value + * @return The smaller of the two specified half-precision values + */ + public static short min(short x, short y) { + if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN; + if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN; + + if ((x & FP16_COMBINED) == 0 && (y & FP16_COMBINED) == 0) { + return (x & FP16_SIGN_MASK) != 0 ? x : y; + } + + return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) < + ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff) ? x : y; + } + + /** + * Returns the larger of two half-precision float values (the value closest + * to positive infinity). Special values are handled in the following ways: + * <ul> + * <li>If either value is NaN, the result is NaN</li> + * <li>{@link #POSITIVE_ZERO} is greater than {@link #NEGATIVE_ZERO}</li> + * </ul> + * + * @param x The first half-precision value + * @param y The second half-precision value + * + * @return The larger of the two specified half-precision values + */ + public static short max(short x, short y) { + if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN; + if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN; + + if ((x & FP16_COMBINED) == 0 && (y & FP16_COMBINED) == 0) { + return (x & FP16_SIGN_MASK) != 0 ? y : x; + } + + return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) > + ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff) ? x : y; + } + + /** + * Returns true if the first half-precision float value is less (smaller + * toward negative infinity) than the second half-precision float value. + * If either of the values is NaN, the result is false. + * + * @param x The first half-precision value + * @param y The second half-precision value + * + * @return True if x is less than y, false otherwise + */ + public static boolean less(short x, short y) { + if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false; + if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false; + + return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) < + ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff); + } + + /** + * Returns true if the first half-precision float value is less (smaller + * toward negative infinity) than or equal to the second half-precision + * float value. If either of the values is NaN, the result is false. + * + * @param x The first half-precision value + * @param y The second half-precision value + * + * @return True if x is less than or equal to y, false otherwise + */ + public static boolean lessEquals(short x, short y) { + if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false; + if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false; + + return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <= + ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff); + } + + /** + * Returns true if the first half-precision float value is greater (larger + * toward positive infinity) than the second half-precision float value. + * If either of the values is NaN, the result is false. + * + * @param x The first half-precision value + * @param y The second half-precision value + * + * @return True if x is greater than y, false otherwise + */ + public static boolean greater(short x, short y) { + if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false; + if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false; + + return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) > + ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff); + } + + /** + * Returns true if the first half-precision float value is greater (larger + * toward positive infinity) than or equal to the second half-precision float + * value. If either of the values is NaN, the result is false. + * + * @param x The first half-precision value + * @param y The second half-precision value + * + * @return True if x is greater than y, false otherwise + */ + public static boolean greaterEquals(short x, short y) { + if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false; + if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false; + + return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >= + ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff); + } + + /** + * Returns true if the two half-precision float values are equal. + * If either of the values is NaN, the result is false. {@link #POSITIVE_ZERO} + * and {@link #NEGATIVE_ZERO} are considered equal. + * + * @param x The first half-precision value + * @param y The second half-precision value + * + * @return True if x is equal to y, false otherwise + */ + public static boolean equals(short x, short y) { + if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false; + if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false; + + return x == y || ((x | y) & FP16_COMBINED) == 0; + } + + /** * Returns the sign of the specified half-precision float. * * @param h A half-precision float value * @return 1 if the value is positive, -1 if the value is negative */ public static int getSign(short h) { - return (h >>> FP16_SIGN_SHIFT) == 0 ? 1 : -1; + return (h & FP16_SIGN_MASK) == 0 ? 1 : -1; } /** * Returns the unbiased exponent used in the representation of * the specified half-precision float value. if the value is NaN * or infinite, this* method returns {@link #MAX_EXPONENT} + 1. - * If the argument is* 0 or denormal, this method returns - * {@link #MIN_EXPONENT} - 1. + * If the argument is 0 or a subnormal representation, this method + * returns {@link #MIN_EXPONENT} - 1. * * @param h A half-precision float value * @return The unbiased exponent of the specified value @@ -141,14 +489,14 @@ public final class Half { } /** - * Returns the mantissa, or significand, used in the representation + * Returns the significand, or mantissa, used in the representation * of the specified half-precision float value. * * @param h A half-precision float value - * @return The mantissa, or significand, of the specified vlaue + * @return The significand, or significand, of the specified vlaue */ - public static int getMantissa(short h) { - return h & FP16_MANTISSA_MASK; + public static int getSignificand(short h) { + return h & FP16_SIGNIFICAND_MASK; } /** @@ -160,9 +508,7 @@ public final class Half { * false otherwise */ public static boolean isInfinite(short h) { - int e = (h >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK; - int m = (h ) & FP16_MANTISSA_MASK; - return e == 0x1f && m == 0; + return (h & FP16_COMBINED) == FP16_EXPONENT_MAX; } /** @@ -173,9 +519,21 @@ public final class Half { * @return true if the value is a NaN, false otherwise */ public static boolean isNaN(short h) { - int e = (h >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK; - int m = (h ) & FP16_MANTISSA_MASK; - return e == 0x1f && m != 0; + return (h & FP16_COMBINED) > FP16_EXPONENT_MAX; + } + + /** + * Returns true if the specified half-precision float value is normalized + * (does not have a subnormal representation). If the specified value is + * {@link #POSITIVE_INFINITY}, {@link #NEGATIVE_INFINITY}, + * {@link #POSITIVE_ZERO}, {@link #NEGATIVE_ZERO}, NaN or any subnormal + * number, this method returns false. + * + * @param h A half-precision float value + * @return true if the value is normalized, false otherwise + */ + public static boolean isNormalized(short h) { + return (h & FP16_EXPONENT_MAX) != 0 && (h & FP16_EXPONENT_MAX) != FP16_EXPONENT_MAX; } /** @@ -195,9 +553,9 @@ public final class Half { */ public static float toFloat(short h) { int bits = h & 0xffff; - int s = (bits >>> FP16_SIGN_SHIFT ); + int s = bits & FP16_SIGN_MASK; int e = (bits >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK; - int m = (bits ) & FP16_MANTISSA_MASK; + int m = (bits ) & FP16_SIGNIFICAND_MASK; int outE = 0; int outM = 0; @@ -218,7 +576,7 @@ public final class Half { } } - int out = (s << FP32_SIGN_SHIFT) | (outE << FP32_EXPONENT_SHIFT) | outM; + int out = (s << 16) | (outE << FP32_EXPONENT_SHIFT) | outM; return Float.intBitsToFloat(out); } @@ -249,7 +607,7 @@ public final class Half { int bits = Float.floatToRawIntBits(f); int s = (bits >>> FP32_SIGN_SHIFT ); int e = (bits >>> FP32_EXPONENT_SHIFT) & FP32_EXPONENT_MASK; - int m = (bits ) & FP32_MANTISSA_MASK; + int m = (bits ) & FP32_SIGNIFICAND_MASK; int outE = 0; int outM = 0; @@ -278,14 +636,12 @@ public final class Half { // Round to nearest "0.5" up int out = (outE << FP16_EXPONENT_SHIFT) | outM; out++; - out |= (s << FP16_SIGN_SHIFT); - return (short) out; + return (short) (out | (s << FP16_SIGN_SHIFT)); } } } - int out = (s << FP16_SIGN_SHIFT) | (outE << FP16_EXPONENT_SHIFT) | outM; - return (short) out; + return (short) ((s << FP16_SIGN_SHIFT) | (outE << FP16_EXPONENT_SHIFT) | outM); } /** @@ -311,16 +667,16 @@ public final class Half { * <li>If the value is inifinity, the string is <code>"Infinity"</code></li> * <li>If the value is 0, the string is <code>"0x0.0p0"</code></li> * <li>If the value has a normalized representation, the exponent and - * mantissa are represented in the string in two fields. The mantissa starts - * with <code>"0x1."</code> followed by its lowercase hexadecimal + * significand are represented in the string in two fields. The significand + * starts with <code>"0x1."</code> followed by its lowercase hexadecimal * representation. Trailing zeroes are removed unless all digits are 0, then - * a single zero is used. The mantissa representation is followed by the + * a single zero is used. The significand representation is followed by the * exponent, represented by <code>"p"</code>, itself followed by a decimal * string of the unbiased exponent</li> - * <li>If the value has a denormal representation, the mantissa starts + * <li>If the value has a subnormal representation, the significand starts * with <code>"0x0."</code> followed by its lowercase hexadecimal * representation. Trailing zeroes are removed unless all digits are 0, then - * a single zero is used. The mantissa representation is followed by the + * a single zero is used. The significand representation is followed by the * exponent, represented by <code>"p-14"</code></li> * </ul> * @@ -333,11 +689,11 @@ public final class Half { int bits = h & 0xffff; int s = (bits >>> FP16_SIGN_SHIFT ); int e = (bits >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK; - int m = (bits ) & FP16_MANTISSA_MASK; + int m = (bits ) & FP16_SIGNIFICAND_MASK; if (e == 0x1f) { // Infinite or NaN if (m == 0) { - if (s == 1) o.append('-'); + if (s != 0) o.append('-'); o.append("Infinity"); } else { o.append("NaN"); @@ -349,14 +705,14 @@ public final class Half { o.append("0x0.0p0"); } else { o.append("0x0."); - String mantissa = Integer.toHexString(m); - o.append(mantissa.replaceFirst("0{2,}$", "")); + String significand = Integer.toHexString(m); + o.append(significand.replaceFirst("0{2,}$", "")); o.append("p-14"); } } else { o.append("0x1."); - String mantissa = Integer.toHexString(m); - o.append(mantissa.replaceFirst("0{2,}$", "")); + String significand = Integer.toHexString(m); + o.append(significand.replaceFirst("0{2,}$", "")); o.append('p'); o.append(Integer.toString(e - FP16_EXPONENT_BIAS)); } diff --git a/core/java/android/view/IPinnedStackController.aidl b/core/java/android/view/IPinnedStackController.aidl index a81eef831f4e..d59be02c7371 100644 --- a/core/java/android/view/IPinnedStackController.aidl +++ b/core/java/android/view/IPinnedStackController.aidl @@ -32,6 +32,11 @@ interface IPinnedStackController { oneway void setInInteractiveMode(boolean inInteractiveMode); /** + * Notifies the controller that the PIP is currently minimized. + */ + oneway void setIsMinimized(boolean isMinimized); + + /** * Notifies the controller that the desired snap mode is to the closest edge. */ oneway void setSnapToEdge(boolean snapToEdge); diff --git a/core/java/android/view/ViewStub.java b/core/java/android/view/ViewStub.java index ec852e88b17e..85d10f199218 100644 --- a/core/java/android/view/ViewStub.java +++ b/core/java/android/view/ViewStub.java @@ -142,11 +142,17 @@ public final class ViewStub extends View { * @see #getInflatedId() * @attr ref android.R.styleable#ViewStub_inflatedId */ - @android.view.RemotableViewMethod + @android.view.RemotableViewMethod(asyncImpl = "setInflatedIdAsync") public void setInflatedId(@IdRes int inflatedId) { mInflatedId = inflatedId; } + /** @hide **/ + public Runnable setInflatedIdAsync(@IdRes int inflatedId) { + mInflatedId = inflatedId; + return null; + } + /** * Returns the layout resource that will be used by {@link #setVisibility(int)} or * {@link #inflate()} to replace this StubbedView @@ -176,11 +182,17 @@ public final class ViewStub extends View { * @see #inflate() * @attr ref android.R.styleable#ViewStub_layout */ - @android.view.RemotableViewMethod + @android.view.RemotableViewMethod(asyncImpl = "setLayoutResourceAsync") public void setLayoutResource(@LayoutRes int layoutResource) { mLayoutResource = layoutResource; } + /** @hide **/ + public Runnable setLayoutResourceAsync(@LayoutRes int layoutResource) { + mLayoutResource = layoutResource; + return null; + } + /** * Set {@link LayoutInflater} to use in {@link #inflate()}, or {@code null} * to use the default. @@ -220,7 +232,7 @@ public final class ViewStub extends View { * @see #inflate() */ @Override - @android.view.RemotableViewMethod + @android.view.RemotableViewMethod(asyncImpl = "setVisibilityAsync") public void setVisibility(int visibility) { if (mInflatedViewRef != null) { View view = mInflatedViewRef.get(); @@ -237,6 +249,43 @@ public final class ViewStub extends View { } } + /** @hide **/ + public Runnable setVisibilityAsync(int visibility) { + if (visibility == VISIBLE || visibility == INVISIBLE) { + ViewGroup parent = (ViewGroup) getParent(); + return new ViewReplaceRunnable(inflateViewNoAdd(parent)); + } else { + return null; + } + } + + private View inflateViewNoAdd(ViewGroup parent) { + final LayoutInflater factory; + if (mInflater != null) { + factory = mInflater; + } else { + factory = LayoutInflater.from(mContext); + } + final View view = factory.inflate(mLayoutResource, parent, false); + + if (mInflatedId != NO_ID) { + view.setId(mInflatedId); + } + return view; + } + + private void replaceSelfWithView(View view, ViewGroup parent) { + final int index = parent.indexOfChild(this); + parent.removeViewInLayout(this); + + final ViewGroup.LayoutParams layoutParams = getLayoutParams(); + if (layoutParams != null) { + parent.addView(view, index, layoutParams); + } else { + parent.addView(view, index); + } + } + /** * Inflates the layout resource identified by {@link #getLayoutResource()} * and replaces this StubbedView in its parent by the inflated layout resource. @@ -250,31 +299,10 @@ public final class ViewStub extends View { if (viewParent != null && viewParent instanceof ViewGroup) { if (mLayoutResource != 0) { final ViewGroup parent = (ViewGroup) viewParent; - final LayoutInflater factory; - if (mInflater != null) { - factory = mInflater; - } else { - factory = LayoutInflater.from(mContext); - } - final View view = factory.inflate(mLayoutResource, parent, - false); - - if (mInflatedId != NO_ID) { - view.setId(mInflatedId); - } - - final int index = parent.indexOfChild(this); - parent.removeViewInLayout(this); - - final ViewGroup.LayoutParams layoutParams = getLayoutParams(); - if (layoutParams != null) { - parent.addView(view, index, layoutParams); - } else { - parent.addView(view, index); - } - - mInflatedViewRef = new WeakReference<View>(view); + final View view = inflateViewNoAdd(parent); + replaceSelfWithView(view, parent); + mInflatedViewRef = new WeakReference<>(view); if (mInflateListener != null) { mInflateListener.onInflate(this, view); } @@ -317,4 +345,18 @@ public final class ViewStub extends View { */ void onInflate(ViewStub stub, View inflated); } + + /** @hide **/ + public class ViewReplaceRunnable implements Runnable { + public final View view; + + ViewReplaceRunnable(View view) { + this.view = view; + } + + @Override + public void run() { + replaceSelfWithView(view, (ViewGroup) getParent()); + } + } } diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 8a9bb33895f4..4195d91f3355 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -509,6 +509,11 @@ public interface WindowManagerPolicy { * Notifies window manager that {@link #isShowingDreamLw} has changed. */ void notifyShowingDreamChanged(); + + /** + * Notifies window manager that {@link #isKeyguardTrustedLw} has changed. + */ + void notifyKeyguardTrustedChanged(); } public interface PointerEventListener { diff --git a/core/java/android/view/inputmethod/InputContentInfo.java b/core/java/android/view/inputmethod/InputContentInfo.java index b39705e0b1fa..7104a2871f21 100644 --- a/core/java/android/view/inputmethod/InputContentInfo.java +++ b/core/java/android/view/inputmethod/InputContentInfo.java @@ -18,11 +18,14 @@ package android.view.inputmethod; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.content.ClipDescription; +import android.content.ContentProvider; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; +import android.os.UserHandle; import com.android.internal.inputmethod.IInputContentUriToken; @@ -33,8 +36,24 @@ import java.security.InvalidParameterException; */ public final class InputContentInfo implements Parcelable { + /** + * The content URI that may or may not have a user ID embedded by + * {@link ContentProvider#maybeAddUserId(Uri, int)}. This always preserves the exact value + * specified to a constructor. In other words, if it had user ID embedded when it was passed + * to the constructor, it still has the same user ID no matter if it is valid or not. + */ @NonNull private final Uri mContentUri; + /** + * The user ID to which {@link #mContentUri} belongs to. If {@link #mContentUri} already + * embedded the user ID when it was specified then this fields has the same user ID. Otherwise + * the user ID is determined based on the process ID when the constructor is called. + * + * <p>CAUTION: If you received {@link InputContentInfo} from a different process, there is no + * guarantee that this value is correct and valid. Never use this for any security purpose</p> + */ + @UserIdInt + private final int mContentUriOwnerUserId; @NonNull private final ClipDescription mDescription; @Nullable @@ -73,6 +92,8 @@ public final class InputContentInfo implements Parcelable { @Nullable Uri linkUri) { validateInternal(contentUri, description, linkUri, true /* throwException */); mContentUri = contentUri; + mContentUriOwnerUserId = + ContentProvider.getUserIdFromUri(mContentUri, UserHandle.myUserId()); mDescription = description; mLinkUri = linkUri; } @@ -139,7 +160,14 @@ public final class InputContentInfo implements Parcelable { * @return Content URI with which the content can be obtained. */ @NonNull - public Uri getContentUri() { return mContentUri; } + public Uri getContentUri() { + // Fix up the content URI when and only when the caller's user ID does not match the owner's + // user ID. + if (mContentUriOwnerUserId != UserHandle.myUserId()) { + return ContentProvider.maybeAddUserId(mContentUri, mContentUriOwnerUserId); + } + return mContentUri; + } /** * @return {@link ClipDescription} object that contains the metadata of {@code #getContentUri()} @@ -203,6 +231,7 @@ public final class InputContentInfo implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { Uri.writeToParcel(dest, mContentUri); + dest.writeInt(mContentUriOwnerUserId); mDescription.writeToParcel(dest, flags); Uri.writeToParcel(dest, mLinkUri); if (mUriToken != null) { @@ -215,6 +244,7 @@ public final class InputContentInfo implements Parcelable { private InputContentInfo(@NonNull Parcel source) { mContentUri = Uri.CREATOR.createFromParcel(source); + mContentUriOwnerUserId = source.readInt(); mDescription = ClipDescription.CREATOR.createFromParcel(source); mLinkUri = Uri.CREATOR.createFromParcel(source); if (source.readInt() == 1) { diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index b2a77d0051b3..18ce260e7136 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -58,6 +58,7 @@ import android.view.RemotableViewMethod; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.view.ViewStub; import android.widget.AdapterView.OnItemClickListener; import com.android.internal.R; @@ -1456,6 +1457,13 @@ public class RemoteViews implements Parcelable, Filter { if (endAction == null) { return ACTION_NOOP; } else { + // Special case view stub + if (endAction instanceof ViewStub.ViewReplaceRunnable) { + root.createTree(); + // Replace child tree + root.findViewTreeById(viewId).replaceView( + ((ViewStub.ViewReplaceRunnable) endAction).view); + } return new RunnableAction(endAction); } } @@ -1581,16 +1589,26 @@ public class RemoteViews implements Parcelable, Filter { if ((target == null) || !(target.mRoot instanceof ViewGroup)) { return ACTION_NOOP; } + final ViewGroup targetVg = (ViewGroup) target.mRoot; if (nestedViews == null) { // Clear all children when nested views omitted target.mChildren = null; - return this; + return new RuntimeAction() { + @Override + public void apply(View root, ViewGroup rootParent, OnClickHandler handler) + throws ActionException { + targetVg.removeAllViews(); + } + }; } else { // Inflate nested views and perform all the async tasks for the child remoteView. final Context context = root.mRoot.getContext(); final AsyncApplyTask task = nestedViews.getAsyncApplyTask( - context, (ViewGroup) target.mRoot, null, handler); + context, targetVg, null, handler); final ViewTree tree = task.doInBackground(); + if (tree == null) { + throw new ActionException(task.mError); + } // Update the global view tree, so that next call to findViewTreeById // goes through the subtree as well. @@ -1600,10 +1618,8 @@ public class RemoteViews implements Parcelable, Filter { @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) throws ActionException { - // This view will exist as we have already made sure - final ViewGroup target = (ViewGroup) root.findViewById(viewId); task.onPostExecute(tree); - target.addView(task.mResult); + targetVg.addView(task.mResult); } }; } @@ -3360,7 +3376,7 @@ public class RemoteViews implements Parcelable, Filter { int count = mRV.mActions.size(); mActions = new Action[count]; for (int i = 0; i < count && !isCancelled(); i++) { - // TODO: check if isCanclled in nested views. + // TODO: check if isCancelled in nested views. mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler); } } else { @@ -3629,7 +3645,7 @@ public class RemoteViews implements Parcelable, Filter { * and can be searched. */ private static class ViewTree { - private final View mRoot; + private View mRoot; private ArrayList<ViewTree> mChildren; @@ -3643,7 +3659,7 @@ public class RemoteViews implements Parcelable, Filter { } mChildren = new ArrayList<>(); - if (mRoot instanceof ViewGroup && mRoot.isRootNamespace()) { + if (mRoot instanceof ViewGroup) { ViewGroup vg = (ViewGroup) mRoot; int count = vg.getChildCount(); for (int i = 0; i < count; i++) { @@ -3668,6 +3684,12 @@ public class RemoteViews implements Parcelable, Filter { return null; } + public void replaceView(View v) { + mRoot = v; + mChildren = null; + createTree(); + } + public View findViewById(int id) { if (mChildren == null) { return mRoot.findViewById(id); @@ -3685,6 +3707,12 @@ public class RemoteViews implements Parcelable, Filter { } private void addViewChild(View v) { + // ViewTree only contains Views which can be found using findViewById. + // If isRootNamespace is true, this view is skipped. + // @see ViewGroup#findViewTraversal(int) + if (v.isRootNamespace()) { + return; + } final ViewTree target; // If the view has a valid id, i.e., if can be found using findViewById, add it to the @@ -3697,7 +3725,7 @@ public class RemoteViews implements Parcelable, Filter { target = this; } - if (v instanceof ViewGroup && v.isRootNamespace()) { + if (v instanceof ViewGroup) { if (target.mChildren == null) { target.mChildren = new ArrayList<>(); ViewGroup vg = (ViewGroup) v; diff --git a/core/java/com/android/internal/os/TransferPipe.java b/core/java/com/android/internal/os/TransferPipe.java index e76b395e9e2d..f9041507ffdd 100644 --- a/core/java/com/android/internal/os/TransferPipe.java +++ b/core/java/com/android/internal/os/TransferPipe.java @@ -16,6 +16,7 @@ package com.android.internal.os; +import java.io.Closeable; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -32,13 +33,13 @@ import android.util.Slog; /** * Helper for transferring data through a pipe from a client app. */ -public final class TransferPipe implements Runnable { +public final class TransferPipe implements Runnable, Closeable { static final String TAG = "TransferPipe"; static final boolean DEBUG = false; static final long DEFAULT_TIMEOUT = 5000; // 5 seconds - final Thread mThread;; + final Thread mThread; final ParcelFileDescriptor[] mFds; FileDescriptor mOutFd; @@ -54,8 +55,13 @@ public final class TransferPipe implements Runnable { } public TransferPipe() throws IOException { + this(null); + } + + public TransferPipe(String bufferPrefix) throws IOException { mThread = new Thread(this, "TransferPipe"); mFds = ParcelFileDescriptor.createPipe(); + mBufferPrefix = bufferPrefix; } ParcelFileDescriptor getReadFd() { @@ -70,6 +76,11 @@ public final class TransferPipe implements Runnable { mBufferPrefix = prefix; } + public static void dumpAsync(IBinder binder, FileDescriptor out, String[] args) + throws IOException, RemoteException { + goDump(binder, out, args); + } + static void go(Caller caller, IInterface iface, FileDescriptor out, String prefix, String[] args) throws IOException, RemoteException { go(caller, iface, out, prefix, args, DEFAULT_TIMEOUT); @@ -86,12 +97,9 @@ public final class TransferPipe implements Runnable { return; } - TransferPipe tp = new TransferPipe(); - try { + try (TransferPipe tp = new TransferPipe()) { caller.go(iface, tp.getWriteFd().getFileDescriptor(), prefix, args); tp.go(out, timeout); - } finally { - tp.kill(); } } @@ -111,12 +119,9 @@ public final class TransferPipe implements Runnable { return; } - TransferPipe tp = new TransferPipe(); - try { + try (TransferPipe tp = new TransferPipe()) { binder.dumpAsync(tp.getWriteFd().getFileDescriptor(), args); tp.go(out, timeout); - } finally { - tp.kill(); } } @@ -173,6 +178,11 @@ public final class TransferPipe implements Runnable { } } + @Override + public void close() { + kill(); + } + public void kill() { synchronized (this) { closeFd(0); diff --git a/core/java/com/android/internal/policy/EmergencyAffordanceManager.java b/core/java/com/android/internal/policy/EmergencyAffordanceManager.java index bed7c1ba4ed3..eb75bd497434 100644 --- a/core/java/com/android/internal/policy/EmergencyAffordanceManager.java +++ b/core/java/com/android/internal/policy/EmergencyAffordanceManager.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Build; +import android.os.UserHandle; import android.provider.Settings; /** @@ -72,7 +73,7 @@ public class EmergencyAffordanceManager { Intent intent = new Intent(Intent.ACTION_CALL_EMERGENCY); intent.setData(getPhoneUri(context)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); + context.startActivityAsUser(intent, UserHandle.CURRENT); } /** diff --git a/core/java/com/android/internal/policy/PipSnapAlgorithm.java b/core/java/com/android/internal/policy/PipSnapAlgorithm.java index cbacf269a0f0..1e2a53ba97e4 100644 --- a/core/java/com/android/internal/policy/PipSnapAlgorithm.java +++ b/core/java/com/android/internal/policy/PipSnapAlgorithm.java @@ -208,15 +208,19 @@ public class PipSnapAlgorithm { final int fromTop = Math.abs(stackBounds.top - movementBounds.top); final int fromRight = Math.abs(movementBounds.right - stackBounds.left); final int fromBottom = Math.abs(movementBounds.bottom - stackBounds.top); + final int boundedLeft = Math.max(movementBounds.left, Math.min(movementBounds.right, + stackBounds.left)); + final int boundedTop = Math.max(movementBounds.top, Math.min(movementBounds.bottom, + stackBounds.top)); boundsOut.set(stackBounds); if (fromLeft <= fromTop && fromLeft <= fromRight && fromLeft <= fromBottom) { - boundsOut.offsetTo(movementBounds.left, stackBounds.top); + boundsOut.offsetTo(movementBounds.left, boundedTop); } else if (fromTop <= fromLeft && fromTop <= fromRight && fromTop <= fromBottom) { - boundsOut.offsetTo(stackBounds.left, movementBounds.top); + boundsOut.offsetTo(boundedLeft, movementBounds.top); } else if (fromRight < fromLeft && fromRight < fromTop && fromRight < fromBottom) { - boundsOut.offsetTo(movementBounds.right, stackBounds.top); + boundsOut.offsetTo(movementBounds.right, boundedTop); } else { - boundsOut.offsetTo(stackBounds.left, movementBounds.bottom); + boundsOut.offsetTo(boundedLeft, movementBounds.bottom); } } diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp index 7387b294bbcd..886c0e6771f4 100644 --- a/core/jni/android_os_HwParcel.cpp +++ b/core/jni/android_os_HwParcel.cpp @@ -267,17 +267,17 @@ static void JHwParcel_native_writeInterfaceToken( const jchar *interfaceName = env->GetStringCritical(interfaceNameObj, NULL); if (interfaceName) { - hardware::Parcel *parcel = - JHwParcel::GetNativeContext(env, thiz)->getParcel(); - - status_t err = parcel->writeInterfaceToken( - String16( - reinterpret_cast<const char16_t *>(interfaceName), - env->GetStringLength(interfaceNameObj))); + String16 nameCopy( + reinterpret_cast<const char16_t *>(interfaceName), + env->GetStringLength(interfaceNameObj)); env->ReleaseStringCritical(interfaceNameObj, interfaceName); interfaceName = NULL; + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + status_t err = parcel->writeInterfaceToken(nameCopy); signalExceptionForError(env, err); } } @@ -294,17 +294,18 @@ static void JHwParcel_native_enforceInterface( const jchar *interfaceName = env->GetStringCritical(interfaceNameObj, NULL); if (interfaceName) { - hardware::Parcel *parcel = - JHwParcel::GetNativeContext(env, thiz)->getParcel(); - - bool valid = parcel->enforceInterface( - String16( - reinterpret_cast<const char16_t *>(interfaceName), - env->GetStringLength(interfaceNameObj))); + String16 interfaceNameCopy( + reinterpret_cast<const char16_t *>(interfaceName), + env->GetStringLength(interfaceNameObj)); env->ReleaseStringCritical(interfaceNameObj, interfaceName); interfaceName = NULL; + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + bool valid = parcel->enforceInterface(interfaceNameCopy); + if (!valid) { jniThrowException( env, diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index bba3d7649226..38078c12657d 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -319,12 +319,6 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode, bool force_mount_namespace) { // See storage config details at http://source.android.com/tech/storage/ - // Create a second private mount namespace for our process - if (unshare(CLONE_NEWNS) == -1) { - ALOGW("Failed to unshare(): %s", strerror(errno)); - return false; - } - String8 storageSource; if (mount_mode == MOUNT_EXTERNAL_DEFAULT) { storageSource = "/mnt/runtime/default"; @@ -336,6 +330,13 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode, // Sane default of no storage visible return true; } + + // Create a second private mount namespace for our process + if (unshare(CLONE_NEWNS) == -1) { + ALOGW("Failed to unshare(): %s", strerror(errno)); + return false; + } + if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage", NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) { ALOGW("Failed to mount %s to /storage: %s", storageSource.string(), strerror(errno)); @@ -628,6 +629,12 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra } } else if (pid > 0) { // the parent process + + // We blocked SIGCHLD prior to a fork, we unblock it here. + if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) { + ALOGE("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)); + RuntimeAbort(env, __LINE__, "Call to sigprocmask(SIG_UNBLOCK, { SIGCHLD }) failed."); + } } return pid; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 4eebea6da46d..0f7b5a53e33c 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2323,6 +2323,13 @@ <permission android:name="android.permission.BIND_VOICE_INTERACTION" android:protectionLevel="signature" /> + <!-- Must be required by a {@link android.service.autofill.AutoFillService}, + to ensure that only the system can bind to it. + <p>Protection level: signature + --> + <permission android:name="android.permission.BIND_AUTO_FILL" + android:protectionLevel="signature" /> + <!-- Must be required by hotword enrollment application, to ensure that only the system can interact with it. @hide <p>Not for use by third-party applications.</p> --> @@ -2937,11 +2944,11 @@ android:protectionLevel="signature" /> <!-- Must be required by an {@link - android.service.notification.NotificationRankerService to ensure that only the system can bind to it. + android.service.notification.NotificationAssistantService to ensure that only the system + can bind to it. <p>Protection level: signature - @hide This is not a third-party API (intended for system apps). --> --> - <permission android:name="android.permission.BIND_NOTIFICATION_RANKER_SERVICE" + <permission android:name="android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE" android:protectionLevel="signature" /> <!-- Must be required by a {@link @@ -3118,6 +3125,17 @@ <permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows an application to manage auto-fill sessions. + @hide <p>Not for use by third-party applications.</p> --> + <permission android:name="android.permission.MANAGE_AUTO_FILL" + android:protectionLevel="signature" /> + + <!-- Allows an app to set the theme overlay in /vendor/overlay + being used. + @hide <p>Not for use by third-party applications.</p> --> + <permission android:name="android.permission.MODIFY_THEME_OVERLAY" + android:protectionLevel="signature" /> + <application android:process="system" android:persistent="true" android:hasCode="false" diff --git a/core/res/res/values-watch/colors_material.xml b/core/res/res/values-watch/colors_material.xml index 18bfd4db5ae0..1456976e897e 100644 --- a/core/res/res/values-watch/colors_material.xml +++ b/core/res/res/values-watch/colors_material.xml @@ -14,13 +14,13 @@ limitations under the License. --> <resources> - <color name="background_material_dark">#ff232e33</color> - <color name="background_floating_material_dark">#ff3e5059</color> + <color name="background_material_dark">#232E33</color> + <color name="background_floating_material_dark">#3E5059</color> - <color name="accent_material_700">#ff2e4978</color> - <color name="accent_material_light">#ff4285f4</color> - <color name="accent_material_dark">#ff5e97f6</color> - <color name="accent_material_50">#ffd0def7</color> + <color name="accent_material_700">#5385DB</color> + <color name="accent_material_light">#75A4F5</color> + <color name="accent_material_dark">#5E97F6</color> + <color name="accent_material_50">#93B7F5</color> <color name="primary_material_dark">#4D4D4D</color> diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml index 89691e90d46e..8f0350a887b9 100644 --- a/core/res/res/values/colors_device_defaults.xml +++ b/core/res/res/values/colors_device_defaults.xml @@ -28,10 +28,10 @@ <color name="tertiary_device_default_settings">@color/tertiary_material_settings</color> <color name="quaternary_device_default_settings">@color/quaternary_material_settings</color> - <color name="accent_device_default_700">@color/material_deep_teal_700</color> + <color name="accent_device_default_700">@color/accent_material_700</color> <color name="accent_device_default_light">@color/accent_material_light</color> <color name="accent_device_default_dark">@color/accent_material_dark</color> - <color name="accent_device_default_50">@color/material_deep_teal_50</color> + <color name="accent_device_default_50">@color/accent_material_50</color> <color name="background_device_default_dark">@color/background_material_dark</color> <color name="background_device_default_light">@color/background_material_light</color> diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml index 92426c6a9bca..37feff8daf7f 100644 --- a/core/res/res/values/colors_material.xml +++ b/core/res/res/values/colors_material.xml @@ -36,8 +36,10 @@ <color name="tertiary_material_settings">@color/material_blue_grey_700</color> <color name="quaternary_material_settings">@color/material_blue_grey_200</color> + <color name="accent_material_700">@color/material_deep_teal_700</color> <color name="accent_material_light">@color/material_deep_teal_500</color> <color name="accent_material_dark">@color/material_deep_teal_200</color> + <color name="accent_material_50">@color/material_deep_teal_50</color> <color name="button_material_dark">#ff5a595b</color> <color name="button_material_light">#ffd6d7d7</color> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index d207c2bdda75..7005afe5b5f6 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1919,6 +1919,12 @@ mirror the content of the default display. --> <bool name="config_localDisplaysMirrorContent">true</bool> + <!-- The default mode for the default display. One of the following values (See Display.java): + 0 - COLOR_MODE_DEFAULT + 7 - COLOR_MODE_SRGB + --> + <integer name="config_defaultDisplayDefaultColorMode">0</integer> + <!-- When true use the linux /dev/input/event subsystem to detect the switch changes on the headphone/microphone jack. When false use the older uevent framework. --> <bool name="config_useDevInputEventForAudioJack">false</bool> @@ -2496,6 +2502,14 @@ Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT --> <integer name="config_defaultPictureInPictureGravity">0x55</integer> + <!-- The minimum aspect ratio (width/height) that is supported for picture-in-picture. Any + ratio smaller than this is considered too tall and thin to be usable. --> + <item name="config_pictureInPictureMinAspectRatio" format="float" type="dimen">0.5</item> + + <!-- The minimum aspect ratio (width/height) that is supported for picture-in-picture. Any + ratio larger than this is considered to wide and short to be usable. --> + <item name="config_pictureInPictureMaxAspectRatio" format="float" type="dimen">2.35</item> + <!-- Controls the snap mode for the docked stack divider 0 - 3 snap targets: left/top has 16:9 ratio, 1:1, and right/bottom has 16:9 ratio 1 - 3 snap targets: fixed ratio, 1:1, (1 - fixed ratio) diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 07c4c6955c62..6c0dc3529b16 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -309,10 +309,13 @@ <java-symbol type="bool" name="config_supportsMultiWindow" /> <java-symbol type="bool" name="config_guestUserEphemeral" /> <java-symbol type="bool" name="config_localDisplaysMirrorContent" /> + <java-symbol type="integer" name="config_defaultDisplayDefaultColorMode" /> <java-symbol type="bool" name="config_enableAppWidgetService" /> <java-symbol type="string" name="config_defaultPictureInPictureScreenEdgeInsets" /> <java-symbol type="string" name="config_defaultPictureInPictureSize" /> <java-symbol type="integer" name="config_defaultPictureInPictureGravity" /> + <java-symbol type="dimen" name="config_pictureInPictureMinAspectRatio" /> + <java-symbol type="dimen" name="config_pictureInPictureMaxAspectRatio" /> <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_threshold" /> <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_factor" /> <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_penalty_threshold" /> diff --git a/core/tests/coretests/res/layout/remote_view_host.xml b/core/tests/coretests/res/layout/remote_view_host.xml index 19d0a738decc..68095081a9d9 100644 --- a/core/tests/coretests/res/layout/remote_view_host.xml +++ b/core/tests/coretests/res/layout/remote_view_host.xml @@ -19,7 +19,7 @@ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/container" android:orientation="vertical" android:layout_width="match_parent" - android:layout_height="match_parent"> -</LinearLayout> + android:layout_height="match_parent" /> diff --git a/core/tests/coretests/res/layout/remote_views_text.xml b/core/tests/coretests/res/layout/remote_views_text.xml new file mode 100644 index 000000000000..a265d2e4b21e --- /dev/null +++ b/core/tests/coretests/res/layout/remote_views_text.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2016 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 + --> + +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/text" + android:layout_width="match_parent" + android:layout_height="match_parent" /> diff --git a/core/tests/coretests/res/layout/remote_views_viewstub.xml b/core/tests/coretests/res/layout/remote_views_viewstub.xml new file mode 100644 index 000000000000..d5327491ac5d --- /dev/null +++ b/core/tests/coretests/res/layout/remote_views_viewstub.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2016 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="0dp" + android:layout_weight="1"> + + <ViewStub android:id="@+id/viewStub" + android:inflatedId="@+id/stub_inflated" + android:layout_width="match_parent" + android:layout_height="match_parent" /> +</FrameLayout> diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java index 7ba46bed8b34..b6b0e68c7b89 100644 --- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java +++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java @@ -20,11 +20,13 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.os.AsyncTask; import android.os.Parcel; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.view.View; +import android.view.ViewGroup; import com.android.frameworks.coretests.R; @@ -38,6 +40,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.concurrent.CountDownLatch; + /** * Tests for RemoteViews. */ @@ -166,4 +172,164 @@ public class RemoteViewsTest { parcel.recycle(); return size; } + + @Test + public void asyncApply_fail() throws Exception { + RemoteViews views = new RemoteViews(mPackage, R.layout.remote_view_test_bad_1); + ViewAppliedListener listener = new ViewAppliedListener(); + views.applyAsync(mContext, mContainer, AsyncTask.THREAD_POOL_EXECUTOR, listener); + + boolean exceptionThrown = false; + try { + listener.waitAndGetView(); + } catch (Exception e) { + exceptionThrown = true; + } + assertTrue(exceptionThrown); + } + + @Test + public void asyncApply() throws Exception { + RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test); + views.setTextViewText(R.id.text, "Dummy"); + + View syncView = views.apply(mContext, mContainer); + + ViewAppliedListener listener = new ViewAppliedListener(); + views.applyAsync(mContext, mContainer, AsyncTask.THREAD_POOL_EXECUTOR, listener); + View asyncView = listener.waitAndGetView(); + + verifyViewTree(syncView, asyncView, "Dummy"); + } + + @Test + public void asyncApply_viewStub() throws Exception { + RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_viewstub); + views.setInt(R.id.viewStub, "setLayoutResource", R.layout.remote_views_text); + // This will cause the view to be inflated + views.setViewVisibility(R.id.viewStub, View.INVISIBLE); + views.setTextViewText(R.id.stub_inflated, "Dummy"); + + View syncView = views.apply(mContext, mContainer); + + ViewAppliedListener listener = new ViewAppliedListener(); + views.applyAsync(mContext, mContainer, AsyncTask.THREAD_POOL_EXECUTOR, listener); + View asyncView = listener.waitAndGetView(); + + verifyViewTree(syncView, asyncView, "Dummy"); + } + + @Test + public void asyncApply_nestedViews() throws Exception { + RemoteViews views = new RemoteViews(mPackage, R.layout.remote_view_host); + views.removeAllViews(R.id.container); + views.addView(R.id.container, createViewChained(1, "row1-c1", "row1-c2", "row1-c3")); + views.addView(R.id.container, createViewChained(5, "row2-c1", "row2-c2")); + views.addView(R.id.container, createViewChained(2, "row3-c1", "row3-c2")); + + View syncView = views.apply(mContext, mContainer); + + ViewAppliedListener listener = new ViewAppliedListener(); + views.applyAsync(mContext, mContainer, AsyncTask.THREAD_POOL_EXECUTOR, listener); + View asyncView = listener.waitAndGetView(); + + verifyViewTree(syncView, asyncView, + "row1-c1", "row1-c2", "row1-c3", "row2-c1", "row2-c2", "row3-c1", "row3-c2"); + } + + @Test + public void asyncApply_viewstub_nestedViews() throws Exception { + RemoteViews viewstub = new RemoteViews(mPackage, R.layout.remote_views_viewstub); + viewstub.setInt(R.id.viewStub, "setLayoutResource", R.layout.remote_view_host); + // This will cause the view to be inflated + viewstub.setViewVisibility(R.id.viewStub, View.INVISIBLE); + viewstub.addView(R.id.stub_inflated, createViewChained(1, "row1-c1", "row1-c2", "row1-c3")); + + RemoteViews views = new RemoteViews(mPackage, R.layout.remote_view_host); + views.removeAllViews(R.id.container); + views.addView(R.id.container, viewstub); + views.addView(R.id.container, createViewChained(5, "row2-c1", "row2-c2")); + + View syncView = views.apply(mContext, mContainer); + + ViewAppliedListener listener = new ViewAppliedListener(); + views.applyAsync(mContext, mContainer, AsyncTask.THREAD_POOL_EXECUTOR, listener); + View asyncView = listener.waitAndGetView(); + + verifyViewTree(syncView, asyncView, "row1-c1", "row1-c2", "row1-c3", "row2-c1", "row2-c2"); + } + + private RemoteViews createViewChained(int depth, String... texts) { + RemoteViews result = new RemoteViews(mPackage, R.layout.remote_view_host); + + // Create depth + RemoteViews parent = result; + while(depth > 0) { + depth--; + RemoteViews child = new RemoteViews(mPackage, R.layout.remote_view_host); + parent.addView(R.id.container, child); + parent = child; + } + + // Add texts + for (String text : texts) { + RemoteViews child = new RemoteViews(mPackage, R.layout.remote_views_text); + child.setTextViewText(R.id.text, text); + parent.addView(R.id.container, child); + } + return result; + } + + private void verifyViewTree(View v1, View v2, String... texts) { + ArrayList<String> expectedTexts = new ArrayList<>(Arrays.asList(texts)); + verifyViewTreeRecur(v1, v2, expectedTexts); + // Verify that all expected texts were found + assertEquals(0, expectedTexts.size()); + } + + private void verifyViewTreeRecur(View v1, View v2, ArrayList<String> expectedTexts) { + assertEquals(v1.getClass(), v2.getClass()); + + if (v1 instanceof TextView) { + String text = ((TextView) v1).getText().toString(); + assertEquals(text, ((TextView) v2).getText().toString()); + // Verify that the text was one of the expected texts and remove it from the list + assertTrue(expectedTexts.remove(text)); + } else if (v1 instanceof ViewGroup) { + ViewGroup vg1 = (ViewGroup) v1; + ViewGroup vg2 = (ViewGroup) v2; + assertEquals(vg1.getChildCount(), vg2.getChildCount()); + for (int i = vg1.getChildCount() - 1; i >= 0; i--) { + verifyViewTreeRecur(vg1.getChildAt(i), vg2.getChildAt(i), expectedTexts); + } + } + } + + private class ViewAppliedListener implements RemoteViews.OnViewAppliedListener { + + private final CountDownLatch mLatch = new CountDownLatch(1); + private View mView; + private Exception mError; + + @Override + public void onViewApplied(View v) { + mView = v; + mLatch.countDown(); + } + + @Override + public void onError(Exception e) { + mError = e; + mLatch.countDown(); + } + + public View waitAndGetView() throws Exception { + mLatch.await(); + + if (mError != null) { + throw new Exception(mError); + } + return mView; + } + } } diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index 4fc789249c5c..5e59a64b23d4 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -26,6 +26,7 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.net.Uri; +import android.os.Binder; import android.os.Build; import android.os.IBinder; import android.os.Looper; @@ -630,7 +631,7 @@ public final class KeyChain { if (!mConnectedAtLeastOnce) { mConnectedAtLeastOnce = true; try { - q.put(IKeyChainService.Stub.asInterface(service)); + q.put(IKeyChainService.Stub.asInterface(Binder.allowBlocking(service))); } catch (InterruptedException e) { // will never happen, since the queue starts with one available slot } diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 7c97e770b723..5e21dfc6db8a 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -738,7 +738,7 @@ void SkiaCanvas::drawGlyphs(const uint16_t* text, const float* positions, int co void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset, const SkPaint& paint, const SkPath& path, size_t start, size_t end) { const int N = end - start; - SkAutoSMalloc<1024> storage(N * (sizeof(uint16_t) + sizeof(SkRSXform))); + SkAutoSTMalloc<1024, uint8_t> storage(N * (sizeof(uint16_t) + sizeof(SkRSXform))); SkRSXform* xform = (SkRSXform*)storage.get(); uint16_t* glyphs = (uint16_t*)(xform + N); SkPathMeasure meas(path, false); diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp index a204d5cb8f71..6973209c4626 100644 --- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp +++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp @@ -215,7 +215,7 @@ static void DrawSpotShadowGeneral(SkCanvas* canvas, const Shape& shape, float ca static void DrawRRectShadows(const SkRect& casterRect, SkScalar casterCornerRadius, SkScalar ambientAlpha, SkScalar spotAlpha, SkScalar casterAlpha, SkScalar casterZValue, SkScalar scaleFactor, SkCanvas* canvas) { - SkASSERT(cornerRadius >= 0.0f); + SkASSERT(casterCornerRadius >= 0.0f); // For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space const SkScalar minRadius = 0.5f / scaleFactor; @@ -387,7 +387,7 @@ static void DrawRRectShadows(const SkRect& casterRect, SkScalar casterCornerRadi static void DrawRRectShadowsWithClip(const SkRect& casterRect, SkScalar casterCornerRadius, SkScalar ambientAlpha, SkScalar spotAlpha, SkScalar casterZValue, SkScalar scaleFactor, const SkRRect& clipRR, SkCanvas* canvas) { - SkASSERT(cornerRadius >= 0.0f); + SkASSERT(casterCornerRadius >= 0.0f); const bool isOval = casterCornerRadius >= std::max(SkScalarHalf(casterRect.width()), SkScalarHalf(casterRect.height())); diff --git a/location/java/android/location/IFusedProvider.aidl b/location/java/android/location/IFusedProvider.aidl index 8870d2a2a420..e86ad1ac5147 100644 --- a/location/java/android/location/IFusedProvider.aidl +++ b/location/java/android/location/IFusedProvider.aidl @@ -22,11 +22,11 @@ import android.hardware.location.IFusedLocationHardware; * Interface definition for Location providers that require FLP services. * @hide */ -interface IFusedProvider { +oneway interface IFusedProvider { /** * Provides access to a FusedLocationHardware instance needed for the provider to work. * * @param instance The FusedLocationHardware available for the provider to use. */ void onFusedLocationHardwareChange(in IFusedLocationHardware instance); -}
\ No newline at end of file +} diff --git a/location/java/android/location/IGeofenceProvider.aidl b/location/java/android/location/IGeofenceProvider.aidl index 5a5fdc635b00..d4ff0dd70bf2 100644 --- a/location/java/android/location/IGeofenceProvider.aidl +++ b/location/java/android/location/IGeofenceProvider.aidl @@ -23,6 +23,6 @@ import android.hardware.location.IGeofenceHardware; * * {@hide} */ -interface IGeofenceProvider { +oneway interface IGeofenceProvider { void setGeofenceHardware(in IGeofenceHardware proxy); } diff --git a/media/java/android/media/IRingtonePlayer.aidl b/media/java/android/media/IRingtonePlayer.aidl index 4b1e39f2fc9d..5f6686a88a7e 100644 --- a/media/java/android/media/IRingtonePlayer.aidl +++ b/media/java/android/media/IRingtonePlayer.aidl @@ -26,14 +26,14 @@ import android.os.UserHandle; */ interface IRingtonePlayer { /** Used for Ringtone.java playback */ - void play(IBinder token, in Uri uri, in AudioAttributes aa, float volume, boolean looping); - void stop(IBinder token); + oneway void play(IBinder token, in Uri uri, in AudioAttributes aa, float volume, boolean looping); + oneway void stop(IBinder token); boolean isPlaying(IBinder token); - void setPlaybackProperties(IBinder token, float volume, boolean looping); + oneway void setPlaybackProperties(IBinder token, float volume, boolean looping); /** Used for Notification sound playback. */ - void playAsync(in Uri uri, in UserHandle user, boolean looping, in AudioAttributes aa); - void stopAsync(); + oneway void playAsync(in Uri uri, in UserHandle user, boolean looping, in AudioAttributes aa); + oneway void stopAsync(); /** Return the title of the media. */ String getTitle(in Uri uri); diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml index c2ca9989689c..f442219a2ffc 100644 --- a/packages/ExtServices/AndroidManifest.xml +++ b/packages/ExtServices/AndroidManifest.xml @@ -26,16 +26,6 @@ android:directBootAware="true"> <library android:name="android.ext.services"/> - - <service android:name=".notification.Ranker" - android:label="@string/notification_ranker" - android:permission="android.permission.BIND_NOTIFICATION_RANKER_SERVICE" - android:exported="true"> - <intent-filter> - <action android:name="android.service.notification.NotificationRankerService" /> - </intent-filter> - </service> - </application> </manifest> diff --git a/packages/ExtServices/res/values/strings.xml b/packages/ExtServices/res/values/strings.xml index b77ff1055cb1..531e5171dec8 100644 --- a/packages/ExtServices/res/values/strings.xml +++ b/packages/ExtServices/res/values/strings.xml @@ -16,6 +16,4 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_name">Android Services Library</string> - <string name="notification_ranker">Android Notification Ranking Service</string> - <string name="notification_ranker_autobundle_explanation">Auto-grouping updated by Ranking Service</string> </resources> diff --git a/packages/ExtServices/src/android/ext/services/notification/Ranker.java b/packages/ExtServices/src/android/ext/services/notification/Ranker.java deleted file mode 100644 index 2feb51f7a710..000000000000 --- a/packages/ExtServices/src/android/ext/services/notification/Ranker.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2016 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.ext.services.notification; - -import android.os.Bundle; -import android.os.UserHandle; -import android.service.notification.Adjustment; -import android.service.notification.NotificationRankerService; -import android.service.notification.StatusBarNotification; -import android.util.Log; -import android.util.Slog; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; - -import android.ext.services.R; - -/** - * Class that provides an updatable ranker module for the notification manager. - * TODO: delete - */ -public final class Ranker extends NotificationRankerService { - private static final String TAG = "RocketRanker"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - - @Override - public Adjustment onNotificationEnqueued(StatusBarNotification sbn, int importance, - boolean user) { - return null; - } -}
\ No newline at end of file diff --git a/packages/PrintSpooler/res/values-bs-rBA/strings.xml b/packages/PrintSpooler/res/values-bs-rBA/strings.xml index a50391fb391f..2450be395279 100644 --- a/packages/PrintSpooler/res/values-bs-rBA/strings.xml +++ b/packages/PrintSpooler/res/values-bs-rBA/strings.xml @@ -32,7 +32,7 @@ <string name="template_page_range" msgid="428638530038286328">"Opseg od <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string> <string name="pages_range_example" msgid="8558694453556945172">"npr. 1—5,8,11—13"</string> <string name="print_preview" msgid="8010217796057763343">"Pregled prije štampanja"</string> - <string name="install_for_print_preview" msgid="6366303997385509332">"Instaliraj PDF preglednik za prikaz"</string> + <string name="install_for_print_preview" msgid="6366303997385509332">"Instaliraj PDF pregledavač za prikaz"</string> <string name="printing_app_crashed" msgid="854477616686566398">"Aplikacija za štampanje je prestala raditi"</string> <string name="generating_print_job" msgid="3119608742651698916">"Kreiranje zadatka za štampu"</string> <string name="save_as_pdf" msgid="5718454119847596853">"Sačuvaj kao PDF"</string> diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 227d0e944e6e..d4c7c7ad8cb1 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -112,6 +112,7 @@ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" /> <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" /> <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" /> + <uses-permission android:name="android.permission.MANAGE_AUTO_FILL" /> <!-- Permission needed to rename bugreport notifications (so they're not shown as Shell) --> <uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" /> <!-- Permission needed to hold a wakelock in dumpstate.cpp (drop_root_user()) --> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index e07d8650591a..0b5383acf4d2 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -171,6 +171,8 @@ <!-- shortcut manager --> <uses-permission android:name="android.permission.RESET_SHORTCUT_MANAGER_THROTTLING" /> + <uses-permission android:name="android.permission.MODIFY_THEME_OVERLAY" /> + <application android:name=".SystemUIApplication" android:persistent="true" diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index ea070b3e811b..5294c9c46429 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Maak <xliff:g id="ID_1">%s</xliff:g>-instellings oop."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Wysig volgorde van instellings."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Bladsy <xliff:g id="ID_1">%1$d</xliff:g> van <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Vou uit"</string> </resources> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 837d67794729..601930a91591 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"የ<xliff:g id="ID_1">%s</xliff:g> ቅንብሮችን ክፈት።"</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"የቅንብሮድ ቅደም-ተከተል አርትዕ።"</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"ገጽ <xliff:g id="ID_1">%1$d</xliff:g> ከ <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"ዘርጋ"</string> </resources> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index f207dbc124a7..502a94eda666 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -656,6 +656,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"فتح إعدادات <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"تعديل ترتيب الإعدادات."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"الصفحة <xliff:g id="ID_1">%1$d</xliff:g> من <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"توسيع"</string> </resources> diff --git a/packages/SystemUI/res/values-az-rAZ/strings.xml b/packages/SystemUI/res/values-az-rAZ/strings.xml index c4b0f73eefe9..1bc9e59e9046 100644 --- a/packages/SystemUI/res/values-az-rAZ/strings.xml +++ b/packages/SystemUI/res/values-az-rAZ/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> ayarlarını açın."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Ayarların sıralanmasını redaktə edin."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g> səhifədən <xliff:g id="ID_1">%1$d</xliff:g> səhifə"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Genişləndirin"</string> </resources> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index e70e4855a805..8105d9ea50b2 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -650,6 +650,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Otvori podešavanja za <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Izmeni redosled podešavanja."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_1">%1$d</xliff:g>. strana od <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Proširi"</string> </resources> diff --git a/packages/SystemUI/res/values-be-rBY/strings.xml b/packages/SystemUI/res/values-be-rBY/strings.xml index 7f7a67ff789f..83c67d4b81a7 100644 --- a/packages/SystemUI/res/values-be-rBY/strings.xml +++ b/packages/SystemUI/res/values-be-rBY/strings.xml @@ -654,6 +654,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Адкрыць налады <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Змяніць парадак налад."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Старонка <xliff:g id="ID_1">%1$d</xliff:g> з <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Разгарнуць"</string> </resources> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 16dfeebbf9fc..bf3a6148c973 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Отваряне на настройките за <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Редактиране на подредбата на настройките."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Страница <xliff:g id="ID_1">%1$d</xliff:g> от <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Разгъване"</string> </resources> diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml index f8ee362540be..bd599bcdb2ab 100644 --- a/packages/SystemUI/res/values-bn-rBD/strings.xml +++ b/packages/SystemUI/res/values-bn-rBD/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> সেটিংস খুলুন৷"</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"ক্রম বা সেটিংস সম্পাদনা করুন৷"</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g>টির মধ্যে <xliff:g id="ID_1">%1$d</xliff:g> নং পৃষ্ঠা"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"প্রসারিত করুন"</string> </resources> diff --git a/packages/SystemUI/res/values-bs-rBA/strings.xml b/packages/SystemUI/res/values-bs-rBA/strings.xml index 0e91713f2571..5ba720ccef7a 100644 --- a/packages/SystemUI/res/values-bs-rBA/strings.xml +++ b/packages/SystemUI/res/values-bs-rBA/strings.xml @@ -652,6 +652,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Otvori postavke za: <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Urediti raspored postavki."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Stranica <xliff:g id="ID_1">%1$d</xliff:g> od <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Proširi"</string> </resources> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index d258943d46e9..927e75b12ee2 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Obre la configuració per a <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Edita l\'ordre de la configuració."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Pàgina <xliff:g id="ID_1">%1$d</xliff:g> (<xliff:g id="ID_2">%2$d</xliff:g> en total)"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Desplega"</string> </resources> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 845c8b527ecf..69629a9eb208 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -654,6 +654,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Otevřít nastavení aplikace <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Upravit pořadí nastavení."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Stránka <xliff:g id="ID_1">%1$d</xliff:g> z <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Rozbalit"</string> </resources> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index bb340a678b17..b0a828ff0e26 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Åbn <xliff:g id="ID_1">%s</xliff:g>-indstillinger."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Rediger rækkefølgen af indstillinger."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Side <xliff:g id="ID_1">%1$d</xliff:g> af <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Udvid"</string> </resources> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index faabbfa60ac0..b3a457d7e326 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -650,6 +650,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Einstellungen für <xliff:g id="ID_1">%s</xliff:g> öffnen."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Reihenfolge der Einstellungen bearbeiten."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Seite <xliff:g id="ID_1">%1$d</xliff:g> von <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Maximieren"</string> </resources> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 0f5f44711581..050d7f8ca18e 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Άνοιγμα ρυθμίσεων <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Επεξεργασία σειράς ρυθμίσεων."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Σελίδα <xliff:g id="ID_1">%1$d</xliff:g> από <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Ανάπτυξη"</string> </resources> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 14d2a19ceae2..9aca3cc290e8 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Open <xliff:g id="ID_1">%s</xliff:g> settings."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Edit order of settings."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Page <xliff:g id="ID_1">%1$d</xliff:g> of <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Expand"</string> </resources> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 14d2a19ceae2..9aca3cc290e8 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Open <xliff:g id="ID_1">%s</xliff:g> settings."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Edit order of settings."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Page <xliff:g id="ID_1">%1$d</xliff:g> of <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Expand"</string> </resources> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 14d2a19ceae2..9aca3cc290e8 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Open <xliff:g id="ID_1">%s</xliff:g> settings."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Edit order of settings."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Page <xliff:g id="ID_1">%1$d</xliff:g> of <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Expand"</string> </resources> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 3b92a3acf05a..90c2ca1a8817 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -650,6 +650,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Abrir configuración de <xliff:g id="ID_1">%s</xliff:g>"</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Editar orden de configuración"</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Expandir"</string> </resources> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 600d7d504dbf..0a45ac1cae4d 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -650,6 +650,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Abrir ajustes de <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Cambiar el orden de los ajustes."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Mostrar"</string> </resources> diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml index 884eac5ddb3b..84fadb2b50d6 100644 --- a/packages/SystemUI/res/values-et-rEE/strings.xml +++ b/packages/SystemUI/res/values-et-rEE/strings.xml @@ -650,6 +650,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Ava teenuse <xliff:g id="ID_1">%s</xliff:g> seaded."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Muuda seadete järjestust."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Leht <xliff:g id="ID_1">%1$d</xliff:g>/<xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Laiendamine"</string> </resources> diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml index b5e6696bef58..d6578a24a0c4 100644 --- a/packages/SystemUI/res/values-eu-rES/strings.xml +++ b/packages/SystemUI/res/values-eu-rES/strings.xml @@ -650,6 +650,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Ireki <xliff:g id="ID_1">%s</xliff:g> ezarpenak."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Editatu ezarpenen ordena."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_1">%1$d</xliff:g>/<xliff:g id="ID_2">%2$d</xliff:g> orria"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Zabaldu"</string> </resources> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 866904cf9a51..e00c5b5fbf5f 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"باز کردن تنظیمات <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"ویرایش ترتیب تنظیمات."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"صفحه <xliff:g id="ID_1">%1$d</xliff:g> از <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"بزرگ کردن"</string> </resources> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 144704dce8b5..08572fe22ffe 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Avaa kohteen <xliff:g id="ID_1">%s</xliff:g> asetukset."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Muokkaa asetusten järjestystä."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Sivu <xliff:g id="ID_1">%1$d</xliff:g>/<xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Laajenna"</string> </resources> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index b5c227ebf01f..830bfa7b6269 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -650,6 +650,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Ouvrir les paramètres <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Modifier l\'ordre des paramètres."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Page <xliff:g id="ID_1">%1$d</xliff:g> sur <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Développer"</string> </resources> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 008d06180e98..3b138c2ce244 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -650,6 +650,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Ouvrir les paramètres <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Modifier l\'ordre des paramètres."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Page <xliff:g id="ID_1">%1$d</xliff:g> sur <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Développer"</string> </resources> diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml index cca60a86b3a9..579335d5ef20 100644 --- a/packages/SystemUI/res/values-gl-rES/strings.xml +++ b/packages/SystemUI/res/values-gl-rES/strings.xml @@ -650,6 +650,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Abrir a configuración de <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Editar a orde das opcións de configuración."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Páxina <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Despregar"</string> </resources> diff --git a/packages/SystemUI/res/values-gu-rIN/strings.xml b/packages/SystemUI/res/values-gu-rIN/strings.xml index 558a90a884bd..48a6d0fe699b 100644 --- a/packages/SystemUI/res/values-gu-rIN/strings.xml +++ b/packages/SystemUI/res/values-gu-rIN/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> સેટિંગ્સ ખોલો."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"સેટિંગ્સનો ક્રમ સંપાદિત કરો."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g> માંથી <xliff:g id="ID_1">%1$d</xliff:g> પૃષ્ઠ"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"વિસ્તૃત કરો"</string> </resources> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 1fff05172bcb..f482caaa393a 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> सेटिंग खोलें."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"सेटिंग का क्रम संपादित करें."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"पृष्ठ <xliff:g id="ID_2">%2$d</xliff:g> में से <xliff:g id="ID_1">%1$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"विस्तृत करें"</string> </resources> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index ae7e0c4360e3..7ff63bdac12f 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -650,6 +650,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Otvaranje postavki za <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Uređivanje redoslijeda postavki."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Stranica <xliff:g id="ID_1">%1$d</xliff:g> od <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Proširivanje"</string> </resources> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index ec62e1c44b40..bd0805bf5d2a 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"A(z) <xliff:g id="ID_1">%s</xliff:g> beállításainak megnyitása."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Beállítások sorrendjének szerkesztése."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_1">%1$d</xliff:g>. oldal, összesen: <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Kibontás"</string> </resources> diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml index 80cb9770b217..2f5358e3294c 100644 --- a/packages/SystemUI/res/values-hy-rAM/strings.xml +++ b/packages/SystemUI/res/values-hy-rAM/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Բացել <xliff:g id="ID_1">%s</xliff:g> կարգավորումները:"</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Խմբագրել կարգավորումների հերթականությունը:"</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Էջ <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Ընդարձակել"</string> </resources> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 93960e81b3c9..6444ae57bf0c 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Buka setelan <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Edit urutan setelan."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Halaman <xliff:g id="ID_1">%1$d</xliff:g> dari <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Luaskan"</string> </resources> diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml index 71ef4bbf8fff..7e08a01f752e 100644 --- a/packages/SystemUI/res/values-is-rIS/strings.xml +++ b/packages/SystemUI/res/values-is-rIS/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Opna <xliff:g id="ID_1">%s</xliff:g> stillingar."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Breyta röð stillinga."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Blaðsíða <xliff:g id="ID_1">%1$d</xliff:g> af <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Stækka"</string> </resources> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 12d0ed6069ea..8ffb10d2641f 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -650,6 +650,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Apri le impostazioni <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Modifica l\'ordine delle impostazioni."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Pagina <xliff:g id="ID_1">%1$d</xliff:g> di <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Espandi"</string> </resources> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index f5656030e13c..a6d7983c6096 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -652,6 +652,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"פתיחת הגדרות של <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"עריכת סדר ההגדרות."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"דף <xliff:g id="ID_1">%1$d</xliff:g> מתוך <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"הרחב"</string> </resources> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 37bea17f79b9..6ef9f73a6aa6 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -650,6 +650,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> の設定を開きます。"</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"設定の順序を編集します。"</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"ページ <xliff:g id="ID_1">%1$d</xliff:g>/<xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"展開"</string> </resources> diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml index 15f5d638b5e0..695279da446b 100644 --- a/packages/SystemUI/res/values-ka-rGE/strings.xml +++ b/packages/SystemUI/res/values-ka-rGE/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> პარამეტრების გახსნა."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"პარამეტრების მიმდევრობის რედაქტირება."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"გვერდი <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>-დან"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"გაშლა"</string> </resources> diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml index 477ab821767c..ba71182b8926 100644 --- a/packages/SystemUI/res/values-kk-rKZ/strings.xml +++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> параметрлерін ашу."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Параметрлер тәртібін өзгерту."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g> ішінен <xliff:g id="ID_1">%1$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Жаю"</string> </resources> diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml index 45771d9ec5fa..b222555765f5 100644 --- a/packages/SystemUI/res/values-km-rKH/strings.xml +++ b/packages/SystemUI/res/values-km-rKH/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"បើការកំណត់ <xliff:g id="ID_1">%s</xliff:g>"</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"កែលំដាប់ការកំណត់"</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"ទំព័រ <xliff:g id="ID_1">%1$d</xliff:g> នៃ <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"ពង្រីក"</string> </resources> diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml index 87c48dd185db..b2a6f27e504b 100644 --- a/packages/SystemUI/res/values-kn-rIN/strings.xml +++ b/packages/SystemUI/res/values-kn-rIN/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ತೆರೆಯಿರಿ."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"ಸೆಟ್ಟಿಂಗ್ಗಳ ಕ್ರಮವನ್ನು ಎಡಿಟ್ ಮಾಡಿ."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g> ರಲ್ಲಿ <xliff:g id="ID_1">%1$d</xliff:g> ಪುಟ"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"ವಿಸ್ತೃತಗೊಳಿಸು"</string> </resources> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 3e0cdfef4ff6..cefb57a514b1 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -650,6 +650,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> 설정 열기"</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"설정 순서 수정"</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g>페이지 중 <xliff:g id="ID_1">%1$d</xliff:g>페이지"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"펼치기"</string> </resources> diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml index 2d8be38513e5..e013c6b7c383 100644 --- a/packages/SystemUI/res/values-ky-rKG/strings.xml +++ b/packages/SystemUI/res/values-ky-rKG/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> жөндөөлөрүн ачуу."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Жөндөөлөрдүн иретин өзгөртүү."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g> ичинен <xliff:g id="ID_1">%1$d</xliff:g>-бет"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Жайып көрсөтүү"</string> </resources> diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml index 93098ade4fce..94e789a1325a 100644 --- a/packages/SystemUI/res/values-lo-rLA/strings.xml +++ b/packages/SystemUI/res/values-lo-rLA/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"ເປີດການຕັ້ງຄ່າ <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"ແກ້ໄຂລຳດັບການຕັ້ງຄ່າ."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_1">%1$d</xliff:g> ຈາກທັງໝົດ <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"ຂະຫຍາຍ"</string> </resources> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 02e614c06b12..5af5979ae904 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -652,6 +652,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Atidaryti „<xliff:g id="ID_1">%s</xliff:g>“ nustatymus."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Redaguoti nustatymų tvarką."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_1">%1$d</xliff:g> psl. iš <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Išskleisti"</string> </resources> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 82f5f22e8270..503c8c8bd742 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -650,6 +650,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Atvērt <xliff:g id="ID_1">%s</xliff:g> iestatījumus."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Rediģēt iestatījumu secību."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_1">%1$d</xliff:g>. lpp. no <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Izvērst"</string> </resources> diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml index fcaa26f39cb8..62fc35081cbc 100644 --- a/packages/SystemUI/res/values-mk-rMK/strings.xml +++ b/packages/SystemUI/res/values-mk-rMK/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Отворете ги поставките на <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Уредете го редоследот на поставките."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Страница <xliff:g id="ID_1">%1$d</xliff:g> од <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Проширете"</string> </resources> diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml index 44d76d5d4c8c..9e7d2f29fd91 100644 --- a/packages/SystemUI/res/values-ml-rIN/strings.xml +++ b/packages/SystemUI/res/values-ml-rIN/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> ക്രമീകരണം തുറക്കുക."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"ക്രമീകരണ ക്രമം എഡിറ്റുചെയ്യുക."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"പേജ് <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"വികസിപ്പിക്കുക"</string> </resources> diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml index 1c7bc381cb47..04fb4c23cc7d 100644 --- a/packages/SystemUI/res/values-mn-rMN/strings.xml +++ b/packages/SystemUI/res/values-mn-rMN/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> тохиргоог нээнэ үү."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Тохиргооны дарааллыг өөрчилнө үү."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g>-н <xliff:g id="ID_1">%1$d</xliff:g>-р хуудас"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Дэлгэх"</string> </resources> diff --git a/packages/SystemUI/res/values-mr-rIN/strings.xml b/packages/SystemUI/res/values-mr-rIN/strings.xml index 71d6677dc817..a21019ad50f7 100644 --- a/packages/SystemUI/res/values-mr-rIN/strings.xml +++ b/packages/SystemUI/res/values-mr-rIN/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> सेटिंग्ज उघडा."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"सेटिंग्जचा क्रम संपादित करा."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"पृष्ठ <xliff:g id="ID_2">%2$d</xliff:g> पैकी <xliff:g id="ID_1">%1$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"विस्तृत करा"</string> </resources> diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml index bc9d79fe61a8..ce942952ccd0 100644 --- a/packages/SystemUI/res/values-ms-rMY/strings.xml +++ b/packages/SystemUI/res/values-ms-rMY/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Buka tetapan <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Edit susunan tetapan."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Halaman <xliff:g id="ID_1">%1$d</xliff:g> daripada <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Kembangkan"</string> </resources> diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml index 37c002ca7d18..8ab1de0f707e 100644 --- a/packages/SystemUI/res/values-my-rMM/strings.xml +++ b/packages/SystemUI/res/values-my-rMM/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> ဆက်တင်များကို ဖွင့်ပါ။"</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"ဆက်တင်များ၏ အစီအစဉ်ကို တည်းဖြတ်ပါ။"</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"စာမျက်နှာ <xliff:g id="ID_2">%2$d</xliff:g> အနက်မှ စာမျက်နှာ <xliff:g id="ID_1">%1$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"ချဲ့ရန်"</string> </resources> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 025c270dc33c..c6312e1b5697 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Åpne <xliff:g id="ID_1">%s</xliff:g>-innstillingene."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Endre rekkefølgen på innstillingene."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Side <xliff:g id="ID_1">%1$d</xliff:g> av <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Vis"</string> </resources> diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml index 3ed57827e512..f87223c4aff8 100644 --- a/packages/SystemUI/res/values-ne-rNP/strings.xml +++ b/packages/SystemUI/res/values-ne-rNP/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> सम्बन्धी सेटिङहरूलाई खोल्नुहोस्।"</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"सेटिङहरूको क्रमलाई सम्पादन गर्नुहोस्।"</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g> मध्ये पृष्ठ <xliff:g id="ID_1">%1$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"विस्तृत गर्नुहोस्"</string> </resources> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 13ef0f61e80f..7c12b5fdf086 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g>-instellingen openen."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Volgorde van instellingen bewerken."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Pagina <xliff:g id="ID_1">%1$d</xliff:g> van <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Uitvouwen"</string> </resources> diff --git a/packages/SystemUI/res/values-pa-rIN/strings.xml b/packages/SystemUI/res/values-pa-rIN/strings.xml index 553b6048c7a8..f25662b745f6 100644 --- a/packages/SystemUI/res/values-pa-rIN/strings.xml +++ b/packages/SystemUI/res/values-pa-rIN/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ।"</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"ਸੈਟਿੰਗਾਂ ਦੇ ਕ੍ਰਮ ਦਾ ਸੰਪਾਦਨ ਕਰੋ।"</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g> ਦਾ <xliff:g id="ID_1">%1$d</xliff:g> ਪੰਨਾ"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"ਵਿਸਤਾਰ ਕਰੋ"</string> </resources> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 5c7ea4fd25c5..d037cdacd0c2 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Abrir as definições de <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Editar a ordem das definições."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Expandir"</string> </resources> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 0d42645df5c8..e33ebb5a3c78 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -652,6 +652,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Deschideți setările <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Editați ordinea setărilor."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Pagina <xliff:g id="ID_1">%1$d</xliff:g> din <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Extindeți"</string> </resources> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 9adc9fe8abca..0fbb0bd3c413 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -654,6 +654,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Открыть настройки <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Изменить порядок быстрых настроек."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Страница <xliff:g id="ID_1">%1$d</xliff:g> из <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Развернуть"</string> </resources> diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml index 6739ea110d77..c4d23788d7f3 100644 --- a/packages/SystemUI/res/values-si-rLK/strings.xml +++ b/packages/SystemUI/res/values-si-rLK/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> සැකසීම් විවෘත කරන්න."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"සැකසීම්වල අනුපිළිවෙළ සංංස්කරණය කරන්න."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g> න් <xliff:g id="ID_1">%1$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"දිග හරින්න"</string> </resources> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 44881bd0eef0..23daf91fb07b 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -654,6 +654,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Otvoriť nastavenia <xliff:g id="ID_1">%s</xliff:g>"</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Upraviť poradie nastavení"</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Strana <xliff:g id="ID_1">%1$d</xliff:g> z <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Rozbaliť"</string> </resources> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index cc11704358bc..90985e78b12a 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -654,6 +654,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Odpri nastavitve za <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Uredi vrstni red nastavitev."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_1">%1$d</xliff:g>. stran od <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Razširi"</string> </resources> diff --git a/packages/SystemUI/res/values-sq-rAL/strings.xml b/packages/SystemUI/res/values-sq-rAL/strings.xml index 55598bd91cc6..9616c073aca2 100644 --- a/packages/SystemUI/res/values-sq-rAL/strings.xml +++ b/packages/SystemUI/res/values-sq-rAL/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Hap cilësimet e <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Modifiko rendin e cilësimeve."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Faqja <xliff:g id="ID_1">%1$d</xliff:g> nga <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Zgjero"</string> </resources> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 22bd8530e73e..667c65d4e724 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -650,6 +650,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Отвори подешавања за <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Измени редослед подешавања."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_1">%1$d</xliff:g>. страна од <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Прошири"</string> </resources> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 87acdb3a33b9..0a9501784f10 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Öppna <xliff:g id="ID_1">%s</xliff:g>-inställningarna."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Ändra ordning på inställningarna."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Sida <xliff:g id="ID_1">%1$d</xliff:g> av <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Utöka"</string> </resources> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index cc4391cee9cd..afc5ee40bcf4 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Fungua mipangilio ya <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Badilisha orodha ya mipangilio."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Ukurasa wa <xliff:g id="ID_1">%1$d</xliff:g> kati ya <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Panua"</string> </resources> diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml index 2d3c27f81bb7..99b84392198f 100644 --- a/packages/SystemUI/res/values-ta-rIN/strings.xml +++ b/packages/SystemUI/res/values-ta-rIN/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> அமைப்புகளைத் திற."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"அமைப்புகளின் வரிசை முறையைத் திருத்து."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"பக்கம் <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"விரி"</string> </resources> diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml index 9fa3f8635089..c1aeb09bb582 100644 --- a/packages/SystemUI/res/values-te-rIN/strings.xml +++ b/packages/SystemUI/res/values-te-rIN/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> సెట్టింగ్లను తెరవండి."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"సెట్టింగ్ల క్రమాన్ని సవరించండి."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g>లో <xliff:g id="ID_1">%1$d</xliff:g>వ పేజీ"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"విస్తరింపజేయి"</string> </resources> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index f59e69bdce58..0c461b41c677 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"เปิดการตั้งค่า <xliff:g id="ID_1">%s</xliff:g>"</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"แก้ไขลำดับการตั้งค่า"</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"หน้า <xliff:g id="ID_1">%1$d</xliff:g> จาก <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"ขยาย"</string> </resources> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 2d978c463df3..caa9f97af345 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Buksan ang mga setting ng <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"I-edit ang pagkakasunud-sunod ng mga setting."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Page <xliff:g id="ID_1">%1$d</xliff:g> ng <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Palawakin"</string> </resources> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index e3827f7f43e0..030c97fef490 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> ayarlarını aç."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Ayarların sırasını düzenle."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Sayfa <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Genişlet"</string> </resources> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index fd627cc3f60a..c086fff2a786 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -654,6 +654,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Відкрити налаштування <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Змінити порядок налаштувань."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Сторінка <xliff:g id="ID_1">%1$d</xliff:g> з <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Розгорнути"</string> </resources> diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml index 415c5b613b9c..ff46ad3d1643 100644 --- a/packages/SystemUI/res/values-ur-rPK/strings.xml +++ b/packages/SystemUI/res/values-ur-rPK/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> ترتیبات کھولیں۔"</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"ترتیبات کی ترتیب میں ترمیم کریں۔"</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"صفحہ <xliff:g id="ID_1">%1$d</xliff:g> از <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"پھیلائیں"</string> </resources> diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml index aaea5e533b22..f2d266109479 100644 --- a/packages/SystemUI/res/values-uz-rUZ/strings.xml +++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml @@ -650,6 +650,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> sozlamalarini ochish."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Sozlamalar tartibini o‘zgartirish."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_1">%1$d</xliff:g>-sahifa, jami: <xliff:g id="ID_2">%2$d</xliff:g> ta sahifa"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Yoyish"</string> </resources> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 4ae3370aabdd..48b1e4fb9fd3 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -650,6 +650,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Mở cài đặt <xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Chỉnh sửa thứ tự cài đặt."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Trang <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Mở rộng"</string> </resources> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 7c7bc4a9aec3..94c3af1b9b65 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"打开<xliff:g id="ID_1">%s</xliff:g>设置。"</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"修改设置顺序。"</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"第 <xliff:g id="ID_1">%1$d</xliff:g> 页,共 <xliff:g id="ID_2">%2$d</xliff:g> 页"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"展开"</string> </resources> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index e41a24b7064a..5785af922224 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -650,6 +650,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"開啟<xliff:g id="ID_1">%s</xliff:g>設定頁面。"</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"編輯設定次序。"</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"第 <xliff:g id="ID_1">%1$d</xliff:g> 頁 (共 <xliff:g id="ID_2">%2$d</xliff:g> 頁)"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"展開"</string> </resources> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 51e27a396c40..6562b3902b95 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"開啟「<xliff:g id="ID_1">%s</xliff:g>」設定。"</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"編輯設定順序。"</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"第 <xliff:g id="ID_1">%1$d</xliff:g> 頁,共 <xliff:g id="ID_2">%2$d</xliff:g> 頁"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"展開"</string> </resources> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 887da2e2f15d..e3c419b481d9 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -648,6 +648,5 @@ <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Vula izilungiselelo ze-<xliff:g id="ID_1">%s</xliff:g>."</string> <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Hlela uhlelo lwezilungiselelo."</string> <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Ikhasi <xliff:g id="ID_1">%1$d</xliff:g> kwangu-<xliff:g id="ID_2">%2$d</xliff:g>"</string> - <!-- no translation found for pip_phone_expand (5889780005575693909) --> - <skip /> + <string name="pip_phone_expand" msgid="5889780005575693909">"Nweba"</string> </resources> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 9eea3750a8ec..0f5d37ea3691 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -48,6 +48,12 @@ <!-- Whether or not we show the number in the bar. --> <bool name="config_statusBarShowNumber">false</bool> + <!-- Vibrator pattern for camera gesture launch. --> + <integer-array translatable="false" name="config_cameraLaunchGestureVibePattern"> + <item>0</item> + <item>400</item> + </integer-array> + <!-- How many icons may be shown at once in the system bar. Includes any slots that may be reused for things like IME control. --> <integer name="config_maxNotificationIcons">5</integer> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 37a7e38eb4f9..331d09e4537a 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1698,20 +1698,35 @@ not appear on production builds ever. --> <string name="pip_drag_to_dismiss_summary" translatable="false">Drag to the dismiss target at the bottom of the screen to close the PIP</string> - <!-- PIP tap once to break through to the activity. Non-translatable since it should + <!-- PIP tap once to break through to the activity title. Non-translatable since it should not appear on production builds ever. --> <string name="pip_tap_through_title" translatable="false">Tap to interact</string> - <!-- PIP tap once to break through to the activity. Non-translatable since it should + <!-- PIP tap once to break through to the activity description. Non-translatable since it should not appear on production builds ever. --> <string name="pip_tap_through_summary" translatable="false">Tap once to interact with the activity</string> - <!-- PIP snap to closest edge. Non-translatable since it should + <!-- PIP snap to closest edge title. Non-translatable since it should not appear on production builds ever. --> <string name="pip_snap_mode_edge_title" translatable="false">Snap to closest edge</string> - <!-- PIP snap to closest edge. Non-translatable since it should + <!-- PIP snap to closest edge description. Non-translatable since it should not appear on production builds ever. --> <string name="pip_snap_mode_edge_summary" translatable="false">Snap to the closest edge</string> + <!-- PIP allow minimize title. Non-translatable since it should + not appear on production builds ever. --> + <string name="pip_allow_minimize_title" translatable="false">Allow PIP to minimize</string> + + <!-- PIP allow minimize description. Non-translatable since it should + not appear on production builds ever. --> + <string name="pip_allow_minimize_summary" translatable="false">Allow PIP to minimize slightly offscreen</string> + + <!-- Tuner string --> + <string name="change_theme_reboot" translatable="false">Changing the theme requires a restart.</string> + <!-- Tuner string --> + <string name="theme" translatable="false">Theme</string> + <!-- Tuner string --> + <string name="default_theme" translatable="false">Default</string> + </resources> diff --git a/packages/SystemUI/res/xml/other_settings.xml b/packages/SystemUI/res/xml/other_settings.xml index ce636cdaf4fb..18cb9306b1b6 100644 --- a/packages/SystemUI/res/xml/other_settings.xml +++ b/packages/SystemUI/res/xml/other_settings.xml @@ -23,5 +23,10 @@ android:key="power_notification_controls" android:title="@string/tuner_full_importance_settings" android:fragment="com.android.systemui.tuner.PowerNotificationControlsFragment"/> +e + <com.android.systemui.tuner.ThemePreference + android:key="theme" + android:title="@string/theme" + android:summary="%s" /> -</PreferenceScreen>
\ No newline at end of file +</PreferenceScreen> diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml index f09d6e9fe052..74d5d6ccf92d 100644 --- a/packages/SystemUI/res/xml/tuner_prefs.xml +++ b/packages/SystemUI/res/xml/tuner_prefs.xml @@ -149,6 +149,12 @@ android:summary="@string/pip_snap_mode_edge_summary" sysui:defValue="false" /> + <com.android.systemui.tuner.TunerSwitch + android:key="pip_allow_minimize" + android:title="@string/pip_allow_minimize_title" + android:summary="@string/pip_allow_minimize_summary" + sysui:defValue="false" /> + </PreferenceScreen> <PreferenceScreen diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 99e787676ef5..8292f85d9b31 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -32,6 +32,7 @@ import android.util.Log; import com.android.systemui.keyboard.KeyboardUI; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.media.RingtonePlayer; +import com.android.systemui.pip.PipUI; import com.android.systemui.plugins.OverlayPlugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.PluginManager; @@ -42,7 +43,6 @@ import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.SystemBars; import com.android.systemui.statusbar.phone.PhoneStatusBar; import com.android.systemui.tuner.TunerService; -import com.android.systemui.pip.PipUI; import com.android.systemui.usb.StorageNotification; import com.android.systemui.volume.VolumeUI; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java index b3038b96fe04..870d4d1c6a9d 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java @@ -78,10 +78,10 @@ public class DozeLog { log("pulseFinish"); } - public static void traceNotificationPulse(Context context, long instance) { + public static void traceNotificationPulse(Context context) { if (!ENABLED) return; init(context); - log("notificationPulse instance=" + instance); + log("notificationPulse"); sNotificationPulseStats.append(); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 9df8113b7ba6..9f26b0c57dbe 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -87,6 +87,7 @@ public class DozeTriggers implements DozeMachine.Part { mNotificationPulseTime = SystemClock.elapsedRealtime(); if (!mConfig.pulseOnNotificationEnabled(UserHandle.USER_CURRENT)) return; requestPulse(DozeLog.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */); + DozeLog.traceNotificationPulse(mContext); } private void onWhisper() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index 816d70d3baef..fe9f55f9a1ee 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -136,7 +136,7 @@ public class KeyguardService extends Service { @Override // Binder interface public void onScreenTurnedOn() { - Trace.beginSection("KeyguardService.mBinder#onScreenTurningOn"); + Trace.beginSection("KeyguardService.mBinder#onScreenTurnedOn"); checkPermission(); mKeyguardViewMediator.onScreenTurnedOn(); Trace.endSection(); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 9ae341a90cb1..8f1a943d98ff 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -285,6 +285,7 @@ public class KeyguardViewMediator extends SystemUI { private LockPatternUtils mLockPatternUtils; private boolean mKeyguardDonePending = false; private boolean mHideAnimationRun = false; + private boolean mHideAnimationRunning = false; private SoundPool mLockSounds; private int mLockSoundId; @@ -515,9 +516,7 @@ public class KeyguardViewMediator extends SystemUI { return; } - if (!mKeyguardDonePending) { - KeyguardViewMediator.this.handleKeyguardDone(true /* authenticated */); - } + tryKeyguardDone(true); if (strongAuth) { mUpdateMonitor.reportSuccessfulStrongAuthUnlockAttempt(); } @@ -545,7 +544,8 @@ public class KeyguardViewMediator extends SystemUI { mKeyguardDonePending = true; mHideAnimationRun = true; - mStatusBarKeyguardViewManager.startPreHideAnimation(null /* finishRunnable */); + mHideAnimationRunning = true; + mStatusBarKeyguardViewManager.startPreHideAnimation(mHideAnimationFinishedRunnable); mHandler.sendEmptyMessageDelayed(KEYGUARD_DONE_PENDING_TIMEOUT, KEYGUARD_DONE_PENDING_TIMEOUT_MS); if (strongAuth) { @@ -565,9 +565,11 @@ public class KeyguardViewMediator extends SystemUI { public void readyForKeyguardDone() { Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#readyForKeyguardDone"); if (mKeyguardDonePending) { + mKeyguardDonePending = false; + // Somebody has called keyguardDonePending before, which means that we are // authenticated - KeyguardViewMediator.this.handleKeyguardDone(true /* authenticated */); + tryKeyguardDone(true); } Trace.endSection(); } @@ -643,9 +645,7 @@ public class KeyguardViewMediator extends SystemUI { // Assume keyguard is showing (unless it's disabled) until we know for sure... setShowingLocked(!shouldWaitForProvisioning() && !mLockPatternUtils.isLockScreenDisabled( - KeyguardUpdateMonitor.getCurrentUser())); - updateInputRestrictedLocked(); - mTrustManager.reportKeyguardShowingChanged(); + KeyguardUpdateMonitor.getCurrentUser()), true /* forceCallbacks */); mStatusBarKeyguardViewManager = SystemUIFactory.getInstance().createStatusBarKeyguardViewManager(mContext, @@ -1257,7 +1257,7 @@ public class KeyguardViewMediator extends SystemUI { */ public void handleDismiss(boolean allowWhileOccluded) { if (mShowing && (allowWhileOccluded || !mOccluded)) { - mStatusBarKeyguardViewManager.dismiss(); + mStatusBarKeyguardViewManager.dismissAndCollapse(); } } @@ -1493,6 +1493,16 @@ public class KeyguardViewMediator extends SystemUI { } }; + private void tryKeyguardDone(boolean authenticated) { + if (!mKeyguardDonePending && mHideAnimationRun && !mHideAnimationRunning) { + handleKeyguardDone(authenticated); + } else if (!mHideAnimationRun) { + mHideAnimationRun = true; + mHideAnimationRunning = true; + mStatusBarKeyguardViewManager.startPreHideAnimation(mHideAnimationFinishedRunnable); + } + } + /** * @see #keyguardDone * @see #KEYGUARD_DONE @@ -1679,6 +1689,11 @@ public class KeyguardViewMediator extends SystemUI { } }; + private final Runnable mHideAnimationFinishedRunnable = () -> { + mHideAnimationRunning = false; + tryKeyguardDone(true); + }; + /** * Handle message sent by {@link #hideLocked()} * @see #HIDE @@ -1697,16 +1712,10 @@ public class KeyguardViewMediator extends SystemUI { return; } mHiding = true; + if (mShowing && !mOccluded) { - if (!mHideAnimationRun) { - mStatusBarKeyguardViewManager.startPreHideAnimation(mKeyguardGoingAwayRunnable); - } else { - mKeyguardGoingAwayRunnable.run(); - } + mKeyguardGoingAwayRunnable.run(); } else { - - // Don't try to rely on WindowManager - if Keyguard wasn't showing, window - // manager won't start the exit animation. handleStartKeyguardExitAnimation( SystemClock.uptimeMillis() + mHideAnimation.getStartOffset(), mHideAnimation.getDuration()); @@ -1806,7 +1815,7 @@ public class KeyguardViewMediator extends SystemUI { synchronized (KeyguardViewMediator.this) { if (DEBUG) Log.d(TAG, "handleVerifyUnlock"); setShowingLocked(true); - mStatusBarKeyguardViewManager.verifyUnlock(); + mStatusBarKeyguardViewManager.dismissAndCollapse(); } Trace.endSection(); } @@ -1969,7 +1978,11 @@ public class KeyguardViewMediator extends SystemUI { } private void setShowingLocked(boolean showing) { - if (showing != mShowing) { + setShowingLocked(showing, false /* forceCallbacks */); + } + + private void setShowingLocked(boolean showing, boolean forceCallbacks) { + if (showing != mShowing || forceCallbacks) { mShowing = showing; int size = mKeyguardStateCallbacks.size(); for (int i = size - 1; i >= 0; i--) { diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchGesture.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchGesture.java new file mode 100644 index 000000000000..e8e8a4d3215a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchGesture.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.pip.phone; + +/** + * A generic interface for a touch gesture. + */ +public abstract class PipTouchGesture { + + /** + * Handle the touch down. + */ + void onDown(PipTouchState touchState) {} + + /** + * Handle the touch move, and return whether the event was consumed. + */ + boolean onMove(PipTouchState touchState) { + return false; + } + + /** + * Handle the touch up, and return whether the gesture was consumed. + */ + boolean onUp(PipTouchState touchState) { + return false; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index a3593806fb1f..b24d19958f18 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -22,6 +22,7 @@ import static android.view.WindowManager.INPUT_CONSUMER_PIP; import static com.android.systemui.Interpolators.FAST_OUT_LINEAR_IN; import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN; +import static com.android.systemui.Interpolators.LINEAR_OUT_SLOW_IN; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -30,9 +31,9 @@ import android.animation.ValueAnimator.AnimatorUpdateListener; import android.app.ActivityManager.StackInfo; import android.app.IActivityManager; import android.content.Context; +import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; -import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.util.Log; @@ -43,7 +44,6 @@ import android.view.InputChannel; import android.view.InputEvent; import android.view.InputEventReceiver; import android.view.MotionEvent; -import android.view.VelocityTracker; import android.view.ViewConfiguration; import com.android.internal.os.BackgroundThread; @@ -64,10 +64,19 @@ public class PipTouchHandler implements TunerService.Tunable { private static final String TUNER_KEY_DRAG_TO_DISMISS = "pip_drag_to_dismiss"; private static final String TUNER_KEY_TAP_THROUGH = "pip_tap_through"; private static final String TUNER_KEY_SNAP_MODE_EDGE = "pip_snap_mode_edge"; + private static final String TUNER_KEY_ALLOW_MINIMIZE = "pip_allow_minimize"; private static final int SNAP_STACK_DURATION = 225; private static final int DISMISS_STACK_DURATION = 375; private static final int EXPAND_STACK_DURATION = 225; + private static final int MINIMIZE_STACK_MAX_DURATION = 200; + + // The fraction of the stack width to show when minimized + private static final float MINIMIZED_VISIBLE_FRACTION = 0.25f; + // The fraction of the stack width that the user has to drag offscreen to minimize the PIP + private static final float MINIMIZE_OFFSCREEN_FRACTION = 0.15f; + // The fraction of the stack width that the user has to move when flinging to dismiss the PIP + private static final float DISMISS_FLING_DISTANCE_FRACTION = 0.3f; private final Context mContext; private final IActivityManager mActivityManager; @@ -83,10 +92,16 @@ public class PipTouchHandler implements TunerService.Tunable { private final PipSnapAlgorithm mSnapAlgorithm; private PipMotionHelper mMotionHelper; + // Allow swiping offscreen to dismiss the PIP private boolean mEnableSwipeToDismiss = true; + // Allow dragging the PIP to a location to close it private boolean mEnableDragToDismiss = true; + // Allow tapping on the PIP to show additional controls private boolean mEnableTapThrough = false; + // Allow snapping the PIP to the closest edge and not the corners of the screen private boolean mEnableSnapToEdge = false; + // Allow the PIP to be "docked" slightly offscreen + private boolean mEnableMinimizing = false; private final Rect mPinnedStackBounds = new Rect(); private final Rect mBoundedPinnedStackBounds = new Rect(); @@ -99,16 +114,16 @@ public class PipTouchHandler implements TunerService.Tunable { } }; - private final PointF mDownTouch = new PointF(); - private final PointF mLastTouch = new PointF(); - private boolean mIsDragging; - private boolean mIsSwipingToDismiss; + // Behaviour states private boolean mIsTappingThrough; - private int mActivePointerId; + private boolean mIsMinimized; + // Touch state + private final PipTouchState mTouchState; private final FlingAnimationUtils mFlingAnimationUtils; - private VelocityTracker mVelocityTracker; + private final PipTouchGesture[] mGestures; + // Temporary vars private final Rect mTmpBounds = new Rect(); /** @@ -183,13 +198,19 @@ public class PipTouchHandler implements TunerService.Tunable { mMenuController.addListener(mMenuListener); mDismissViewController = new PipDismissViewController(context); mSnapAlgorithm = new PipSnapAlgorithm(mContext); + mTouchState = new PipTouchState(mViewConfig); mFlingAnimationUtils = new FlingAnimationUtils(context, 2f); + mGestures = new PipTouchGesture[]{ + mDragToDismissGesture, mSwipeToDismissGesture, mTapThroughGesture, mMinimizeGesture, + mDefaultMovementGesture + }; mMotionHelper = new PipMotionHelper(BackgroundThread.getHandler()); registerInputConsumer(); // Register any tuner settings changes TunerService.get(context).addTunable(this, TUNER_KEY_SWIPE_TO_DISMISS, - TUNER_KEY_DRAG_TO_DISMISS, TUNER_KEY_TAP_THROUGH, TUNER_KEY_SNAP_MODE_EDGE); + TUNER_KEY_DRAG_TO_DISMISS, TUNER_KEY_TAP_THROUGH, TUNER_KEY_SNAP_MODE_EDGE, + TUNER_KEY_ALLOW_MINIMIZE); } @Override @@ -198,6 +219,8 @@ public class PipTouchHandler implements TunerService.Tunable { // Reset back to default mEnableSwipeToDismiss = true; mEnableDragToDismiss = true; + mEnableMinimizing = false; + setMinimizedState(false); mEnableTapThrough = false; mIsTappingThrough = false; mEnableSnapToEdge = false; @@ -211,6 +234,9 @@ public class PipTouchHandler implements TunerService.Tunable { case TUNER_KEY_DRAG_TO_DISMISS: mEnableDragToDismiss = Integer.parseInt(newValue) != 0; break; + case TUNER_KEY_ALLOW_MINIMIZE: + mEnableMinimizing = Integer.parseInt(newValue) != 0; + break; case TUNER_KEY_TAP_THROUGH: mEnableTapThrough = Integer.parseInt(newValue) != 0; mIsTappingThrough = false; @@ -233,6 +259,9 @@ public class PipTouchHandler implements TunerService.Tunable { return true; } + // Update the touch state + mTouchState.onTouchEvent(ev); + switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: { // Cancel any existing animations on the pinned stack @@ -241,173 +270,58 @@ public class PipTouchHandler implements TunerService.Tunable { } updateBoundedPinnedStackBounds(true /* updatePinnedStackBounds */); - initOrResetVelocityTracker(); - mVelocityTracker.addMovement(ev); - mActivePointerId = ev.getPointerId(0); - mLastTouch.set(ev.getX(), ev.getY()); - mDownTouch.set(mLastTouch); - mIsDragging = false; + for (PipTouchGesture gesture : mGestures) { + gesture.onDown(mTouchState); + } try { mPinnedStackController.setInInteractiveMode(true); } catch (RemoteException e) { Log.e(TAG, "Could not set dragging state", e); } - if (mEnableDragToDismiss) { - // TODO: Consider setting a timer such at after X time, we show the dismiss - // target if the user hasn't already dragged some distance - mDismissViewController.createDismissTarget(); - } break; } case MotionEvent.ACTION_MOVE: { - // Update the velocity tracker - mVelocityTracker.addMovement(ev); - - int activePointerIndex = ev.findPointerIndex(mActivePointerId); - float x = ev.getX(activePointerIndex); - float y = ev.getY(activePointerIndex); - float left = mPinnedStackBounds.left + (x - mLastTouch.x); - float top = mPinnedStackBounds.top + (y - mLastTouch.y); - - if (!mIsDragging) { - // Check if the pointer has moved far enough - float movement = PointF.length(mDownTouch.x - x, mDownTouch.y - y); - if (movement > mViewConfig.getScaledTouchSlop()) { - mIsDragging = true; - mIsTappingThrough = false; - mMenuController.hideMenu(); - if (mEnableSwipeToDismiss) { - // TODO: this check can have some buffer so that we only start swiping - // after a significant move out of bounds - mIsSwipingToDismiss = !(mBoundedPinnedStackBounds.left <= left && - left <= mBoundedPinnedStackBounds.right) && - Math.abs(mDownTouch.x - x) > Math.abs(y - mLastTouch.y); - } - if (mEnableDragToDismiss) { - mDismissViewController.showDismissTarget(); - } + for (PipTouchGesture gesture : mGestures) { + if (gesture.onMove(mTouchState)) { + break; } } - - if (mIsSwipingToDismiss) { - // Ignore the vertical movement - mTmpBounds.set(mPinnedStackBounds); - mTmpBounds.offsetTo((int) left, mPinnedStackBounds.top); - if (!mTmpBounds.equals(mPinnedStackBounds)) { - mPinnedStackBounds.set(mTmpBounds); - mMotionHelper.resizeToBounds(mPinnedStackBounds); - } - } else if (mIsDragging) { - // Move the pinned stack - if (!DEBUG_ALLOW_OUT_OF_BOUNDS_STACK) { - left = Math.max(mBoundedPinnedStackBounds.left, Math.min( - mBoundedPinnedStackBounds.right, left)); - top = Math.max(mBoundedPinnedStackBounds.top, Math.min( - mBoundedPinnedStackBounds.bottom, top)); - } - mTmpBounds.set(mPinnedStackBounds); - mTmpBounds.offsetTo((int) left, (int) top); - if (!mTmpBounds.equals(mPinnedStackBounds)) { - mPinnedStackBounds.set(mTmpBounds); - mMotionHelper.resizeToBounds(mPinnedStackBounds); - } - } - mLastTouch.set(ev.getX(), ev.getY()); - break; - } - case MotionEvent.ACTION_POINTER_UP: { - // Update the velocity tracker - mVelocityTracker.addMovement(ev); - - int pointerIndex = ev.getActionIndex(); - int pointerId = ev.getPointerId(pointerIndex); - if (pointerId == mActivePointerId) { - // Select a new active pointer id and reset the movement state - final int newPointerIndex = (pointerIndex == 0) ? 1 : 0; - mActivePointerId = ev.getPointerId(newPointerIndex); - mLastTouch.set(ev.getX(newPointerIndex), ev.getY(newPointerIndex)); - } break; } case MotionEvent.ACTION_UP: { - // Update the velocity tracker - mVelocityTracker.addMovement(ev); - mVelocityTracker.computeCurrentVelocity(1000, - ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity()); - float velocityX = mVelocityTracker.getXVelocity(); - float velocityY = mVelocityTracker.getYVelocity(); - float velocity = PointF.length(velocityX, velocityY); - // Update the movement bounds again if the state has changed since the user started // dragging (ie. when the IME shows) updateBoundedPinnedStackBounds(false /* updatePinnedStackBounds */); - if (mIsSwipingToDismiss) { - if (Math.abs(velocityX) > mFlingAnimationUtils.getMinVelocityPxPerSecond()) { - flingToDismiss(velocityX); - } else { - animateToClosestSnapTarget(); - } - } else if (mIsDragging) { - if (velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond()) { - flingToSnapTarget(velocity, velocityX, velocityY); - } else { - int activePointerIndex = ev.findPointerIndex(mActivePointerId); - int x = (int) ev.getX(activePointerIndex); - int y = (int) ev.getY(activePointerIndex); - Rect dismissBounds = mEnableDragToDismiss - ? mDismissViewController.getDismissBounds() - : null; - if (dismissBounds != null && dismissBounds.contains(x, y)) { - animateDismissPinnedStack(dismissBounds); - } else { - animateToClosestSnapTarget(); - } + for (PipTouchGesture gesture : mGestures) { + if (gesture.onUp(mTouchState)) { + break; } - } else { - if (mEnableTapThrough) { - if (!mIsTappingThrough) { - mMenuController.showMenu(); - mIsTappingThrough = true; - } - } else { - expandPinnedStackToFullscreen(); - } - } - if (mEnableDragToDismiss) { - mDismissViewController.destroyDismissTarget(); } // Fall through to clean up } case MotionEvent.ACTION_CANCEL: { - mIsDragging = false; - mIsSwipingToDismiss = false; try { mPinnedStackController.setInInteractiveMode(false); } catch (RemoteException e) { Log.e(TAG, "Could not set dragging state", e); } - recycleVelocityTracker(); break; } } return !mIsTappingThrough; } - private void initOrResetVelocityTracker() { - if (mVelocityTracker == null) { - mVelocityTracker = VelocityTracker.obtain(); - } else { - mVelocityTracker.clear(); - } - } - - private void recycleVelocityTracker() { - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } + /** + * @return whether the current touch state is a horizontal drag offscreen. + */ + private boolean isDraggingOffscreen(PipTouchState touchState) { + PointF lastDelta = touchState.getLastTouchDelta(); + PointF downDelta = touchState.getDownTouchDelta(); + float left = mPinnedStackBounds.left + lastDelta.x; + return !(mBoundedPinnedStackBounds.left <= left && left <= mBoundedPinnedStackBounds.right) + && Math.abs(downDelta.x) > Math.abs(downDelta.y); } /** @@ -449,6 +363,74 @@ public class PipTouchHandler implements TunerService.Tunable { } /** + * Sets the minimized state and notifies the controller. + */ + private void setMinimizedState(boolean isMinimized) { + mIsMinimized = isMinimized; + try { + mPinnedStackController.setIsMinimized(isMinimized); + } catch (RemoteException e) { + Log.e(TAG, "Could not set minimized state", e); + } + } + + /** + * @return whether the given {@param pinnedStackBounds} indicates the PIP should be minimized. + */ + private boolean shouldMinimizedPinnedStack() { + Point displaySize = new Point(); + mContext.getDisplay().getRealSize(displaySize); + if (mPinnedStackBounds.left < 0) { + float offscreenFraction = (float) -mPinnedStackBounds.left / mPinnedStackBounds.width(); + return offscreenFraction >= MINIMIZE_OFFSCREEN_FRACTION; + } else if (mPinnedStackBounds.right > displaySize.x) { + float offscreenFraction = (float) (mPinnedStackBounds.right - displaySize.x) / + mPinnedStackBounds.width(); + return offscreenFraction >= MINIMIZE_OFFSCREEN_FRACTION; + } else { + return false; + } + } + + /** + * Flings the minimized PIP to the closest minimized snap target. + */ + private void flingToMinimizedSnapTarget(float velocityY) { + Rect movementBounds = new Rect(mPinnedStackBounds.left, mBoundedPinnedStackBounds.top, + mPinnedStackBounds.left, mBoundedPinnedStackBounds.bottom); + Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mPinnedStackBounds, + 0 /* velocityX */, velocityY); + if (!mPinnedStackBounds.equals(toBounds)) { + mPinnedStackBoundsAnimator = mMotionHelper.createAnimationToBounds(mPinnedStackBounds, + toBounds, 0, FAST_OUT_SLOW_IN, mUpdatePinnedStackBoundsListener); + mFlingAnimationUtils.apply(mPinnedStackBoundsAnimator, 0, + distanceBetweenRectOffsets(mPinnedStackBounds, toBounds), + velocityY); + mPinnedStackBoundsAnimator.start(); + } + } + + /** + * Animates the PIP to the minimized state, slightly offscreen. + */ + private void animateToClosestMinimizedTarget() { + Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(mBoundedPinnedStackBounds, + mPinnedStackBounds); + Point displaySize = new Point(); + mContext.getDisplay().getRealSize(displaySize); + int visibleWidth = (int) (MINIMIZED_VISIBLE_FRACTION * mPinnedStackBounds.width()); + if (mPinnedStackBounds.left < 0) { + toBounds.offsetTo(-toBounds.width() + visibleWidth, toBounds.top); + } else if (mPinnedStackBounds.right > displaySize.x) { + toBounds.offsetTo(displaySize.x - visibleWidth, toBounds.top); + } + mPinnedStackBoundsAnimator = mMotionHelper.createAnimationToBounds(mPinnedStackBounds, + toBounds, MINIMIZE_STACK_MAX_DURATION, LINEAR_OUT_SLOW_IN, + mUpdatePinnedStackBoundsListener); + mPinnedStackBoundsAnimator.start(); + } + + /** * Flings the PIP to the closest snap target. */ private void flingToSnapTarget(float velocity, float velocityX, float velocityY) { @@ -478,12 +460,26 @@ public class PipTouchHandler implements TunerService.Tunable { } /** + * @return whether the velocity is coincident with the current pinned stack bounds to be + * considered a fling to dismiss. + */ + private boolean isFlingToDismiss(float velocityX) { + Point displaySize = new Point(); + mContext.getDisplay().getRealSize(displaySize); + return (mPinnedStackBounds.right > displaySize.x && velocityX > 0) || + (mPinnedStackBounds.left < 0 && velocityX < 0); + } + + /** * Flings the PIP to dismiss it offscreen. */ private void flingToDismiss(float velocityX) { + Point displaySize = new Point(); + mContext.getDisplay().getRealSize(displaySize); float offsetX = velocityX > 0 - ? mBoundedPinnedStackBounds.right + 2 * mPinnedStackBounds.width() - : mBoundedPinnedStackBounds.left - 2 * mPinnedStackBounds.width(); + ? displaySize.x + mPinnedStackBounds.width() + : -mPinnedStackBounds.width(); + Rect toBounds = new Rect(mPinnedStackBounds); toBounds.offsetTo((int) offsetX, toBounds.top); if (!mPinnedStackBounds.equals(toBounds)) { @@ -495,13 +491,7 @@ public class PipTouchHandler implements TunerService.Tunable { mPinnedStackBoundsAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - BackgroundThread.getHandler().post(() -> { - try { - mActivityManager.removeStack(PINNED_STACK_ID); - } catch (RemoteException e) { - Log.e(TAG, "Failed to remove PIP", e); - } - }); + BackgroundThread.getHandler().post(PipTouchHandler.this::dismissPinnedStack); } }); mPinnedStackBoundsAnimator.start(); @@ -521,13 +511,7 @@ public class PipTouchHandler implements TunerService.Tunable { mPinnedStackBoundsAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - BackgroundThread.getHandler().post(() -> { - try { - mActivityManager.removeStack(PINNED_STACK_ID); - } catch (RemoteException e) { - Log.e(TAG, "Failed to remove PIP", e); - } - }); + BackgroundThread.getHandler().post(PipTouchHandler.this::dismissPinnedStack); } }); mPinnedStackBoundsAnimator.start(); @@ -549,6 +533,27 @@ public class PipTouchHandler implements TunerService.Tunable { } /** + * Tries to the move the pinned stack to the given {@param bounds}. + */ + private void movePinnedStack(Rect bounds) { + if (!bounds.equals(mPinnedStackBounds)) { + mPinnedStackBounds.set(bounds); + mMotionHelper.resizeToBounds(mPinnedStackBounds); + } + } + + /** + * Dismisses the pinned stack. + */ + private void dismissPinnedStack() { + try { + mActivityManager.removeStack(PINNED_STACK_ID); + } catch (RemoteException e) { + Log.e(TAG, "Failed to remove PIP", e); + } + } + + /** * Updates the movement bounds of the pinned stack. */ private void updateBoundedPinnedStackBounds(boolean updatePinnedStackBounds) { @@ -572,4 +577,231 @@ public class PipTouchHandler implements TunerService.Tunable { private float distanceBetweenRectOffsets(Rect r1, Rect r2) { return PointF.length(r1.left - r2.left, r1.top - r2.top); } + + /** + * Gesture controlling dragging over a target to dismiss the PIP. + */ + private PipTouchGesture mDragToDismissGesture = new PipTouchGesture() { + @Override + public void onDown(PipTouchState touchState) { + if (mEnableDragToDismiss) { + // TODO: Consider setting a timer such at after X time, we show the dismiss + // target if the user hasn't already dragged some distance + mDismissViewController.createDismissTarget(); + } + } + + @Override + boolean onMove(PipTouchState touchState) { + if (mEnableDragToDismiss && touchState.startedDragging()) { + mDismissViewController.showDismissTarget(); + } + return false; + } + + @Override + public boolean onUp(PipTouchState touchState) { + if (mEnableDragToDismiss) { + try { + if (touchState.isDragging()) { + Rect dismissBounds = mDismissViewController.getDismissBounds(); + PointF lastTouch = touchState.getLastTouchPosition(); + if (dismissBounds.contains((int) lastTouch.x, (int) lastTouch.y)) { + animateDismissPinnedStack(dismissBounds); + return true; + } + } + } finally { + mDismissViewController.destroyDismissTarget(); + } + } + return false; + } + }; + + /**** Gestures ****/ + + /** + * Gesture controlling swiping offscreen to dismiss the PIP. + */ + private PipTouchGesture mSwipeToDismissGesture = new PipTouchGesture() { + @Override + boolean onMove(PipTouchState touchState) { + if (mEnableSwipeToDismiss) { + boolean isDraggingOffscreen = isDraggingOffscreen(touchState); + + if (touchState.startedDragging() && isDraggingOffscreen) { + // Reset the minimized state once we drag horizontally + setMinimizedState(false); + } + + if (isDraggingOffscreen) { + // Move the pinned stack, but ignore the vertical movement + float left = mPinnedStackBounds.left + touchState.getLastTouchDelta().x; + mTmpBounds.set(mPinnedStackBounds); + mTmpBounds.offsetTo((int) left, mPinnedStackBounds.top); + if (!mTmpBounds.equals(mPinnedStackBounds)) { + mPinnedStackBounds.set(mTmpBounds); + mMotionHelper.resizeToBounds(mPinnedStackBounds); + } + return true; + } + } + return false; + } + + @Override + public boolean onUp(PipTouchState touchState) { + if (mEnableSwipeToDismiss && touchState.isDragging()) { + PointF vel = touchState.getVelocity(); + PointF downDelta = touchState.getDownTouchDelta(); + float minFlingVel = mFlingAnimationUtils.getMinVelocityPxPerSecond(); + float flingVelScale = mEnableMinimizing ? 3f : 2f; + if (Math.abs(vel.x) > (flingVelScale * minFlingVel)) { + // Determine if this gesture is actually a fling to dismiss + if (isFlingToDismiss(vel.x) && Math.abs(downDelta.x) >= + (DISMISS_FLING_DISTANCE_FRACTION * mPinnedStackBounds.width())) { + flingToDismiss(vel.x); + } else { + flingToSnapTarget(vel.length(), vel.x, vel.y); + } + return true; + } + } + return false; + } + }; + + /** + * Gesture controlling dragging the PIP slightly offscreen to minimize it. + */ + private PipTouchGesture mMinimizeGesture = new PipTouchGesture() { + @Override + boolean onMove(PipTouchState touchState) { + if (mEnableMinimizing) { + boolean isDraggingOffscreen = isDraggingOffscreen(touchState); + if (touchState.startedDragging() && isDraggingOffscreen) { + // Reset the minimized state once we drag horizontally + setMinimizedState(false); + } + + if (isDraggingOffscreen) { + // Move the pinned stack, but ignore the vertical movement + float left = mPinnedStackBounds.left + touchState.getLastTouchDelta().x; + mTmpBounds.set(mPinnedStackBounds); + mTmpBounds.offsetTo((int) left, mPinnedStackBounds.top); + if (!mTmpBounds.equals(mPinnedStackBounds)) { + mPinnedStackBounds.set(mTmpBounds); + mMotionHelper.resizeToBounds(mPinnedStackBounds); + } + return true; + } else if (mIsMinimized && touchState.isDragging()) { + // Move the pinned stack, but ignore the horizontal movement + PointF lastDelta = touchState.getLastTouchDelta(); + float top = mPinnedStackBounds.top + lastDelta.y; + top = Math.max(mBoundedPinnedStackBounds.top, Math.min( + mBoundedPinnedStackBounds.bottom, top)); + mTmpBounds.set(mPinnedStackBounds); + mTmpBounds.offsetTo(mPinnedStackBounds.left, (int) top); + movePinnedStack(mTmpBounds); + return true; + } + } + return false; + } + + @Override + public boolean onUp(PipTouchState touchState) { + if (mEnableMinimizing) { + if (touchState.isDragging()) { + if (isDraggingOffscreen(touchState)) { + if (shouldMinimizedPinnedStack()) { + setMinimizedState(true); + animateToClosestMinimizedTarget(); + return true; + } + } else if (mIsMinimized) { + PointF vel = touchState.getVelocity(); + if (vel.length() > mFlingAnimationUtils.getMinVelocityPxPerSecond()) { + flingToMinimizedSnapTarget(vel.y); + } else { + animateToClosestMinimizedTarget(); + } + return true; + } + } else if (mIsMinimized) { + setMinimizedState(false); + animateToClosestSnapTarget(); + return true; + } + } + return false; + } + }; + + /** + * Gesture controlling tapping on the PIP to show an overlay. + */ + private PipTouchGesture mTapThroughGesture = new PipTouchGesture() { + @Override + boolean onMove(PipTouchState touchState) { + if (mEnableTapThrough && touchState.startedDragging()) { + mIsTappingThrough = false; + mMenuController.hideMenu(); + } + return false; + } + + @Override + public boolean onUp(PipTouchState touchState) { + if (mEnableTapThrough && !touchState.isDragging() && !mIsTappingThrough) { + mMenuController.showMenu(); + mIsTappingThrough = true; + return true; + } + return false; + } + }; + + /** + * Gesture controlling normal movement of the PIP. + */ + private PipTouchGesture mDefaultMovementGesture = new PipTouchGesture() { + @Override + boolean onMove(PipTouchState touchState) { + if (touchState.isDragging()) { + // Move the pinned stack freely + PointF lastDelta = touchState.getLastTouchDelta(); + float left = mPinnedStackBounds.left + lastDelta.x; + float top = mPinnedStackBounds.top + lastDelta.y; + if (!DEBUG_ALLOW_OUT_OF_BOUNDS_STACK) { + left = Math.max(mBoundedPinnedStackBounds.left, Math.min( + mBoundedPinnedStackBounds.right, left)); + top = Math.max(mBoundedPinnedStackBounds.top, Math.min( + mBoundedPinnedStackBounds.bottom, top)); + } + mTmpBounds.set(mPinnedStackBounds); + mTmpBounds.offsetTo((int) left, (int) top); + movePinnedStack(mTmpBounds); + return true; + } + return false; + } + + @Override + public boolean onUp(PipTouchState touchState) { + if (touchState.isDragging()) { + PointF vel = mTouchState.getVelocity(); + float velocity = PointF.length(vel.x, vel.y); + if (velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond()) { + flingToSnapTarget(velocity, vel.x, vel.y); + } else { + animateToClosestSnapTarget(); + } + } else { + expandPinnedStackToFullscreen(); + } + return true; + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java new file mode 100644 index 000000000000..80af5a6c0268 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.pip.phone; + +import android.app.IActivityManager; +import android.graphics.PointF; +import android.view.IPinnedStackController; +import android.view.IPinnedStackListener; +import android.view.IWindowManager; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.ViewConfiguration; + +/** + * This keeps track of the touch state throughout the current touch gesture. + */ +public class PipTouchState { + + private ViewConfiguration mViewConfig; + + private VelocityTracker mVelocityTracker; + private final PointF mDownTouch = new PointF(); + private final PointF mDownDelta = new PointF(); + private final PointF mLastTouch = new PointF(); + private final PointF mLastDelta = new PointF(); + private final PointF mVelocity = new PointF(); + private boolean mIsDragging = false; + private boolean mStartedDragging = false; + private int mActivePointerId; + + public PipTouchState(ViewConfiguration viewConfig) { + mViewConfig = viewConfig; + } + + /** + * Processess a given touch event and updates the state. + */ + public void onTouchEvent(MotionEvent ev) { + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: { + // Initialize the velocity tracker + initOrResetVelocityTracker(); + mActivePointerId = ev.getPointerId(0); + mLastTouch.set(ev.getX(), ev.getY()); + mDownTouch.set(mLastTouch); + mIsDragging = false; + mStartedDragging = false; + break; + } + case MotionEvent.ACTION_MOVE: { + // Update the velocity tracker + mVelocityTracker.addMovement(ev); + int pointerIndex = ev.findPointerIndex(mActivePointerId); + float x = ev.getX(pointerIndex); + float y = ev.getY(pointerIndex); + mLastDelta.set(x - mLastTouch.x, y - mLastTouch.y); + mDownDelta.set(x - mDownTouch.x, y - mDownTouch.y); + + boolean hasMovedBeyondTap = mDownDelta.length() > mViewConfig.getScaledTouchSlop(); + if (!mIsDragging) { + if (hasMovedBeyondTap) { + mIsDragging = true; + mStartedDragging = true; + } + } else { + mStartedDragging = false; + } + mLastTouch.set(x, y); + break; + } + case MotionEvent.ACTION_POINTER_UP: { + // Update the velocity tracker + mVelocityTracker.addMovement(ev); + + int pointerIndex = ev.getActionIndex(); + int pointerId = ev.getPointerId(pointerIndex); + if (pointerId == mActivePointerId) { + // Select a new active pointer id and reset the movement state + final int newPointerIndex = (pointerIndex == 0) ? 1 : 0; + mActivePointerId = ev.getPointerId(newPointerIndex); + mLastTouch.set(ev.getX(newPointerIndex), ev.getY(newPointerIndex)); + } + break; + } + case MotionEvent.ACTION_UP: { + // Update the velocity tracker + mVelocityTracker.addMovement(ev); + mVelocityTracker.computeCurrentVelocity(1000, + mViewConfig.getScaledMaximumFlingVelocity()); + mVelocity.set(mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity()); + + int pointerIndex = ev.findPointerIndex(mActivePointerId); + mLastTouch.set(ev.getX(pointerIndex), ev.getY(pointerIndex)); + + // Fall through to clean up + } + case MotionEvent.ACTION_CANCEL: { + recycleVelocityTracker(); + break; + } + } + } + + /** + * @return the velocity of the active touch pointer at the point it is lifted off the screen. + */ + public PointF getVelocity() { + return mVelocity; + } + + /** + * @return the last touch position of the active pointer. + */ + public PointF getLastTouchPosition() { + return mLastTouch; + } + + /** + * @return the movement delta between the last handled touch event and the previous touch + * position. + */ + public PointF getLastTouchDelta() { + return mLastDelta; + } + + /** + * @return the movement delta between the last handled touch event and the down touch + * position. + */ + public PointF getDownTouchDelta() { + return mDownDelta; + } + + /** + * @return whether the user has started dragging. + */ + public boolean isDragging() { + return mIsDragging; + } + + /** + * @return whether the user has started dragging just in the last handled touch event. + */ + public boolean startedDragging() { + return mStartedDragging; + } + + private void initOrResetVelocityTracker() { + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } else { + mVelocityTracker.clear(); + } + } + + private void recycleVelocityTracker() { + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/grid/RecentsGridActivity.java b/packages/SystemUI/src/com/android/systemui/recents/grid/RecentsGridActivity.java index b88ee7672cfa..9ed492421f18 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/grid/RecentsGridActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/grid/RecentsGridActivity.java @@ -15,12 +15,13 @@ */ package com.android.systemui.recents.grid; +import static android.app.ActivityManager.StackId.INVALID_STACK_ID; + import android.app.Activity; import android.content.Intent; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Bundle; -import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; @@ -70,11 +71,12 @@ public class RecentsGridActivity extends Activity implements ViewTreeObserver.On private TaskStack mTaskStack; private List<Task> mTasks = new ArrayList<>(); - private List<View> mTaskViews = new ArrayList<>(); + private List<TaskView> mTaskViews = new ArrayList<>(); private FrameLayout mRecentsView; private TextView mEmptyView; private View mClearAllButton; - private int mDisplayOrientation = Configuration.ORIENTATION_UNDEFINED; + private int mLastDisplayOrientation = Configuration.ORIENTATION_UNDEFINED; + private int mLastDisplayDensity; private Rect mDisplayRect = new Rect(); private LayoutInflater mInflater; private boolean mTouchExplorationEnabled; @@ -86,8 +88,10 @@ public class RecentsGridActivity extends Activity implements ViewTreeObserver.On SystemServicesProxy ssp = Recents.getSystemServices(); mInflater = LayoutInflater.from(this); - mDisplayOrientation = Utilities.getAppConfiguration(this).orientation; + Configuration appConfiguration = Utilities.getAppConfiguration(this); mDisplayRect = ssp.getDisplayRect(); + mLastDisplayOrientation = appConfiguration.orientation; + mLastDisplayDensity = appConfiguration.densityDpi; mTouchExplorationEnabled = ssp.isTouchExplorationEnabled(); mRecentsView = (FrameLayout) findViewById(R.id.recents_view); @@ -122,16 +126,29 @@ public class RecentsGridActivity extends Activity implements ViewTreeObserver.On return (TaskView) mInflater.inflate(R.layout.recents_task_view, mRecentsView, false); } - private void clearTaskViews() { + private void removeTaskViews() { for (View taskView : mTaskViews) { ViewGroup parent = (ViewGroup) taskView.getParent(); if (parent != null) { parent.removeView(taskView); } } + } + + private void clearTaskViews() { + removeTaskViews(); mTaskViews.clear(); } + private TaskView getChildViewForTask(Task task) { + for (TaskView tv : mTaskViews) { + if (tv.getTask() == task) { + return tv; + } + } + return null; + } + private void updateControlVisibility() { boolean empty = (mTasks.size() == 0); mClearAllButton.setVisibility(empty ? View.INVISIBLE : View.VISIBLE); @@ -170,7 +187,8 @@ public class RecentsGridActivity extends Activity implements ViewTreeObserver.On for (int i = 0; i < mTasks.size(); i++) { Task task = mTasks.get(i); TaskView taskView = createView(); - taskView.onTaskBound(task, mTouchExplorationEnabled, mDisplayOrientation, mDisplayRect); + taskView.onTaskBound(task, mTouchExplorationEnabled, mLastDisplayOrientation, + mDisplayRect); Recents.getTaskLoader().loadTaskData(task); taskView.setTouchEnabled(true); // Show dismiss button right away. @@ -211,6 +229,22 @@ public class RecentsGridActivity extends Activity implements ViewTreeObserver.On } @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + // Notify of the config change. + Configuration newDeviceConfiguration = Utilities.getAppConfiguration(this); + mDisplayRect = Recents.getSystemServices().getDisplayRect(); + mRecentsView.getViewTreeObserver().addOnPreDrawListener(this); + mRecentsView.requestLayout(); + int numStackTasks = mTaskStack.getStackTaskCount(); + EventBus.getDefault().send(new ConfigurationChangedEvent(false /* fromMultiWindow */, + mLastDisplayOrientation != newDeviceConfiguration.orientation, + mLastDisplayDensity != newDeviceConfiguration.densityDpi, numStackTasks > 0)); + mLastDisplayOrientation = newDeviceConfiguration.orientation; + mLastDisplayDensity = newDeviceConfiguration.densityDpi; + } + + @Override public boolean onPreDraw() { mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this); int width = mRecentsView.getWidth(); @@ -218,6 +252,7 @@ public class RecentsGridActivity extends Activity implements ViewTreeObserver.On List<Rect> rects = TaskGridLayoutAlgorithm.getRectsForTaskCount( mTasks.size(), width, height, false /* allowLineOfThree */, 30 /* padding */); + removeTaskViews(); for (int i = 0; i < rects.size(); i++) { Rect rect = rects.get(i); View taskView = mTaskViews.get(i); @@ -235,20 +270,43 @@ public class RecentsGridActivity extends Activity implements ViewTreeObserver.On startActivity(startMain); } + /** Launches the task that recents was launched from if possible. */ + boolean launchPreviousTask() { + if (mRecentsView != null) { + Task task = mTaskStack.getLaunchTarget(); + if (task != null) { + TaskView taskView = getChildViewForTask(task); + EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null, + INVALID_STACK_ID, false)); + return true; + } + } + return false; + } + + /** Dismisses recents back to the launch target task. */ + boolean dismissRecentsToLaunchTargetTaskOrHome() { + SystemServicesProxy ssp = Recents.getSystemServices(); + if (ssp.isRecentsActivityVisible()) { + // If we can launch the task that Recents was launched from, do that, otherwise go home. + if (launchPreviousTask()) return true; + dismissRecentsToHome(); + } + return false; + } + /**** EventBus events ****/ public final void onBusEvent(HideRecentsEvent event) { if (event.triggeredFromAltTab) { - // Do nothing for now. + dismissRecentsToLaunchTargetTaskOrHome(); } else if (event.triggeredFromHomeKey) { dismissRecentsToHome(); } } public final void onBusEvent(ToggleRecentsEvent event) { - // Always go back home for simplicity for now. If recents is entered from another app, this - // code will eventually need to go back to the original app. - dismissRecentsToHome(); + dismissRecentsToLaunchTargetTaskOrHome(); } public final void onBusEvent(DismissTaskViewEvent event) { @@ -306,7 +364,22 @@ public class RecentsGridActivity extends Activity implements ViewTreeObserver.On } public final void onBusEvent(LaunchNextTaskRequestEvent event) { - // Always go back home for simplicity for now. Quick switch will be supported soon. + if (mTaskStack.getTaskCount() > 0) { + // The task to launch is the second most recent, which is at index 1 given our ordering. + // If there is only one task, launch that one instead. + int launchTaskIndex = (mTaskStack.getStackTaskCount() > 1) ? 1 : 0; + Task launchTask = mTaskStack.getStackTasks().get(launchTaskIndex); + TaskView launchTaskView = getChildViewForTask(launchTask); + if (launchTaskView != null) { + EventBus.getDefault().send(new LaunchTaskEvent(launchTaskView, + launchTask, null, INVALID_STACK_ID, false /* screenPinningRequested */)); + MetricsLogger.action(this, MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK, + launchTask.key.getComponent().toString()); + return; + } + } + // We couldn't find a matching task view, or there are no tasks. Just hide recents back + // to home. EventBus.getDefault().send(new HideRecentsEvent(false, true)); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index a9aa2edd1780..0f7ad5903bc5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -561,6 +561,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private int mLastCameraLaunchSource; private PowerManager.WakeLock mGestureWakeLock; private Vibrator mVibrator; + private long[] mCameraLaunchGestureVibePattern; // Fingerprint (as computed by getLoggingFingerprint() of the last logged state. private int mLastLoggedStateFingerprint; @@ -996,6 +997,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mGestureWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "GestureWakeLock"); mVibrator = mContext.getSystemService(Vibrator.class); + int[] pattern = mContext.getResources().getIntArray( + R.array.config_cameraLaunchGestureVibePattern); + mCameraLaunchGestureVibePattern = new long[pattern.length]; + for (int i = 0; i < pattern.length; i++) { + mCameraLaunchGestureVibePattern[i] = pattern[i]; + } // receive broadcasts IntentFilter filter = new IntentFilter(); @@ -1649,9 +1656,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, newNotification.headsUpContentView = sbn.getNotification().headsUpContentView; StatusBarNotification newSbn = new StatusBarNotification(sbn.getPackageName(), - sbn.getOpPkg(), + sbn.getOpPkg(), sbn.getNotificationChannel(), sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(), - 0, newNotification, sbn.getUser(), sbn.getPostTime()); + newNotification, sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime()); updateNotification(newSbn, null); mKeysKeptForRemoteInput.add(entry.key); @@ -3552,8 +3559,14 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, AsyncTask.execute(runnable); } if (dismissShade) { - animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */, - true /* delayed*/); + if (mExpandedVisible) { + animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */, + true /* delayed*/); + } else { + + // Do it after DismissAction has been processed to conserve the needed ordering. + mHandler.post(this::runPostCollapseRunnables); + } } return deferred; }, cancelAction, afterKeyguardGone); @@ -3631,12 +3644,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, dismissKeyguardThenExecute(action, null /* cancelRunnable */, afterKeyguardGone); } - public void dismissKeyguard() { - mStatusBarKeyguardViewManager.dismiss(); - } - private void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancelAction, boolean afterKeyguardGone) { + afterKeyguardGone |= mStatusBarKeyguardViewManager.isShowing() + && mStatusBarKeyguardViewManager.isOccluded(); if (mStatusBarKeyguardViewManager.isShowing()) { mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction, afterKeyguardGone); @@ -4249,7 +4260,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } }, delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE); } - } else { + } else if (!mNotificationPanel.isCollapsing()) { instantCollapseNotificationPanel(); } updateKeyguardState(staying, false /* fromShadeLocked */); @@ -4876,7 +4887,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private void vibrateForCameraGesture() { // Make sure to pass -1 for repeat so VibratorService doesn't stop us when going to sleep. - mVibrator.vibrate(new long[]{0, 400}, -1 /* repeat */); + mVibrator.vibrate(mCameraLaunchGestureVibePattern, -1 /* repeat */); } public void onScreenTurnedOn() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index f5c5e56359a7..69decd72c03a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -155,8 +155,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (!afterKeyguardGone) { mBouncer.showWithDismissAction(r, cancelAction); } else { - mBouncer.show(false /* resetSecuritySelection */); mAfterKeyguardGoneAction = r; + mBouncer.show(false /* resetSecuritySelection */); } } updateStates(); @@ -235,10 +235,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mDeviceWillWakeUp = !mDeviceInteractive; } - public void verifyUnlock() { - dismiss(); - } - public void setNeedsInput(boolean needsInput) { mStatusBarWindowManager.setKeyguardNeedsInput(needsInput); } @@ -333,6 +329,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } }); } else { + executeAfterKeyguardGoneAction(); if (mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING) { mFingerprintUnlockController.startKeyguardFadingAway(); mPhoneStatusBar.setKeyguardFadingAway(startTime, 0, 240); @@ -372,7 +369,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mStatusBarWindowManager.setKeyguardShowing(false); mBouncer.hide(true /* destroyView */); mViewMediatorCallback.keyguardGone(); - executeAfterKeyguardGoneAction(); updateStates(); } } @@ -423,6 +419,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb /** * Dismisses the keyguard by going to the next screen or making it gone. */ + public void dismissAndCollapse() { + mPhoneStatusBar.executeRunnableDismissingKeyguard(null, null, true, false, true); + } + public void dismiss() { showBouncer(); } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/ThemePreference.java b/packages/SystemUI/src/com/android/systemui/tuner/ThemePreference.java new file mode 100644 index 000000000000..e5bb3d5d88f3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/tuner/ThemePreference.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.systemui.tuner; + +import android.app.AlertDialog; +import android.app.UiModeManager; +import android.content.Context; +import android.os.SystemProperties; +import android.support.v7.preference.ListPreference; +import android.text.TextUtils; +import android.util.AttributeSet; + +import com.android.systemui.R; + +import libcore.util.Objects; + +import com.google.android.collect.Lists; + +import java.io.File; +import java.util.ArrayList; + +public class ThemePreference extends ListPreference { + + public ThemePreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public void onAttached() { + super.onAttached(); + File file = new File("/vendor/overlay"); + ArrayList<String> options = Lists.newArrayList(file.list()); + String def = SystemProperties.get("ro.boot.vendor.overlay.theme"); + if (TextUtils.isEmpty(def)) { + def = getContext().getString(R.string.default_theme); + } + if (!options.contains(def)) { + options.add(0, def); + } + String[] list = options.toArray(new String[options.size()]); + setVisible(options.size() > 1); + setEntries(list); + setEntryValues(list); + updateValue(); + } + + private void updateValue() { + setValue(getContext().getSystemService(UiModeManager.class).getTheme()); + } + + @Override + protected void notifyChanged() { + super.notifyChanged(); + if (!Objects.equal(getValue(), + getContext().getSystemService(UiModeManager.class).getTheme())) { + new AlertDialog.Builder(getContext()) + .setTitle(R.string.change_theme_reboot) + .setPositiveButton(com.android.internal.R.string.global_action_restart, (d, i) + -> getContext().getSystemService(UiModeManager.class) + .setTheme(getValue())) + .setNegativeButton(android.R.string.cancel, (d, i) -> updateValue()) + .show(); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java index fd6500fd1eec..f835e7d57ad4 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java @@ -102,7 +102,9 @@ public class TunerFragment extends PreferenceFragment { TunerService.showResetRequest(getContext(), new Runnable() { @Override public void run() { - getActivity().finish(); + if (getActivity() != null) { + getActivity().finish(); + } } }); return true; diff --git a/services/Android.mk b/services/Android.mk index 3385bed0ba8b..291198303548 100644 --- a/services/Android.mk +++ b/services/Android.mk @@ -22,6 +22,7 @@ services := \ core \ accessibility \ appwidget \ + autofill \ backup \ devicepolicy \ midi \ diff --git a/services/autofill/Android.mk b/services/autofill/Android.mk new file mode 100644 index 000000000000..a1f19fd4c72d --- /dev/null +++ b/services/autofill/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := services.autofill + +LOCAL_SRC_FILES += \ + $(call all-java-files-under,java) + +LOCAL_JAVA_LIBRARIES := services.core + +include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java new file mode 100644 index 000000000000..3b41877bd36a --- /dev/null +++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2016 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.autofill; + +import static android.Manifest.permission.MANAGE_AUTO_FILL; +import static android.content.Context.AUTO_FILL_MANAGER_SERVICE; + +import android.Manifest; +import android.app.ActivityManager; +import android.app.AppGlobals; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Parcel; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.os.ShellCallback; +import android.os.UserHandle; +import android.provider.Settings; +import android.service.autofill.IAutoFillManagerService; +import android.text.TextUtils; +import android.util.Slog; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.os.BackgroundThread; +import com.android.server.FgThread; +import com.android.server.SystemService; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + +/** + * Entry point service for auto-fill management. + * + * <p>This service provides the {@link IAutoFillManagerService} implementation and keeps a list of + * {@link AutoFillManagerServiceImpl} per user; the real work is done by + * {@link AutoFillManagerServiceImpl} itself. + */ +public final class AutoFillManagerService extends SystemService { + + private static final String TAG = "AutoFillManagerService"; + private static final boolean DEBUG = true; // TODO: change to false once stable + + private final AutoFillManagerServiceStub mServiceStub; + private final Context mContext; + private final ContentResolver mResolver; + + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private boolean mSafeMode; + + /** + * Map of {@link AutoFillManagerServiceImpl} per user id. + * <p> + * It has to be mapped by user id because the same current user could have simultaneous sessions + * associated to different user profiles (for example, in a multi-window environment). + * <p> + * This map is filled on demand in the following scenarios: + * <ol> + * <li>On start, it sets the value for the default user. + * <li>When an auto-fill service app is removed, its entries are removed. + * <li>When the current user changes. + * <li>When the {@link android.provider.Settings.Secure#AUTO_FILL_SERVICE} changes. + * </ol> + */ + // TODO: make sure all cases listed above are handled + // TODO: should entries be removed when there is no section and have not be used for a while? + @GuardedBy("mLock") + private SparseArray<AutoFillManagerServiceImpl> mImplByUser = new SparseArray<>(); + + // TODO: should disable it on low-memory devices? if not, this attribute should be removed... + private final boolean mEnableService = true; + + public AutoFillManagerService(Context context) { + super(context); + + mContext = context; + mResolver = context.getContentResolver(); + mServiceStub = new AutoFillManagerServiceStub(); + } + + @Override + public void onStart() { + if (DEBUG) + Slog.d(TAG, "onStart(): binding as " + AUTO_FILL_MANAGER_SERVICE); + publishBinderService(AUTO_FILL_MANAGER_SERVICE, mServiceStub); + } + + // TODO: refactor so it's bound on demand, in which case it can use isSafeMode() from PM. + @Override + public void onBootPhase(int phase) { + if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { + systemRunning(isSafeMode()); + } + } + + // TODO: refactor so it's bound on demand, in which case it can use isSafeMode() from PM. + @Override + public void onStartUser(int userHandle) { + if (DEBUG) Slog.d(TAG, "onStartUser(): userHandle=" + userHandle); + + updateImplementationIfNeeded(userHandle, false); + } + + @Override + public void onUnlockUser(int userHandle) { + if (DEBUG) Slog.d(TAG, "onUnlockUser(): userHandle=" + userHandle); + + updateImplementationIfNeeded(userHandle, false); + } + + @Override + public void onSwitchUser(int userHandle) { + if (DEBUG) Slog.d(TAG, "onSwitchUser(): userHandle=" + userHandle); + + updateImplementationIfNeeded(userHandle, false); + } + + private void systemRunning(boolean safeMode) { + if (DEBUG) Slog.d(TAG, "systemRunning(): safeMode=" + safeMode); + + // TODO: register a PackageMonitor + new SettingsObserver(BackgroundThread.getHandler()); + + synchronized (mLock) { + mSafeMode = safeMode; + updateImplementationIfNeededLocked(ActivityManager.getCurrentUser(), false); + } + } + + private void updateImplementationIfNeeded(int user, boolean force) { + synchronized (mLock) { + updateImplementationIfNeededLocked(user, force); + } + } + + private void updateImplementationIfNeededLocked(int user, boolean force) { + if (DEBUG) + Slog.d(TAG, "updateImplementationIfNeededLocked(" + user + ", " + force + ")"); + + if (mSafeMode) { + if (DEBUG) Slog.d(TAG, "skipping on safe mode"); + return; + } + + final String curService = Settings.Secure.getStringForUser( + mResolver, Settings.Secure.AUTO_FILL_SERVICE, user); + if (DEBUG) + Slog.d(TAG, "Current service settings for user " + user + ": " + curService); + ComponentName serviceComponent = null; + ServiceInfo serviceInfo = null; + if (!TextUtils.isEmpty(curService)) { + try { + serviceComponent = ComponentName.unflattenFromString(curService); + serviceInfo = + AppGlobals.getPackageManager().getServiceInfo(serviceComponent, 0, user); + } catch (RuntimeException | RemoteException e) { + Slog.wtf(TAG, "Bad auto-fill service name " + curService, e); + serviceComponent = null; + serviceInfo = null; + } + } + + final AutoFillManagerServiceImpl impl = mImplByUser.get(user); + if (DEBUG) Slog.d(TAG, "Current impl: " + impl + " component: " + serviceComponent + + " info: " + serviceInfo); + + if (force || impl == null || !impl.mComponent.equals(serviceComponent)) { + if (impl != null) { + impl.shutdownLocked(); + } + if (serviceInfo != null) { + final AutoFillManagerServiceImpl newImpl = new AutoFillManagerServiceImpl(mContext, + mLock, mServiceStub, FgThread.getHandler(), user, serviceComponent); + if (DEBUG) Slog.d(TAG, "Setting impl for user " + user + " as: " + newImpl); + mImplByUser.put(user, newImpl); + newImpl.startLocked(); + } else { + if (DEBUG) Slog.d(TAG, "Removing impl for user " + user + ": " + impl); + mImplByUser.remove(user); + } + } + } + + // TODO: might need to return null instead of throw exception + private AutoFillManagerServiceImpl getImplOrThrowLocked(int userId) { + final AutoFillManagerServiceImpl impl = mImplByUser.get(userId); + if (impl == null) { + throw new IllegalStateException("no auto-fill service for user " + userId); + } + return impl; + } + + final class AutoFillManagerServiceStub extends IAutoFillManagerService.Stub { + + @Override + public String startSession(int userId, Bundle args, int flags, IBinder activityToken) { + mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG); + + synchronized (mLock) { + return getImplOrThrowLocked(userId).startSession(args, flags, activityToken); + } + } + + @Override + public boolean finishSession(int userId, String token) { + mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG); + + synchronized (mLock) { + return getImplOrThrowLocked(userId).finishSessionLocked(token); + } + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (mContext.checkCallingPermission( + Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump autofill from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + if (args.length > 0) { + if ("--sessions".equals(args[0])) { + dumpSessions(pw); + return; + } + } + synchronized (mLock) { + pw.print("mEnableService: "); pw.println(mEnableService); + pw.print("mSafeMode: "); pw.println(mSafeMode); + final int size = mImplByUser.size(); + pw.print("Number of implementations: "); + if (size == 0) { + pw.println("none"); + } else { + pw.println(size); + for (int i = 0; i < size; i++) { + pw.print("\nImplementation at index "); pw.println(i); + final AutoFillManagerServiceImpl impl = mImplByUser.valueAt(i); + impl.dumpLocked(" ", pw); + } + } + } + } + + private void dumpSessions(PrintWriter pw) { + boolean foundOne = false; + synchronized (mLock) { + final int size = mImplByUser.size(); + for (int i = 0; i < size; i++) { + final AutoFillManagerServiceImpl impl = mImplByUser.valueAt(i); + foundOne |= impl.dumpSessionsLocked("", pw); + } + } + if (!foundOne) { + pw.println("No active sessions"); + } + } + + @Override + public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, + String[] args, ShellCallback callback, ResultReceiver resultReceiver) { + (new AutoFillManagerServiceShellCommand(this)).exec( + this, in, out, err, args, callback, resultReceiver); + } + + } + + private final class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.AUTO_FILL_SERVICE), false, this, + UserHandle.USER_ALL); + } + + @Override + public void onChange(boolean selfChange, Uri uri, int userId) { + synchronized (mLock) { + updateImplementationIfNeededLocked(userId, false); + } + } + } +} diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java new file mode 100644 index 000000000000..c780062c567c --- /dev/null +++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2016 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.autofill; + +import android.app.ActivityManagerInternal; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.UserHandle; +import android.service.autofill.AutoFillService; +import android.service.autofill.AutoFillServiceInfo; +import android.service.autofill.IAutoFillService; +import android.util.Log; +import android.util.PrintWriterPrinter; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.LocalServices; +import com.android.server.autofill.AutoFillManagerService.AutoFillManagerServiceStub; + +import java.io.PrintWriter; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +/** + * Bridge between the {@code system_server}'s {@link AutoFillManagerService} and the + * app's {@link IAutoFillService} implementation. + * + * <p>It keeps a list of auto-fill sessions for a specifc user. + */ +final class AutoFillManagerServiceImpl { + + private static final String TAG = "AutoFillManagerServiceImpl"; + private static final boolean DEBUG = true; // TODO: change to false once stable + + final int mUser; + final ComponentName mComponent; + + private final Context mContext; + private final Object mLock; + private final AutoFillManagerServiceStub mServiceStub; + private final AutoFillServiceInfo mInfo; + + // Map of sessions keyed by session tokens. + @GuardedBy("mLock") + private Map<String, AutoFillSession> mSessions = new HashMap<>(); + + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) { + final String reason = intent.getStringExtra("reason"); + if (DEBUG) Slog.d(TAG, "close system dialogs: " + reason); + // TODO: close any pending UI like account selection + } + } + }; + + private final ServiceConnection mConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + if (DEBUG) Log.d(TAG, "onServiceConnected():" + name); + synchronized (mLock) { + mService = IAutoFillService.Stub.asInterface(service); + try { + mService.ready(); + } catch (RemoteException e) { + Slog.w(TAG, "Exception on service.ready(): " + e); + } + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + if (DEBUG) Log.d(TAG, name + " disconnected"); + mService = null; + } + }; + + @GuardedBy("mLock") + private IAutoFillService mService; + private boolean mBound; + private boolean mValid; + + AutoFillManagerServiceImpl(Context context, Object lock, AutoFillManagerServiceStub stub, + Handler handler, int user, ComponentName component) { + mContext = context; + mLock = lock; + mServiceStub = stub; + mUser = user; + mComponent = component; + + AutoFillServiceInfo info; + try { + info = new AutoFillServiceInfo(component, mUser); + } catch (PackageManager.NameNotFoundException e) { + Slog.w(TAG, "Auto-fill service not found: " + component, e); + mInfo = null; + mValid = false; + return; + } + mInfo = info; + if (mInfo.getParseError() != null) { + Slog.w(TAG, "Bad auto-fill service: " + mInfo.getParseError()); + mValid = false; + return; + } + + mValid = true; + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + mContext.registerReceiver(mBroadcastReceiver, filter, null, handler); + } + + void startLocked() { + if (DEBUG) Slog.d(TAG, "startLocked()"); + + final Intent intent = new Intent(AutoFillService.SERVICE_INTERFACE); + intent.setComponent(mComponent); + mBound = mContext.bindServiceAsUser(intent, mConnection, + Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, new UserHandle(mUser)); + if (!mBound) { + Slog.w(TAG, "Failed binding to auto-fill service " + mComponent); + return; + } + if (DEBUG) Slog.d(TAG, "Bound to " + mComponent); + } + + String startSession(Bundle args, int flags, IBinder activityToken) { + + if (!mBound) { + // TODO: should it bind on demand? Or perhaps always run when on on low-memory? + Slog.w(TAG, "startSession() failed because it's not bound to service"); + return null; + } + + // TODO: session should have activity ids, so same session is reused when called again + // for the same activity. + + // TODO: activityToken should probably not be null, but we need to wait until the UI is + // triggering the call (for now it's trough 'adb shell cmd autofill start session' + if (activityToken == null) { + // Let's get top activities from all visible stacks. + + // TODO: overload getTopVisibleActivities() to take userId, otherwise it could return + // activities for different users when a work profile app is displayed in another + // window (in a multi-window environment). + final List<IBinder> topActivities = LocalServices + .getService(ActivityManagerInternal.class).getTopVisibleActivities(); + if (DEBUG) + Slog.d(TAG, "Top activities (" + topActivities.size() + "): " + topActivities); + if (topActivities.isEmpty()) { + Slog.w(TAG, "Could not get top activity"); + return null; + } + activityToken = topActivities.get(0); + } + + synchronized (mLock) { + return startSessionLocked(args, flags, activityToken); + } + } + + // TODO: remove args and flags if not needed? + private String startSessionLocked(Bundle args, int flags, IBinder activityToken) { + + final String sessionToken = UUID.randomUUID().toString(); + + if (DEBUG) Slog.d(TAG, "Starting session for user " + mUser + + ": sessionToken=" + sessionToken + ", activityToken=" + activityToken); + + final AutoFillSession session = + new AutoFillSession(mService, mLock, sessionToken, activityToken); + session.startLocked(); + mSessions.put(sessionToken, session); + + return sessionToken; + } + + // TODO: need a way to automatically call it when the activity is destroyed. + boolean finishSessionLocked(String token) { + if (DEBUG) Slog.d(TAG, "Removing session " + token + " for user " + mUser); + final AutoFillSession session = mSessions.remove(token); + if (session != null) { + session.finishLocked(); + } + return session != null; + } + + void shutdownLocked() { + if (DEBUG) Slog.d(TAG, "shutdownLocked()"); + + try { + if (mService != null) { + mService.shutdown(); + } + } catch (RemoteException e) { + Slog.w(TAG, "RemoteException in shutdown", e); + } + + if (mBound) { + mContext.unbindService(mConnection); + mBound = false; + } + if (mValid) { + mContext.unregisterReceiver(mBroadcastReceiver); + } + } + + void dumpLocked(String prefix, PrintWriter pw) { + if (!mValid) { + pw.print(" NOT VALID: "); + if (mInfo == null) { + pw.println("no info"); + } else { + pw.println(mInfo.getParseError()); + } + return; + } + + pw.print(prefix); pw.print("mUser="); pw.println(mUser); + pw.print(prefix); pw.print("mComponent="); pw.println(mComponent.flattenToShortString()); + pw.print(prefix); pw.print("mBound="); pw.println(mBound); + pw.print(prefix); pw.print("mService="); pw.println(mService); + + if (DEBUG) { + // ServiceInfo dump is too noisy and redundant (it can be obtained through other dumps) + pw.print(prefix); pw.println("Service info:"); + mInfo.getServiceInfo().dump(new PrintWriterPrinter(pw), prefix + prefix); + } + + if (!dumpSessionsLocked(prefix, pw)) { + pw.print(prefix); pw.print("No active sessions for user "); pw.println(mUser); + } + } + + boolean dumpSessionsLocked(String prefix, PrintWriter pw) { + if (mSessions.isEmpty()) { + return false; + } + + pw.print(mSessions.size());pw.println(" active sessions:"); + final String sessionPrefix = prefix + prefix; + for (AutoFillSession session : mSessions.values()) { + pw.println(); + session.dumpLocked(sessionPrefix, pw); + } + return true; + } + + @Override + public String toString() { + return "[AutoFillManagerServiceImpl: user=" + mUser + + ", component=" + mComponent.flattenToShortString() + "]"; + } +} diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java new file mode 100644 index 000000000000..4e08ed6c7653 --- /dev/null +++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2016 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.autofill; + +import android.app.ActivityManager; +import android.os.RemoteException; +import android.os.ShellCommand; +import android.os.UserHandle; +import android.service.autofill.IAutoFillManagerService; + +import java.io.PrintWriter; + +public final class AutoFillManagerServiceShellCommand extends ShellCommand { + + private final IAutoFillManagerService.Stub mService; + + public AutoFillManagerServiceShellCommand(IAutoFillManagerService.Stub service) { + mService = service; + } + + @Override + public int onCommand(String cmd) { + if (cmd == null) { + return handleDefaultCommands(cmd); + } + final PrintWriter pw = getOutPrintWriter(); + try { + switch (cmd) { + case "start": + return runStart(pw); + case "finish": + return runFinish(pw); + default: + return handleDefaultCommands(cmd); + } + } catch (RemoteException e) { + pw.println("error: " + e); + } + return -1; + } + + @Override + public void onHelp() { + try (final PrintWriter pw = getOutPrintWriter();) { + pw.println("AutoFill Service (autofill) commands:"); + pw.println(" help"); + pw.println(" Prints this help text."); + pw.println(""); + pw.println(" start session [--user USER_ID]"); + pw.println(" Starts an auto-fill session. " + + "Prints 'token:SESSION_TOKEN if successful, or error message"); + pw.println(""); + pw.println(" finish session <TOKEN> [--user USER_ID]"); + pw.println(" Finishes a session with the given TOKEN. " + + "Prints empty string if successful, or error message."); + pw.println(""); + } + } + + private int runStart(PrintWriter pw) throws RemoteException { + final String type = getNextArg(); + if (type == null) { + pw.println("Error: didn't specify type of data to start"); + return -1; + } + switch (type) { + case "session": + return startAutoFillSession(pw); + } + pw.println("Error: unknown start type '" + type + "'"); + return -1; + } + + private int runFinish(PrintWriter pw) throws RemoteException { + final String type = getNextArg(); + if (type == null) { + pw.println("Error: didn't specify type of data to finish"); + return -1; + } + switch (type) { + case "session": + return finishAutoFillSession(pw); + } + pw.println("Error: unknown finish type '" + type + "'"); + return -1; + } + + private int startAutoFillSession(PrintWriter pw) throws RemoteException { + final int userId = getUserIdFromArgs(); + final String token = mService.startSession(userId, null, 0, null); + pw.print("token:"); pw.println(token); + return 0; + } + + private int finishAutoFillSession(PrintWriter pw) throws RemoteException { + final String token = getNextArgRequired(); + final int userId = getUserIdFromArgs(); + + boolean finished = mService.finishSession(userId, token); + if (!finished) { + pw.println("No such session"); + return 1; + } + return 0; + } + + private int getUserIdFromArgs() { + if ("--user".equals(getNextArg())) { + return UserHandle.parseUserArg(getNextArgRequired()); + } + return ActivityManager.getCurrentUser(); + } +} diff --git a/services/autofill/java/com/android/server/autofill/AutoFillSession.java b/services/autofill/java/com/android/server/autofill/AutoFillSession.java new file mode 100644 index 000000000000..44637c3f5f46 --- /dev/null +++ b/services/autofill/java/com/android/server/autofill/AutoFillSession.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2016 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.autofill; + +import android.app.ActivityManager; +import android.app.ActivityManagerNative; +import android.app.IActivityManager; +import android.app.assist.AssistStructure; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.service.autofill.AutoFillService; +import android.service.autofill.IAutoFillService; +import android.service.voice.VoiceInteractionSession; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.os.IResultReceiver; + +import java.io.PrintWriter; + +/** + * An auto-fill session between the system's {@link AutoFillManagerServiceImpl} and the provider's + * {@link AutoFillService} implementation. + */ +final class AutoFillSession { + + private static final String TAG = "AutoFillSession"; + + private static final boolean FOCUSED = true; + private static final boolean NEW_SESSION_ID = true; + + private final IAutoFillService mService; + private final String mSessionToken; + private final IBinder mActivityToken; + private final Object mLock; + private final IActivityManager mAm; + + private final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() { + @Override + public void send(int resultCode, Bundle resultData) throws RemoteException { + synchronized (mLock) { + mPendingResponse = false; + mAssistResponse = resultData; + deliverSessionDataLocked(); + } + } + }; + + // Assist data is filled asynchronously. + @GuardedBy("mLock") + private Bundle mAssistResponse; + @GuardedBy("mLock") + private boolean mPendingResponse; + + AutoFillSession(IAutoFillService service, Object lock, String sessionToken, + IBinder activityToken) { + mService = service; + mSessionToken = sessionToken; + mActivityToken = activityToken; + mLock = lock; + mAm = ActivityManagerNative.getDefault(); + } + + void startLocked() { + /* + * TODO: apply security checks below: + * - checks if disabled by secure settings / device policy + * - log operation using noteOp() + * - check flags + * - display disclosure if needed + */ + mAssistResponse = null; + mPendingResponse = true; + try { + // TODO: add MetricsLogger call + if (!mAm.requestAssistContextExtras(ActivityManager.ASSIST_CONTEXT_FULL, + mAssistReceiver, (Bundle) null, mActivityToken, FOCUSED, NEW_SESSION_ID)) { + mPendingResponse = false; + Slog.w(TAG, "requestAssistContextExtras() rejected"); + } + } catch (RemoteException e) { + // Should happen, it's a local call. + } + } + + void finishLocked() { + try { + mService.finishSession(mSessionToken); + } catch (RemoteException e) { + Slog.e(TAG, "auto-fill service failed to finish session " + mSessionToken, e); + } + } + + private void deliverSessionDataLocked() { + if (mAssistResponse == null) { + Slog.w(TAG, "No assist data for session " + mSessionToken); + return; + } + + final Bundle assistData = mAssistResponse.getBundle(VoiceInteractionSession.KEY_DATA); + final AssistStructure structure = + mAssistResponse.getParcelable(VoiceInteractionSession.KEY_STRUCTURE); + try { + mService.newSession(mSessionToken, assistData, 0, structure); + } catch (RemoteException e) { + Slog.e(TAG, "auto-fill service failed to start session " + mSessionToken, e); + } finally { + mPendingResponse = false; + // We could set mAssistResponse to null here, but we don't so it's shown on dump() + } + } + + void dumpLocked(String prefix, PrintWriter pw) { + pw.print(prefix); pw.print("mSessionToken="); pw.println(mSessionToken); + pw.print(prefix); pw.print("mActivityToken="); pw.println(mActivityToken); + pw.print(prefix); pw.print("mPendingResponse="); pw.println(mPendingResponse); + pw.print(prefix); pw.print("mAssistResponse="); pw.println(mAssistResponse); + } + +} diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 82897fb40c50..2558045a0e22 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -1940,13 +1940,9 @@ class AlarmManagerService extends SystemService { } for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) { final Alarm a = mPendingWhileIdleAlarms.get(i); - try { - if (a.uid == uid && ActivityManagerNative.getDefault().getAppStartMode( - uid, a.packageName) == ActivityManager.APP_START_MODE_DISABLED) { - // Don't set didRemove, since this doesn't impact the scheduled alarms. - mPendingWhileIdleAlarms.remove(i); - } - } catch (RemoteException e) { + if (a.uid == uid) { + // Don't set didRemove, since this doesn't impact the scheduled alarms. + mPendingWhileIdleAlarms.remove(i); } } @@ -2807,15 +2803,22 @@ class AlarmManagerService extends SystemService { @Override public void onUidStateChanged(int uid, int procState) throws RemoteException { } - @Override public void onUidGone(int uid) throws RemoteException { + @Override public void onUidGone(int uid, boolean disabled) throws RemoteException { + if (disabled) { + synchronized (mLock) { + removeForStoppedLocked(uid); + } + } } @Override public void onUidActive(int uid) throws RemoteException { } - @Override public void onUidIdle(int uid) throws RemoteException { - synchronized (mLock) { - removeForStoppedLocked(uid); + @Override public void onUidIdle(int uid, boolean disabled) throws RemoteException { + if (disabled) { + synchronized (mLock) { + removeForStoppedLocked(uid); + } } } }; diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 645604848580..9b3fac3d66d4 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -1364,7 +1364,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetoothLock.writeLock().lock(); if (msg.arg1 == SERVICE_IBLUETOOTHGATT) { - mBluetoothGatt = IBluetoothGatt.Stub.asInterface(service); + mBluetoothGatt = IBluetoothGatt.Stub + .asInterface(Binder.allowBlocking(service)); onBluetoothGattServiceUp(); break; } // else must be SERVICE_IBLUETOOTH @@ -1374,7 +1375,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBinding = false; mBluetoothBinder = service; - mBluetooth = IBluetooth.Stub.asInterface(service); + mBluetooth = IBluetooth.Stub.asInterface(Binder.allowBlocking(service)); if (!isNameAndAddressSet()) { Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 2698f9546357..d1f07a505359 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -29,6 +29,7 @@ import com.android.internal.inputmethod.InputMethodUtils; import com.android.internal.inputmethod.InputMethodUtils.InputMethodSettings; import com.android.internal.os.HandlerCaller; import com.android.internal.os.SomeArgs; +import com.android.internal.os.TransferPipe; import com.android.internal.util.FastXmlSerializer; import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethod; @@ -59,6 +60,7 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; @@ -3968,10 +3970,22 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + mCurAttribute.packageName + " packageName=" + packageName); return null; } + // This user ID can never bee spoofed. final int imeUserId = UserHandle.getUserId(uid); + // This user ID can never bee spoofed. final int appUserId = UserHandle.getUserId(mCurClient.uid); - return new InputContentUriTokenHandler(contentUri, uid, packageName, imeUserId, - appUserId); + // This user ID may be invalid if "contentUri" embedded an invalid user ID. + final int contentUriOwnerUserId = ContentProvider.getUserIdFromUri(contentUri, + imeUserId); + final Uri contentUriWithoutUserId = ContentProvider.getUriWithoutUserId(contentUri); + // Note: InputContentUriTokenHandler.take() checks whether the IME (specified by "uid") + // actually has the right to grant a read permission for "contentUriWithoutUserId" that + // is claimed to belong to "contentUriOwnerUserId". For example, specifying random + // content URI and/or contentUriOwnerUserId just results in a SecurityException thrown + // from InputContentUriTokenHandler.take() and can never be allowed beyond what is + // actually allowed to "uid", which is guaranteed to be the IME's one. + return new InputContentUriTokenHandler(contentUriWithoutUserId, uid, + packageName, contentUriOwnerUserId, appUserId); } } @@ -4041,9 +4055,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (client != null) { pw.flush(); try { - client.client.asBinder().dump(fd, args); - } catch (RemoteException e) { - p.println("Input method client dead: " + e); + TransferPipe.dumpAsync(client.client.asBinder(), fd, args); + } catch (IOException | RemoteException e) { + p.println("Failed to dump input method client: " + e); } } else { p.println("No input method client."); @@ -4057,9 +4071,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub p.println(" "); pw.flush(); try { - focusedWindowClient.client.asBinder().dump(fd, args); - } catch (RemoteException e) { - p.println("Input method client in focused window dead: " + e); + TransferPipe.dumpAsync(focusedWindowClient.client.asBinder(), fd, args); + } catch (IOException | RemoteException e) { + p.println("Failed to dump input method client in focused window: " + e); } } @@ -4067,9 +4081,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (method != null) { pw.flush(); try { - method.asBinder().dump(fd, args); - } catch (RemoteException e) { - p.println("Input method service dead: " + e); + TransferPipe.dumpAsync(method.asBinder(), fd, args); + } catch (IOException | RemoteException e) { + p.println("Failed to dump input method service: " + e); } } else { p.println("No input method service."); diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java index 33f92344f615..0414b47e0604 100644 --- a/services/core/java/com/android/server/MmsServiceBroker.java +++ b/services/core/java/com/android/server/MmsServiceBroker.java @@ -92,7 +92,7 @@ public class MmsServiceBroker extends SystemService { public void onServiceConnected(ComponentName name, IBinder service) { Slog.i(TAG, "MmsService connected"); synchronized (MmsServiceBroker.this) { - mService = IMms.Stub.asInterface(service); + mService = IMms.Stub.asInterface(Binder.allowBlocking(service)); MmsServiceBroker.this.notifyAll(); } } diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java index 83d6344c95b0..f5cda0ab4d63 100644 --- a/services/core/java/com/android/server/NetworkScoreService.java +++ b/services/core/java/com/android/server/NetworkScoreService.java @@ -42,8 +42,10 @@ import android.util.Log; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; +import com.android.internal.os.TransferPipe; import java.io.FileDescriptor; +import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; @@ -418,12 +420,9 @@ public class NetworkScoreService extends INetworkScoreService.Stub { for (INetworkScoreCache scoreCache : getScoreCaches()) { try { - scoreCache.asBinder().dump(fd, args); - } catch (RemoteException e) { - writer.println("Unable to dump score cache"); - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Unable to dump score cache", e); - } + TransferPipe.dumpAsync(scoreCache.asBinder(), fd, args); + } catch (IOException | RemoteException e) { + writer.println("Failed to dump score cache: " + e); } } if (mServiceConnection != null) { diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index 2825cf9d867b..626869718580 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -39,19 +39,24 @@ import android.os.Handler; import android.os.IBinder; import android.os.PowerManager; import android.os.RemoteException; +import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; import android.service.dreams.Sandman; import android.util.Slog; +import android.view.WindowManagerInternal; +import android.view.WindowManagerPolicy; import java.io.FileDescriptor; import java.io.PrintWriter; import com.android.internal.R; import com.android.internal.app.DisableCarModeActivity; +import com.android.server.power.ShutdownThread; import com.android.server.twilight.TwilightListener; import com.android.server.twilight.TwilightManager; import com.android.server.twilight.TwilightState; +import com.android.server.wm.WindowManagerService; final class UiModeManagerService extends SystemService { private static final String TAG = UiModeManager.class.getSimpleName(); @@ -297,6 +302,30 @@ final class UiModeManagerService extends SystemService { } @Override + public void setTheme(String theme) { + if (getContext().checkCallingOrSelfPermission( + android.Manifest.permission.MODIFY_THEME_OVERLAY) + != PackageManager.PERMISSION_GRANTED) { + Slog.e(TAG, "setTheme requires MODIFY_THEME_OVERLAY permission"); + return; + } + SystemProperties.set("persist.vendor.overlay.theme", theme); + mHandler.post(() -> ShutdownThread.reboot(getContext(), + PowerManager.SHUTDOWN_USER_REQUESTED, false)); + } + + @Override + public String getTheme() { + if (getContext().checkCallingOrSelfPermission( + android.Manifest.permission.MODIFY_THEME_OVERLAY) + != PackageManager.PERMISSION_GRANTED) { + Slog.e(TAG, "setTheme requires MODIFY_THEME_OVERLAY permission"); + return null; + } + return SystemProperties.get("persist.vendor.overlay.theme"); + } + + @Override public int getNightMode() { synchronized (mLock) { return mNightMode; diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index dc4a52d1c4b7..136e02c434d5 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -348,7 +348,7 @@ public final class ActiveServices { // Before going further -- if this app is not allowed to run in the // background, then at this point we aren't going to let it period. final int allowed = mAm.checkAllowBackgroundLocked( - r.appInfo.uid, r.packageName, callingPid, true); + r.appInfo.uid, r.packageName, callingPid, false); if (allowed != ActivityManager.APP_START_MODE_NORMAL) { Slog.w(TAG, "Background start not allowed: service " + service + " to " + r.name.flattenToShortString() @@ -594,7 +594,8 @@ public final class ActiveServices { for (int i=services.mServicesByName.size()-1; i>=0; i--) { ServiceRecord service = services.mServicesByName.valueAt(i); if (service.appInfo.uid == uid && service.startRequested) { - if (mAm.mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND, + if (service.appInfo.isEphemeralApp() || + mAm.mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, service.packageName) != AppOpsManager.MODE_ALLOWED) { if (stopping == null) { stopping = new ArrayList<>(); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 42d16fb55a9d..d60f1159bef0 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -21,6 +21,7 @@ import android.app.ApplicationThreadConstants; import android.app.ContentProviderHolder; import android.app.IActivityManager; import android.app.WaitResult; +import android.graphics.PointF; import android.os.IDeviceIdentifiersPolicyService; import com.android.internal.telephony.TelephonyIntents; import com.google.android.collect.Lists; @@ -290,10 +291,8 @@ import static android.provider.Settings.Global.DEBUG_APP; import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL; -import static android.provider.Settings.Global.LENIENT_BACKGROUND_CHECK; import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER; import static android.provider.Settings.System.FONT_SCALE; -import static android.util.TypedValue.COMPLEX_UNIT_DIP; import static android.view.Display.DEFAULT_DISPLAY; import static com.android.internal.util.XmlUtils.readBooleanAttribute; @@ -1360,7 +1359,6 @@ public class ActivityManagerService extends IActivityManager.Stub String mOrigDebugApp = null; boolean mOrigWaitForDebugger = false; boolean mAlwaysFinishActivities = false; - boolean mLenientBackgroundCheck = false; boolean mForceResizableActivities; boolean mSupportsMultiWindow; boolean mSupportsFreeformWindowManagement; @@ -1575,6 +1573,10 @@ public class ActivityManagerService extends IActivityManager.Stub int mThumbnailHeight; float mFullscreenThumbnailScale; + /** The aspect ratio bounds of the PIP. */ + float mMinPipAspectRatio; + float mMaxPipAspectRatio; + final ServiceThread mHandlerThread; final MainHandler mHandler; final UiHandler mUiHandler; @@ -2732,7 +2734,8 @@ public class ActivityManagerService extends IActivityManager.Stub for (int i=0; i<N; i++) { Parcel data2 = Parcel.obtain(); try { - procs.get(i).transact(IBinder.SYSPROPS_TRANSACTION, data2, null, 0); + procs.get(i).transact(IBinder.SYSPROPS_TRANSACTION, data2, null, + Binder.FLAG_ONEWAY); } catch (RemoteException e) { } data2.recycle(); @@ -4144,7 +4147,7 @@ public class ActivityManagerService extends IActivityManager.Stub if ((reg.which & ActivityManager.UID_OBSERVER_IDLE) != 0) { if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, "UID idle uid=" + item.uid); - observer.onUidIdle(item.uid); + observer.onUidIdle(item.uid, item.ephemeral); } if (VALIDATE_UID_STATES && i == 0) { if (validateUid != null) { @@ -4166,7 +4169,7 @@ public class ActivityManagerService extends IActivityManager.Stub if ((reg.which & ActivityManager.UID_OBSERVER_GONE) != 0) { if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, "UID gone uid=" + item.uid); - observer.onUidGone(item.uid); + observer.onUidGone(item.uid, item.ephemeral); } if (reg.lastProcStates != null) { reg.lastProcStates.delete(item.uid); @@ -7469,6 +7472,15 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void enterPictureInPictureMode(IBinder token) { + enterPictureInPictureMode(token, DEFAULT_DISPLAY, null /* aspectRatio */); + } + + @Override + public void enterPictureInPictureModeWithAspectRatio(IBinder token, float aspectRatio) { + enterPictureInPictureMode(token, DEFAULT_DISPLAY, aspectRatio); + } + + public void enterPictureInPictureMode(IBinder token, int displayId, Float aspectRatio) { final long origId = Binder.clearCallingIdentity(); try { synchronized(this) { @@ -7478,7 +7490,6 @@ public class ActivityManagerService extends IActivityManager.Stub } final ActivityRecord r = ActivityRecord.forTokenLocked(token); - if (r == null) { throw new IllegalStateException("enterPictureInPictureMode: " + "Can't find activity for token=" + token); @@ -7489,21 +7500,55 @@ public class ActivityManagerService extends IActivityManager.Stub + "Picture-In-Picture not supported for r=" + r); } - // Use the default launch bounds for pinned stack if it doesn't exist yet or use the - // current bounds. - final ActivityStack pinnedStack = mStackSupervisor.getStack(PINNED_STACK_ID); - final Rect bounds = (pinnedStack != null) - ? pinnedStack.mBounds - : mWindowManager.getPictureInPictureDefaultBounds(DEFAULT_DISPLAY); + if (aspectRatio != null && !isValidPictureInPictureAspectRatio(aspectRatio)) { + throw new IllegalArgumentException(String.format("enterPictureInPictureMode: " + + "Aspect ratio is too extreme (must be between %f and %f).", + mMinPipAspectRatio, mMaxPipAspectRatio)); + } + + final Rect bounds = isValidPictureInPictureAspectRatio(aspectRatio) + ? mWindowManager.getPictureInPictureBounds(displayId, aspectRatio) + : mWindowManager.getPictureInPictureDefaultBounds(displayId); + mStackSupervisor.moveActivityToPinnedStackLocked(r, "enterPictureInPictureMode", + bounds); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void setPictureInPictureAspectRatio(IBinder token, float aspectRatio) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized(this) { + final ActivityRecord r = ActivityRecord.forTokenLocked(token); + if (r == null || r.getStack().mStackId != PINNED_STACK_ID) { + throw new IllegalStateException("setPictureInPictureAspectRatio: " + + "Requesting activity must be in picture-in-picture mode."); + } + + if (!isValidPictureInPictureAspectRatio(aspectRatio)) { + throw new IllegalArgumentException(String.format( + "setPictureInPictureAspectRatio: Aspect ratio is too extreme (must be " + + "between %f and %f).", mMinPipAspectRatio, + mMaxPipAspectRatio)); + } - mStackSupervisor.moveActivityToPinnedStackLocked( - r, "enterPictureInPictureMode", bounds); + mWindowManager.setPictureInPictureAspectRatio(aspectRatio); } } finally { Binder.restoreCallingIdentity(origId); } } + private boolean isValidPictureInPictureAspectRatio(Float aspectRatio) { + if (aspectRatio == null) { + return false; + } + return mMinPipAspectRatio <= aspectRatio && aspectRatio <= mMaxPipAspectRatio; + } + // ========================================================= // PROCESS INFO // ========================================================= @@ -7791,38 +7836,43 @@ public class ActivityManagerService extends IActivityManager.Stub public int getAppStartMode(int uid, String packageName) { synchronized (this) { - return checkAllowBackgroundLocked(uid, packageName, -1, true); + return checkAllowBackgroundLocked(uid, packageName, -1, false); } } int checkAllowBackgroundLocked(int uid, String packageName, int callingPid, - boolean allowWhenForeground) { + boolean alwaysRestrict) { UidRecord uidRec = mActiveUids.get(uid); - if (!mLenientBackgroundCheck) { - if (!allowWhenForeground || uidRec == null - || uidRec.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) { - if (mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, - packageName) != AppOpsManager.MODE_ALLOWED) { - return ActivityManager.APP_START_MODE_DELAYED; - } + if (uidRec == null || alwaysRestrict || uidRec.idle) { + boolean ephemeral; + if (uidRec == null) { + ephemeral = getPackageManagerInternalLocked().isPackageEphemeral( + UserHandle.getUserId(uid), packageName); + } else { + ephemeral = uidRec.ephemeral; } - } else if (uidRec == null || uidRec.idle) { - if (callingPid >= 0) { - ProcessRecord proc; - synchronized (mPidsSelfLocked) { - proc = mPidsSelfLocked.get(callingPid); + if (ephemeral) { + // We are hard-core about ephemeral apps not running in the background. + return ActivityManager.APP_START_MODE_DISABLED; + } else { + if (callingPid >= 0) { + ProcessRecord proc; + synchronized (mPidsSelfLocked) { + proc = mPidsSelfLocked.get(callingPid); + } + if (proc != null && proc.curProcState + < ActivityManager.PROCESS_STATE_RECEIVER) { + // Whoever is instigating this is in the foreground, so we will allow it + // to go through. + return ActivityManager.APP_START_MODE_NORMAL; + } } - if (proc != null && proc.curProcState < ActivityManager.PROCESS_STATE_RECEIVER) { - // Whoever is instigating this is in the foreground, so we will allow it - // to go through. - return ActivityManager.APP_START_MODE_NORMAL; + if (mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, + packageName) != AppOpsManager.MODE_ALLOWED) { + return ActivityManager.APP_START_MODE_DELAYED; } } - if (mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName) - != AppOpsManager.MODE_ALLOWED) { - return ActivityManager.APP_START_MODE_DELAYED; - } } return ActivityManager.APP_START_MODE_NORMAL; } @@ -11869,25 +11919,6 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void setLenientBackgroundCheck(boolean enabled) { - enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT, - "setLenientBackgroundCheck()"); - - long ident = Binder.clearCallingIdentity(); - try { - Settings.Global.putInt( - mContext.getContentResolver(), - Settings.Global.LENIENT_BACKGROUND_CHECK, enabled ? 1 : 0); - - synchronized (this) { - mLenientBackgroundCheck = enabled; - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - @Override public void setActivityController(IActivityController controller, boolean imAMonkey) { enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER, "setActivityController()"); @@ -12986,8 +13017,6 @@ public class ActivityManagerService extends IActivityManager.Stub final boolean waitForDebugger = Settings.Global.getInt(resolver, WAIT_FOR_DEBUGGER, 0) != 0; final boolean alwaysFinishActivities = Settings.Global.getInt(resolver, ALWAYS_FINISH_ACTIVITIES, 0) != 0; - final boolean lenientBackgroundCheck = - Settings.Global.getInt(resolver, LENIENT_BACKGROUND_CHECK, 0) != 0; final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0; final boolean forceResizable = Settings.Global.getInt( resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0; @@ -13008,7 +13037,6 @@ public class ActivityManagerService extends IActivityManager.Stub mDebugApp = mOrigDebugApp = debugApp; mWaitForDebugger = mOrigWaitForDebugger = waitForDebugger; mAlwaysFinishActivities = alwaysFinishActivities; - mLenientBackgroundCheck = lenientBackgroundCheck; mSupportsLeanbackOnly = supportsLeanbackOnly; mForceResizableActivities = forceResizable; if (supportsMultiWindow || forceResizable) { @@ -13035,6 +13063,10 @@ public class ActivityManagerService extends IActivityManager.Stub com.android.internal.R.dimen.thumbnail_width); mThumbnailHeight = res.getDimensionPixelSize( com.android.internal.R.dimen.thumbnail_height); + mMinPipAspectRatio = res.getFloat( + com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio); + mMaxPipAspectRatio = res.getFloat( + com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio); mAppErrors.loadAppsNotReportingCrashesFromConfigLocked(res.getString( com.android.internal.R.string.config_appsNotReportingCrashes)); mUserController.mUserSwitchUiEnabled = !res.getBoolean( @@ -14781,9 +14813,8 @@ public class ActivityManagerService extends IActivityManager.Stub } } if (dumpPackage == null) { - if (mAlwaysFinishActivities || mLenientBackgroundCheck) { - pw.println(" mAlwaysFinishActivities=" + mAlwaysFinishActivities - + " mLenientBackgroundCheck=" + mLenientBackgroundCheck); + if (mAlwaysFinishActivities) { + pw.println(" mAlwaysFinishActivities=" + mAlwaysFinishActivities); } if (mController != null) { pw.println(" mController=" + mController @@ -20708,6 +20739,7 @@ public class ActivityManagerService extends IActivityManager.Stub pendingChange.change = change; pendingChange.processState = uidRec != null ? uidRec.setProcState : ActivityManager.PROCESS_STATE_NONEXISTENT; + pendingChange.ephemeral = uidRec.ephemeral; // Directly update the power manager, since we sit on top of it and it is critical // it be kept in sync (so wake locks will be held as soon as appropriate). @@ -21075,8 +21107,11 @@ public class ActivityManagerService extends IActivityManager.Stub } else { // Keeping this process, update its uid. final UidRecord uidRec = app.uidRecord; - if (uidRec != null && uidRec.curProcState > app.curProcState) { - uidRec.curProcState = app.curProcState; + if (uidRec != null) { + uidRec.ephemeral = app.info.isEphemeralApp(); + if (uidRec.curProcState > app.curProcState) { + uidRec.curProcState = app.curProcState; + } } } @@ -21335,6 +21370,63 @@ public class ActivityManagerService extends IActivityManager.Stub } } + @Override + public void makePackageIdle(String packageName, int userId) { + if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) + != PackageManager.PERMISSION_GRANTED) { + String msg = "Permission Denial: makePackageIdle() from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid() + + " requires " + android.Manifest.permission.FORCE_STOP_PACKAGES; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + final int callingPid = Binder.getCallingPid(); + userId = mUserController.handleIncomingUser(callingPid, Binder.getCallingUid(), + userId, true, ALLOW_FULL_ONLY, "makePackageIdle", null); + long callingId = Binder.clearCallingIdentity(); + synchronized(this) { + try { + IPackageManager pm = AppGlobals.getPackageManager(); + int pkgUid = -1; + try { + pkgUid = pm.getPackageUid(packageName, MATCH_UNINSTALLED_PACKAGES + | MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM); + } catch (RemoteException e) { + } + if (pkgUid == -1) { + throw new IllegalArgumentException("Unknown package name " + packageName); + } + + if (mLocalPowerManager != null) { + mLocalPowerManager.startUidChanges(); + } + final int appId = UserHandle.getAppId(pkgUid); + final int N = mActiveUids.size(); + for (int i=N-1; i>=0; i--) { + final UidRecord uidRec = mActiveUids.valueAt(i); + final long bgTime = uidRec.lastBackgroundTime; + if (bgTime > 0 && !uidRec.idle) { + if (UserHandle.getAppId(uidRec.uid) == appId) { + if (userId == UserHandle.USER_ALL || + userId == UserHandle.getUserId(uidRec.uid)) { + uidRec.idle = true; + Slog.w(TAG, "Idling uid " + UserHandle.formatUid(uidRec.uid) + + " from package " + packageName + " user " + userId); + doStopUidLocked(uidRec.uid, uidRec); + } + } + } + } + } finally { + if (mLocalPowerManager != null) { + mLocalPowerManager.finishUidChanges(); + } + Binder.restoreCallingIdentity(callingId); + } + } + } + final void idleUids() { synchronized (this) { final int N = mActiveUids.size(); @@ -22156,6 +22248,15 @@ public class ActivityManagerService extends IActivityManager.Stub // no need to synchronize(this) just to read & return the value return mSystemReady; } + + @Override + public void notifyKeyguardTrustedChanged() { + synchronized (ActivityManagerService.this) { + if (mKeyguardController.isKeyguardShowing()) { + mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); + } + } + } } private final class SleepTokenImpl extends SleepToken { diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index abeea74ffdae..52ad72d819fe 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -169,6 +169,8 @@ final class ActivityManagerShellCommand extends ShellCommand { return runKill(pw); case "kill-all": return runKillAll(pw); + case "make-idle": + return runMakeIdle(pw); case "monitor": return runMonitor(pw); case "hang": @@ -205,8 +207,6 @@ final class ActivityManagerShellCommand extends ShellCommand { return runTrackAssociations(pw); case "untrack-associations": return runUntrackAssociations(pw); - case "lenient-background-check": - return runLenientBackgroundCheck(pw); case "get-uid-state": return getUidState(pw); case "get-config": @@ -853,6 +853,22 @@ final class ActivityManagerShellCommand extends ShellCommand { return 0; } + int runMakeIdle(PrintWriter pw) throws RemoteException { + int userId = UserHandle.USER_ALL; + + String opt; + while ((opt = getNextOption()) != null) { + if (opt.equals("--user")) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + } else { + getErrPrintWriter().println("Error: Unknown option: " + opt); + return -1; + } + } + mInterface.makePackageIdle(getNextArgRequired(), userId); + return 0; + } + static final class MyActivityController extends IActivityController.Stub { final IActivityManager mInterface; final PrintWriter mPw; @@ -1432,22 +1448,6 @@ final class ActivityManagerShellCommand extends ShellCommand { return 0; } - int runLenientBackgroundCheck(PrintWriter pw) throws RemoteException { - String arg = getNextArg(); - if (arg != null) { - boolean state = Boolean.valueOf(arg) || "1".equals(arg); - mInterface.setLenientBackgroundCheck(state); - } - synchronized (mInternal) { - if (mInternal.mLenientBackgroundCheck) { - pw.println("Lenient background check enabled"); - } else { - pw.println("Lenient background check disabled"); - } - } - return 0; - } - int getUidState(PrintWriter pw) throws RemoteException { mInternal.enforceCallingPermission(android.Manifest.permission.DUMP, "getUidState()"); @@ -2478,8 +2478,6 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" Enable association tracking."); pw.println(" untrack-associations"); pw.println(" Disable and clear association tracking."); - pw.println(" lenient-background-check [<true|false>]"); - pw.println(" Optionally controls lenient background check mode, returns current mode."); pw.println(" get-uid-state <UID>"); pw.println(" Gets the process state of an app given its <UID>."); pw.println(" attach-agent <PROCESS> <FILE>"); diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 0d7998071e99..1bdef433483a 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -1806,8 +1806,9 @@ final class ActivityStack extends ConfigurationContainer { final boolean keyguardShowing = mStackSupervisor.mKeyguardController.isKeyguardShowing(); final boolean keyguardLocked = mStackSupervisor.mKeyguardController.isKeyguardLocked(); final boolean showWhenLocked = r.hasShowWhenLockedWindows(); + final boolean dismissKeyguard = r.hasDismissKeyguardWindows(); if (shouldBeVisible) { - if (r.hasDismissKeyguardWindows() && mTopDismissingKeyguardActivity == null) { + if (dismissKeyguard && mTopDismissingKeyguardActivity == null) { mTopDismissingKeyguardActivity = r; } @@ -1819,8 +1820,10 @@ final class ActivityStack extends ConfigurationContainer { } if (keyguardShowing) { - // If keyguard is showing, nothing is visible. - return false; + // If keyguard is showing, nothing is visible, except if we are able to dismiss Keyguard + // right away. + return shouldBeVisible && mStackSupervisor.mKeyguardController + .canShowActivityWhileKeyguardShowing(dismissKeyguard); } else if (keyguardLocked) { // Show when locked windows above keyguard. diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 2a0c6d030b1d..691d6b9cd628 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -1204,6 +1204,14 @@ class ActivityStarter { // since the app transition will not be triggered through the resume channel. mWindowManager.executeAppTransition(); } else { + // If the target stack was not previously focusable (previous top running activity + // on that stack was not visible) then any prior calls to move the stack to the + // will not update the focused stack. If starting the new activity now allows the + // task stack to be focusable, then ensure that we now update the focused stack + // accordingly. + if (mTargetStack.isFocusable() && !mSupervisor.isFocusedStack(mTargetStack)) { + mTargetStack.moveToFront("startActivityUnchecked"); + } mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity, mOptions); } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 4e69162e84b9..8104a43cee09 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -36,7 +36,6 @@ import android.content.IntentSender; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -595,8 +594,12 @@ public final class BroadcastQueue { } if (!skip) { final int allowed = mService.checkAllowBackgroundLocked(filter.receiverList.uid, - filter.packageName, -1, true); - if (allowed == ActivityManager.APP_START_MODE_DISABLED) { + filter.packageName, -1, false); + if (false && allowed == ActivityManager.APP_START_MODE_DISABLED) { + // XXX should we really not allow this? It means that while we are + // keeping an ephemeral app cached, its registered receivers will stop + // receiving broadcasts after it goes idle... so if it comes back to + // the foreground, it won't know what the current state of those broadcasts is. Slog.w(TAG, "Background execution not allowed: receiving " + r.intent + " to " + filter.receiverList.app @@ -1155,7 +1158,7 @@ public final class BroadcastQueue { if (!skip) { final int allowed = mService.checkAllowBackgroundLocked( info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, -1, - false); + true); if (allowed != ActivityManager.APP_START_MODE_NORMAL) { // We won't allow this receiver to be launched if the app has been // completely disabled from launches, or it was not explicitly sent diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags index 722974b62f59..f618fc7c2a29 100644 --- a/services/core/java/com/android/server/am/EventLogTags.logtags +++ b/services/core/java/com/android/server/am/EventLogTags.logtags @@ -112,3 +112,5 @@ option java_package com.android.server.am # Report changing memory conditions (Values are ProcessStats.ADJ_MEM_FACTOR* constants) 30050 am_mem_factor (Current|1|5),(Previous|1|5) +# UserState has changed +30051 am_user_state_changed (id|1|5),(state|1|5) diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java index 98acc9ca4fe1..9d8c3835b33f 100644 --- a/services/core/java/com/android/server/am/KeyguardController.java +++ b/services/core/java/com/android/server/am/KeyguardController.java @@ -154,6 +154,14 @@ class KeyguardController { } } + /** + * @return True if we may show an activity while Keyguard is showing because we are in the + * process of dismissing it anyways, false otherwise. + */ + boolean canShowActivityWhileKeyguardShowing(boolean dismissKeyguard) { + return dismissKeyguard && canDismissKeyguard(); + } + private void visibilitiesUpdated() { final boolean lastOccluded = mOccluded; final ActivityRecord lastDismissingKeyguardActivity = mDismissingKeyguardActivity; @@ -215,7 +223,6 @@ class KeyguardController { && mWindowManager.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) { mWindowManager.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */); - mKeyguardGoingAway = true; mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); mWindowManager.executeAppTransition(); } diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java index d24c3a52adba..d1a15bdd3829 100644 --- a/services/core/java/com/android/server/am/UidRecord.java +++ b/services/core/java/com/android/server/am/UidRecord.java @@ -29,6 +29,7 @@ public final class UidRecord { int curProcState; int setProcState = ActivityManager.PROCESS_STATE_NONEXISTENT; long lastBackgroundTime; + boolean ephemeral; boolean idle; int numProcs; @@ -43,6 +44,7 @@ public final class UidRecord { int uid; int change; int processState; + boolean ephemeral; } ChangeItem pendingChange; @@ -64,6 +66,9 @@ public final class UidRecord { UserHandle.formatUid(sb, uid); sb.append(' '); sb.append(ProcessList.makeProcStateString(curProcState)); + if (ephemeral) { + sb.append(" ephemeral"); + } if (lastBackgroundTime > 0) { sb.append(" bg:"); TimeUtils.formatDuration(SystemClock.elapsedRealtime()-lastBackgroundTime, sb); diff --git a/services/core/java/com/android/server/am/UserState.java b/services/core/java/com/android/server/am/UserState.java index 48238b6f4cf3..42c31b1a6de4 100644 --- a/services/core/java/com/android/server/am/UserState.java +++ b/services/core/java/com/android/server/am/UserState.java @@ -70,6 +70,7 @@ public final class UserState { public boolean setState(int oldState, int newState) { if (state == oldState) { setState(newState); + EventLogTags.writeAmUserStateChanged(mHandle.getIdentifier(), newState); return true; } else { Slog.w(TAG, "Expected user " + mHandle.getIdentifier() + " in state " diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index 0beb227e5825..6d96a1015aa2 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -71,6 +71,7 @@ import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.server.connectivity.tethering.IControlsTethering; import com.android.server.connectivity.tethering.IPv6TetheringCoordinator; +import com.android.server.connectivity.tethering.IPv6TetheringInterfaceServices; import com.android.server.connectivity.tethering.TetherInterfaceStateMachine; import com.android.server.net.BaseNetworkObserver; @@ -1939,7 +1940,8 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering private void trackNewTetherableInterface(String iface, int interfaceType) { TetherState tetherState; tetherState = new TetherState(new TetherInterfaceStateMachine(iface, mLooper, - interfaceType, mNMService, mStatsService, this)); + interfaceType, mNMService, mStatsService, this, + new IPv6TetheringInterfaceServices(iface, mNMService))); mTetherStates.put(iface, tetherState); tetherState.mStateMachine.start(); } diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java index c2c1a8c5fef7..dec2f77b6455 100644 --- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java +++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java @@ -45,7 +45,7 @@ import java.util.Objects; /** * @hide */ -class IPv6TetheringInterfaceServices { +public class IPv6TetheringInterfaceServices { private static final String TAG = IPv6TetheringInterfaceServices.class.getSimpleName(); private static final IpPrefix LINK_LOCAL_PREFIX = new IpPrefix("fe80::/64"); private static final int RFC7421_IP_PREFIX_LENGTH = 64; @@ -59,7 +59,7 @@ class IPv6TetheringInterfaceServices { private RouterAdvertisementDaemon mRaDaemon; private RaParams mLastRaParams; - IPv6TetheringInterfaceServices(String ifname, INetworkManagementService nms) { + public IPv6TetheringInterfaceServices(String ifname, INetworkManagementService nms) { mIfName = ifname; mNMService = nms; } diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java index 6ca4e271ec55..37221a971ad1 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java @@ -94,14 +94,14 @@ public class TetherInterfaceStateMachine extends StateMachine { public TetherInterfaceStateMachine(String ifaceName, Looper looper, int interfaceType, INetworkManagementService nMService, INetworkStatsService statsService, - IControlsTethering tetherController) { + IControlsTethering tetherController, IPv6TetheringInterfaceServices ipv6Svc) { super(ifaceName, looper); mNMService = nMService; mStatsService = statsService; mTetherController = tetherController; mIfaceName = ifaceName; mInterfaceType = interfaceType; - mIPv6TetherSvc = new IPv6TetheringInterfaceServices(mIfaceName, mNMService); + mIPv6TetherSvc = ipv6Svc; mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR; mInitialState = new InitialState(); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 971989b21219..9c762cce7e0f 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -220,6 +220,11 @@ public final class DisplayManagerService extends SystemService { private final DisplayViewport mTempDefaultViewport = new DisplayViewport(); private final DisplayViewport mTempExternalTouchViewport = new DisplayViewport(); + // The default color mode for default displays. Overrides the usual + // Display.Display.COLOR_MODE_DEFAULT for displays with the + // DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY flag set. + private final int mDefaultDisplayDefaultColorMode; + // Temporary list of deferred work to perform when setting the display state. // Only used by requestDisplayState. The field is self-synchronized and only // intended for use inside of the requestGlobalDisplayStateInternal function. @@ -232,6 +237,8 @@ public final class DisplayManagerService extends SystemService { mUiHandler = UiThread.getHandler(); mDisplayAdapterListener = new DisplayAdapterListener(); mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false); + mDefaultDisplayDefaultColorMode = mContext.getResources().getInteger( + com.android.internal.R.integer.config_defaultDisplayDefaultColorMode); PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting(); @@ -703,6 +710,14 @@ public final class DisplayManagerService extends SystemService { } if (display != null && display.getPrimaryDisplayDeviceLocked() == device) { int colorMode = mPersistentDataStore.getColorMode(device); + if (colorMode == Display.COLOR_MODE_INVALID) { + if ((device.getDisplayDeviceInfoLocked().flags + & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) { + colorMode = mDefaultDisplayDefaultColorMode; + } else { + colorMode = Display.COLOR_MODE_DEFAULT; + } + } display.setRequestedColorModeLocked(colorMode); } scheduleTraversalLocked(false); @@ -1043,6 +1058,7 @@ public final class DisplayManagerService extends SystemService { pw.println(" mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId); pw.println(" mDefaultViewport=" + mDefaultViewport); pw.println(" mExternalTouchViewport=" + mExternalTouchViewport); + pw.println(" mDefaultDisplayDefaultColorMode=" + mDefaultDisplayDefaultColorMode); pw.println(" mSingleDisplayDemoMode=" + mSingleDisplayDemoMode); pw.println(" mWifiDisplayScanRequestCount=" + mWifiDisplayScanRequestCount); diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java index 5616fb97bad7..47701b99860a 100644 --- a/services/core/java/com/android/server/display/PersistentDataStore.java +++ b/services/core/java/com/android/server/display/PersistentDataStore.java @@ -183,11 +183,11 @@ final class PersistentDataStore { public int getColorMode(DisplayDevice device) { if (!device.hasStableUniqueId()) { - return Display.COLOR_MODE_DEFAULT; + return Display.COLOR_MODE_INVALID; } DisplayState state = getDisplayState(device.getUniqueId(), false); if (state == null) { - return Display.COLOR_MODE_DEFAULT; + return Display.COLOR_MODE_INVALID; } return state.getColorMode(); } diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index df6444704858..b8780992226e 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -426,7 +426,7 @@ public final class JobSchedulerService extends com.android.server.SystemService Slog.d(TAG, "Removing jobs for package " + pkgName + " in user " + userId); } - cancelJobsForUid(pkgUid, true); + cancelJobsForUid(pkgUid); } } catch (RemoteException|IllegalArgumentException e) { /* @@ -455,7 +455,7 @@ public final class JobSchedulerService extends com.android.server.SystemService if (DEBUG) { Slog.d(TAG, "Removing jobs for uid: " + uidRemoved); } - cancelJobsForUid(uidRemoved, true); + cancelJobsForUid(uidRemoved); } } else if (Intent.ACTION_USER_REMOVED.equals(action)) { final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); @@ -509,15 +509,20 @@ public final class JobSchedulerService extends com.android.server.SystemService updateUidState(uid, procState); } - @Override public void onUidGone(int uid) throws RemoteException { + @Override public void onUidGone(int uid, boolean disabled) throws RemoteException { updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); + if (disabled) { + cancelJobsForUid(uid); + } } @Override public void onUidActive(int uid) throws RemoteException { } - @Override public void onUidIdle(int uid) throws RemoteException { - cancelJobsForUid(uid, false); + @Override public void onUidIdle(int uid, boolean disabled) throws RemoteException { + if (disabled) { + cancelJobsForUid(uid); + } } }; @@ -646,26 +651,15 @@ public final class JobSchedulerService extends com.android.server.SystemService * This will remove the job from the master list, and cancel the job if it was staged for * execution or being executed. * @param uid Uid to check against for removal of a job. - * @param forceAll If true, all jobs for the uid will be canceled; if false, only those - * whose apps are stopped. + * */ - public void cancelJobsForUid(int uid, boolean forceAll) { + public void cancelJobsForUid(int uid) { List<JobStatus> jobsForUid; synchronized (mLock) { jobsForUid = mJobs.getJobsByUid(uid); } for (int i=0; i<jobsForUid.size(); i++) { JobStatus toRemove = jobsForUid.get(i); - if (!forceAll) { - String packageName = toRemove.getServiceComponent().getPackageName(); - try { - if (ActivityManagerNative.getDefault().getAppStartMode(uid, packageName) - != ActivityManager.APP_START_MODE_DISABLED) { - continue; - } - } catch (RemoteException e) { - } - } cancelJobImpl(toRemove, null); } } @@ -1698,7 +1692,7 @@ public final class JobSchedulerService extends com.android.server.SystemService long ident = Binder.clearCallingIdentity(); try { - JobSchedulerService.this.cancelJobsForUid(uid, true); + JobSchedulerService.this.cancelJobsForUid(uid); } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java index 5eb06ed20b42..b44087c07c16 100644 --- a/services/core/java/com/android/server/location/LocationProviderProxy.java +++ b/services/core/java/com/android/server/location/LocationProviderProxy.java @@ -17,6 +17,7 @@ package com.android.server.location; import java.io.FileDescriptor; +import java.io.IOException; import java.io.PrintWriter; import android.content.Context; @@ -30,6 +31,7 @@ import android.util.Log; import com.android.internal.location.ProviderProperties; import com.android.internal.location.ILocationProvider; import com.android.internal.location.ProviderRequest; +import com.android.internal.os.TransferPipe; import com.android.server.LocationManagerService; import com.android.server.ServiceWatcher; @@ -230,14 +232,9 @@ public class LocationProviderProxy implements LocationProviderInterface { pw.flush(); try { - service.asBinder().dump(fd, args); - } catch (RemoteException e) { - pw.println("service down (RemoteException)"); - Log.w(TAG, e); - } catch (Exception e) { - pw.println("service down (Exception)"); - // never let remote service crash system server - Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e); + TransferPipe.dumpAsync(service.asBinder(), fd, args); + } catch (IOException | RemoteException e) { + pw.println("Failed to dump location provider: " + e); } } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index fd99f57af9b8..c1506b958e83 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -689,7 +689,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - @Override public void onUidGone(int uid) throws RemoteException { + @Override public void onUidGone(int uid, boolean disabled) throws RemoteException { synchronized (mUidRulesFirstLock) { removeUidStateUL(uid); } @@ -698,7 +698,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @Override public void onUidActive(int uid) throws RemoteException { } - @Override public void onUidIdle(int uid) throws RemoteException { + @Override public void onUidIdle(int uid, boolean disabled) throws RemoteException { } }; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f3ae2bce997e..c7f4d6b5e203 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -16,25 +16,24 @@ package com.android.server.notification; -import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_NONE; -import static android.service.notification.NotificationRankerService.REASON_APP_CANCEL; -import static android.service.notification.NotificationRankerService.REASON_APP_CANCEL_ALL; -import static android.service.notification.NotificationRankerService.REASON_CHANNEL_BANNED; -import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CANCEL; -import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CANCEL_ALL; -import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CLICK; -import static android.service.notification.NotificationRankerService.REASON_DELEGATE_ERROR; -import static android.service.notification.NotificationRankerService.REASON_GROUP_SUMMARY_CANCELED; -import static android.service.notification.NotificationRankerService.REASON_LISTENER_CANCEL; -import static android.service.notification.NotificationRankerService.REASON_LISTENER_CANCEL_ALL; -import static android.service.notification.NotificationRankerService.REASON_PACKAGE_BANNED; -import static android.service.notification.NotificationRankerService.REASON_PACKAGE_CHANGED; -import static android.service.notification.NotificationRankerService.REASON_PACKAGE_SUSPENDED; -import static android.service.notification.NotificationRankerService.REASON_PROFILE_TURNED_OFF; -import static android.service.notification.NotificationRankerService.REASON_SNOOZED; -import static android.service.notification.NotificationRankerService.REASON_UNAUTOBUNDLED; -import static android.service.notification.NotificationRankerService.REASON_USER_STOPPED; +import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL; +import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL; +import static android.service.notification.NotificationListenerService.REASON_CHANNEL_BANNED; +import static android.service.notification.NotificationListenerService.REASON_DELEGATE_CANCEL; +import static android.service.notification.NotificationListenerService.REASON_DELEGATE_CANCEL_ALL; +import static android.service.notification.NotificationListenerService.REASON_DELEGATE_CLICK; +import static android.service.notification.NotificationListenerService.REASON_DELEGATE_ERROR; +import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED; +import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL; +import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL_ALL; +import static android.service.notification.NotificationListenerService.REASON_PACKAGE_BANNED; +import static android.service.notification.NotificationListenerService.REASON_PACKAGE_CHANGED; +import static android.service.notification.NotificationListenerService.REASON_PACKAGE_SUSPENDED; +import static android.service.notification.NotificationListenerService.REASON_PROFILE_TURNED_OFF; +import static android.service.notification.NotificationListenerService.REASON_SNOOZED; +import static android.service.notification.NotificationListenerService.REASON_UNAUTOBUNDLED; +import static android.service.notification.NotificationListenerService.REASON_USER_STOPPED; import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS; import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS; import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS; @@ -110,7 +109,7 @@ import android.service.notification.Condition; import android.service.notification.IConditionProvider; import android.service.notification.INotificationListener; import android.service.notification.IStatusBarNotificationHolder; -import android.service.notification.NotificationRankerService; +import android.service.notification.NotificationAssistantService; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationRankingUpdate; import android.service.notification.StatusBarNotification; @@ -125,7 +124,6 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.Xml; -import android.view.WindowManager; import android.view.WindowManagerInternal; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; @@ -169,12 +167,10 @@ import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Set; import java.util.concurrent.TimeUnit; /** {@hide} */ @@ -229,7 +225,6 @@ public class NotificationManagerService extends SystemService { /** notification_enqueue status value for an ignored notification. */ private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2; private static final long MIN_PACKAGE_OVERRATE_LOG_INTERVAL = 5000; // milliseconds - private String mRankerServicePackageName; private IActivityManager mAm; AudioManager mAudioManager; @@ -301,7 +296,7 @@ public class NotificationManagerService extends SystemService { private final UserProfiles mUserProfiles = new UserProfiles(); private NotificationListeners mListeners; - private NotificationRankers mRankerServices; + private NotificationAssistants mNotificationAssistants; private ConditionProviders mConditionProviders; private NotificationUsageStats mUsageStats; @@ -758,7 +753,7 @@ public class NotificationManagerService extends SystemService { } } mListeners.onPackagesChanged(removingPackage, pkgList); - mRankerServices.onPackagesChanged(removingPackage, pkgList); + mNotificationAssistants.onPackagesChanged(removingPackage, pkgList); mConditionProviders.onPackagesChanged(removingPackage, pkgList); mRankingHelper.onPackagesChanged(removingPackage, changeUserId, pkgList); } @@ -808,7 +803,7 @@ public class NotificationManagerService extends SystemService { // Refresh managed services mConditionProviders.onUserSwitched(user); mListeners.onUserSwitched(user); - mRankerServices.onUserSwitched(user); + mNotificationAssistants.onUserSwitched(user); mZenModeHelper.onUserSwitched(user); } else if (action.equals(Intent.ACTION_USER_ADDED)) { mUserProfiles.updateCache(context); @@ -819,7 +814,7 @@ public class NotificationManagerService extends SystemService { final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); mConditionProviders.onUserUnlocked(user); mListeners.onUserUnlocked(user); - mRankerServices.onUserUnlocked(user); + mNotificationAssistants.onUserUnlocked(user); mZenModeHelper.onUserUnlocked(user); } } @@ -962,10 +957,6 @@ public class NotificationManagerService extends SystemService { mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE); mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class); - // This is the package that contains the AOSP framework update. - mRankerServicePackageName = getContext().getPackageManager() - .getServicesSystemSharedLibraryPackageName(); - mHandler = new WorkerHandler(); mRankingThread.start(); String[] extractorNames; @@ -1059,10 +1050,8 @@ public class NotificationManagerService extends SystemService { // This is a MangedServices object that keeps track of the listeners. mListeners = new NotificationListeners(); - // This is a MangedServices object that keeps track of the ranker. - mRankerServices = new NotificationRankers(); - // Find the updatable ranker and register it. - mRankerServices.registerRanker(); + // This is a MangedServices object that keeps track of the assistant. + mNotificationAssistants = new NotificationAssistants(); mStatusBar = getLocalService(StatusBarManagerInternal.class); if (mStatusBar != null) { @@ -1209,7 +1198,7 @@ public class NotificationManagerService extends SystemService { // bind to listener services. mSettingsObserver.observe(); mListeners.onBootPhaseAppsCanStart(); - mRankerServices.onBootPhaseAppsCanStart(); + mNotificationAssistants.onBootPhaseAppsCanStart(); mConditionProviders.onBootPhaseAppsCanStart(); } } @@ -1682,10 +1671,10 @@ public class NotificationManagerService extends SystemService { final StatusBarNotification sbnOut = new StatusBarNotification( sbn.getPackageName(), sbn.getOpPkg(), + sbn.getNotificationChannel(), sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(), - 0, // hide score from apps sbn.getNotification().clone(), - sbn.getUser(), sbn.getPostTime()); + sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime()); list.add(sbnOut); } } @@ -1791,8 +1780,8 @@ public class NotificationManagerService extends SystemService { long identity = Binder.clearCallingIdentity(); try { ManagedServices manager = - mRankerServices.isComponentEnabledForCurrentProfiles(component) - ? mRankerServices + mNotificationAssistants.isComponentEnabledForCurrentProfiles(component) + ? mNotificationAssistants : mListeners; manager.setComponentState(component, true); } finally { @@ -2358,12 +2347,12 @@ public class NotificationManagerService extends SystemService { } @Override - public void applyAdjustmentFromRankerService(INotificationListener token, + public void applyAdjustmentFromAssistantService(INotificationListener token, Adjustment adjustment) throws RemoteException { final long identity = Binder.clearCallingIdentity(); try { synchronized (mNotificationList) { - mRankerServices.checkServiceTokenLocked(token); + mNotificationAssistants.checkServiceTokenLocked(token); applyAdjustmentLocked(adjustment); } mRankingHandler.requestSort(); @@ -2373,13 +2362,13 @@ public class NotificationManagerService extends SystemService { } @Override - public void applyAdjustmentsFromRankerService(INotificationListener token, + public void applyAdjustmentsFromAssistantService(INotificationListener token, List<Adjustment> adjustments) throws RemoteException { final long identity = Binder.clearCallingIdentity(); try { synchronized (mNotificationList) { - mRankerServices.checkServiceTokenLocked(token); + mNotificationAssistants.checkServiceTokenLocked(token); for (Adjustment adjustment : adjustments) { applyAdjustmentLocked(adjustment); } @@ -2478,15 +2467,14 @@ public class NotificationManagerService extends SystemService { } final StatusBarNotification summarySbn = new StatusBarNotification(adjustedSbn.getPackageName(), - adjustedSbn.getOpPkg(), Integer.MAX_VALUE, + adjustedSbn.getOpPkg(), + adjustedSbn.getNotificationChannel(), + Integer.MAX_VALUE, GroupHelper.AUTOGROUP_KEY, adjustedSbn.getUid(), adjustedSbn.getInitialPid(), summaryNotification, adjustedSbn.getUser(), GroupHelper.AUTOGROUP_KEY, System.currentTimeMillis()); - summaryRecord = new NotificationRecord(getContext(), summarySbn, - mRankingHelper.getNotificationChannel(adjustedSbn.getPackageName(), - adjustedSbn.getUid(), - adjustedSbn.getNotification().getNotificationChannel())); + summaryRecord = new NotificationRecord(getContext(), summarySbn); summaries.put(pkg, summarySbn.getKey()); } } @@ -2632,9 +2620,8 @@ public class NotificationManagerService extends SystemService { } } pw.println(')'); - pw.println("\n mRankerServicePackageName: " + mRankerServicePackageName); - pw.println("\n Notification ranker services:"); - mRankerServices.dump(pw, filter); + pw.println("\n Notification assistant services:"); + mNotificationAssistants.dump(pw, filter); } if (!zenOnly) { @@ -2734,9 +2721,11 @@ public class NotificationManagerService extends SystemService { throw new IllegalArgumentException("null not allowed: pkg=" + pkg + " id=" + id + " notification=" + notification); } + final NotificationChannel channel = mRankingHelper.getNotificationChannelWithFallback(pkg, + callingUid, notification.getNotificationChannel()); final StatusBarNotification n = new StatusBarNotification( - pkg, opPkg, id, tag, callingUid, callingPid, 0, notification, - user); + pkg, opPkg, channel, id, tag, callingUid, callingPid, notification, + user, null, System.currentTimeMillis()); // Limit the number of notifications that any given package except the android // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks. @@ -2799,9 +2788,7 @@ public class NotificationManagerService extends SystemService { Notification.PRIORITY_MAX); // setup local book-keeping - final NotificationRecord r = new NotificationRecord(getContext(), n, - mRankingHelper.getNotificationChannelWithFallback(pkg, callingUid, - n.getNotification().getNotificationChannel())); + final NotificationRecord r = new NotificationRecord(getContext(), n); mHandler.post(new EnqueueNotificationRunnable(userId, r)); idOut[0] = id; @@ -2885,9 +2872,9 @@ public class NotificationManagerService extends SystemService { } } - // tell the ranker service about the notification - if (mRankerServices.isEnabled()) { - mRankerServices.onNotificationEnqueued(r); + // tell the assistant service about the notification + if (mNotificationAssistants.isEnabled()) { + mNotificationAssistants.onNotificationEnqueued(r); // TODO delay the code below here for 100ms or until there is an answer } @@ -2930,7 +2917,8 @@ public class NotificationManagerService extends SystemService { } else { Slog.e(TAG, "Not posting notification without small icon: " + notification); if (old != null && !old.isCanceled) { - mListeners.notifyRemovedLocked(n); + mListeners.notifyRemovedLocked(n, + NotificationListenerService.REASON_DELEGATE_ERROR); mHandler.post(new Runnable() { @Override public void run() { @@ -3542,7 +3530,7 @@ public class NotificationManagerService extends SystemService { // status bar if (r.getNotification().getSmallIcon() != null) { r.isCanceled = true; - mListeners.notifyRemovedLocked(r.sbn); + mListeners.notifyRemovedLocked(r.sbn, reason); mHandler.post(new Runnable() { @Override public void run() { @@ -4075,19 +4063,19 @@ public class NotificationManagerService extends SystemService { } } - public class NotificationRankers extends ManagedServices { + public class NotificationAssistants extends ManagedServices { - public NotificationRankers() { + public NotificationAssistants() { super(getContext(), mHandler, mNotificationList, mUserProfiles); } @Override protected Config getConfig() { Config c = new Config(); - c.caption = "notification ranker service"; - c.serviceInterface = NotificationRankerService.SERVICE_INTERFACE; - c.secureSettingName = null; - c.bindPermission = Manifest.permission.BIND_NOTIFICATION_RANKER_SERVICE; + c.caption = "notification assistant service"; + c.serviceInterface = NotificationAssistantService.SERVICE_INTERFACE; + c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT; + c.bindPermission = Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE; c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS; c.clientLabel = R.string.notification_ranker_binding_label; return c; @@ -4117,10 +4105,10 @@ public class NotificationManagerService extends SystemService { final StatusBarNotification sbn = r.sbn; TrimCache trimCache = new TrimCache(sbn); - // mServices is the list inside ManagedServices of all the rankers, + // mServices is the list inside ManagedServices of all the assistants, // There should be only one, but it's a list, so while we enforce // singularity elsewhere, we keep it general here, to avoid surprises. - for (final ManagedServiceInfo info : NotificationRankers.this.mServices) { + for (final ManagedServiceInfo info : NotificationAssistants.this.mServices) { boolean sbnVisible = isVisibleToListener(sbn, info); if (!sbnVisible) { continue; @@ -4140,68 +4128,18 @@ public class NotificationManagerService extends SystemService { private void notifyEnqueued(final ManagedServiceInfo info, final StatusBarNotification sbn, int importance, boolean fromUser) { - final INotificationListener ranker = (INotificationListener) info.service; + final INotificationListener assistant = (INotificationListener) info.service; StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn); try { - ranker.onNotificationEnqueued(sbnHolder, importance, fromUser); + assistant.onNotificationEnqueued(sbnHolder, importance, fromUser); } catch (RemoteException ex) { - Log.e(TAG, "unable to notify ranker (enqueued): " + ranker, ex); + Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex); } } public boolean isEnabled() { return !mServices.isEmpty(); } - - @Override - public void onUserSwitched(int user) { - synchronized (mNotificationList) { - int i = mServices.size()-1; - while (i --> 0) { - final ManagedServiceInfo info = mServices.get(i); - unregisterService(info.service, info.userid); - } - } - registerRanker(); - } - - @Override - public void onPackagesChanged(boolean removingPackage, String[] pkgList) { - if (DEBUG) Slog.d(TAG, "onPackagesChanged removingPackage=" + removingPackage - + " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList))); - if (mRankerServicePackageName == null) { - return; - } - - if (pkgList != null && (pkgList.length > 0) && !removingPackage) { - for (String pkgName : pkgList) { - if (mRankerServicePackageName.equals(pkgName)) { - registerRanker(); - } - } - } - } - - protected void registerRanker() { - // Find the updatable ranker and register it. - if (mRankerServicePackageName == null) { - Slog.w(TAG, "could not start ranker service: no package specified!"); - return; - } - Set<ComponentName> rankerComponents = queryPackageForServices( - mRankerServicePackageName, UserHandle.USER_SYSTEM); - Iterator<ComponentName> iterator = rankerComponents.iterator(); - if (iterator.hasNext()) { - ComponentName rankerComponent = iterator.next(); - if (iterator.hasNext()) { - Slog.e(TAG, "found multiple ranker services:" + rankerComponents); - } else { - registerSystemService(rankerComponent, UserHandle.USER_SYSTEM); - } - } else { - Slog.w(TAG, "could not start ranker service: none found"); - } - } } public class NotificationListeners extends ManagedServices { @@ -4295,7 +4233,7 @@ public class NotificationManagerService extends SystemService { mHandler.post(new Runnable() { @Override public void run() { - notifyRemoved(info, oldSbnLightClone, update); + notifyRemoved(info, oldSbnLightClone, update, REASON_USER_STOPPED); } }); continue; @@ -4314,7 +4252,7 @@ public class NotificationManagerService extends SystemService { /** * asynchronously notify all listeners about a removed notification */ - public void notifyRemovedLocked(StatusBarNotification sbn) { + public void notifyRemovedLocked(StatusBarNotification sbn, int reason) { // make a copy in case changes are made to the underlying Notification object // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the // notification @@ -4327,7 +4265,7 @@ public class NotificationManagerService extends SystemService { mHandler.post(new Runnable() { @Override public void run() { - notifyRemoved(info, sbnLight, update); + notifyRemoved(info, sbnLight, update, reason); } }); } @@ -4391,14 +4329,14 @@ public class NotificationManagerService extends SystemService { } private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn, - NotificationRankingUpdate rankingUpdate) { + NotificationRankingUpdate rankingUpdate, int reason) { if (!info.enabledAndUserMatches(sbn.getUserId())) { return; } final INotificationListener listener = (INotificationListener) info.service; StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn); try { - listener.onNotificationRemoved(sbnHolder, rankingUpdate); + listener.onNotificationRemoved(sbnHolder, rankingUpdate, reason); } catch (RemoteException ex) { Log.e(TAG, "unable to notify listener (removed): " + listener, ex); } diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index a1256db61cba..965257c873e9 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -101,11 +101,8 @@ public final class NotificationRecord { private String mUserExplanation; private String mPeopleExplanation; - private NotificationChannel mNotificationChannel; - @VisibleForTesting - public NotificationRecord(Context context, StatusBarNotification sbn, - NotificationChannel channel) + public NotificationRecord(Context context, StatusBarNotification sbn) { this.sbn = sbn; mOriginalFlags = sbn.getNotification().flags; @@ -114,7 +111,6 @@ public final class NotificationRecord { mUpdateTimeMs = mCreationTimeMs; mContext = context; stats = new NotificationUsageStats.SingleNotificationStats(); - mNotificationChannel = channel; mImportance = defaultImportance(); } @@ -148,8 +144,8 @@ public final class NotificationRecord { || (n.defaults & Notification.DEFAULT_VIBRATE) != 0 || n.sound != null || n.vibrate != null - || mNotificationChannel.shouldVibrate() - || mNotificationChannel.getRingtone() != null; + || sbn.getNotificationChannel().shouldVibrate() + || sbn.getNotificationChannel().getRingtone() != null; stats.isNoisy = isNoisy; if (!isNoisy && importance > IMPORTANCE_LOW) { @@ -287,7 +283,7 @@ public final class NotificationRecord { pw.println(prefix + " mVisibleSinceMs=" + mVisibleSinceMs); pw.println(prefix + " mUpdateTimeMs=" + mUpdateTimeMs); pw.println(prefix + " mSuppressedVisualEffects= " + mSuppressedVisualEffects); - pw.println(prefix + " mNotificationChannel= " + mNotificationChannel); + pw.println(prefix + " notificationChannel= " + notification.getNotificationChannel()); } @@ -535,6 +531,6 @@ public final class NotificationRecord { } public NotificationChannel getChannel() { - return mNotificationChannel; + return sbn.getNotificationChannel(); } } diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java index 97789196b5a1..ecc03d7c5fbb 100644 --- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java +++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java @@ -32,7 +32,10 @@ import android.os.SystemClock; import android.os.UserHandle; import android.util.TimedRemoteCaller; +import com.android.internal.os.TransferPipe; + import java.io.FileDescriptor; +import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -102,13 +105,11 @@ final class EphemeralResolverConnection { .append((mRemoteInstance != null) ? "true" : "false").println(); pw.flush(); - try { - getRemoteInstanceLazy().asBinder().dump(fd, new String[] { prefix }); - } catch (TimeoutException te) { - /* ignore */ - } catch (RemoteException re) { - /* ignore */ + TransferPipe.dumpAsync(getRemoteInstanceLazy().asBinder(), fd, + new String[] { prefix }); + } catch (IOException | TimeoutException | RemoteException e) { + pw.println("Failed to dump remote instance: " + e); } } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index cf3aaba63f96..79ef4861ab65 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1083,8 +1083,8 @@ public class PackageManagerService extends IPackageManager.Stub { class DefaultContainerConnection implements ServiceConnection { public void onServiceConnected(ComponentName name, IBinder service) { if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected"); - IMediaContainerService imcs = - IMediaContainerService.Stub.asInterface(service); + final IMediaContainerService imcs = IMediaContainerService.Stub + .asInterface(Binder.allowBlocking(service)); mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs)); } @@ -16627,7 +16627,8 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public void onServiceConnected(ComponentName name, IBinder service) { synchronized (this) { - mContainerService = IMediaContainerService.Stub.asInterface(service); + mContainerService = IMediaContainerService.Stub + .asInterface(Binder.allowBlocking(service)); notifyAll(); } } @@ -21256,6 +21257,14 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } @Override + public boolean isPackageEphemeral(int userId, String packageName) { + synchronized (mPackages) { + PackageParser.Package p = mPackages.get(packageName); + return p != null ? p.applicationInfo.isEphemeralApp() : false; + } + } + + @Override public boolean wasPackageEverLaunched(String packageName, int userId) { synchronized (mPackages) { return mSettings.wasPackageEverLaunchedLPr(packageName, userId); diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index ad31a321ccff..5a0bee1e049b 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -467,7 +467,7 @@ public class ShortcutService extends IShortcutService.Stub { } @Override - public void onUidGone(int uid) throws RemoteException { + public void onUidGone(int uid, boolean disabled) throws RemoteException { handleOnUidStateChanged(uid, ActivityManager.PROCESS_STATE_NONEXISTENT); } @@ -476,7 +476,7 @@ public class ShortcutService extends IShortcutService.Stub { } @Override - public void onUidIdle(int uid) throws RemoteException { + public void onUidIdle(int uid, boolean disabled) throws RemoteException { } }; diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index b7067d2da261..396c958542c8 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -226,6 +226,7 @@ import com.android.server.GestureLauncherService; import com.android.server.LocalServices; import com.android.server.policy.keyguard.KeyguardServiceDelegate; import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener; +import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.wm.AppTransition; @@ -6956,7 +6957,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override public void systemReady() { - mKeyguardDelegate = new KeyguardServiceDelegate(mContext); + mKeyguardDelegate = new KeyguardServiceDelegate(mContext, + new StateCallback() { + @Override + public void onTrustedChanged() { + mWindowManagerFuncs.notifyKeyguardTrustedChanged(); + } + }); mKeyguardDelegate.onSystemReady(); readCameraLensCoverState(); diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java index 28f36f779e89..ca641fbf9234 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java @@ -1,7 +1,5 @@ package com.android.server.policy.keyguard; -import android.app.ActivityManager; -import android.app.ActivityManagerInternal; import android.app.ActivityManagerNative; import android.content.ComponentName; import android.content.Context; @@ -15,13 +13,11 @@ import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; import android.util.Slog; -import android.view.View; import android.view.WindowManagerPolicy.OnKeyguardExitResult; import com.android.internal.policy.IKeyguardDrawnCallback; import com.android.internal.policy.IKeyguardExitCallback; import com.android.internal.policy.IKeyguardService; -import com.android.server.LocalServices; import com.android.server.UiThread; import java.io.PrintWriter; @@ -47,6 +43,8 @@ public class KeyguardServiceDelegate { private final Context mContext; private final Handler mHandler; private final KeyguardState mKeyguardState = new KeyguardState(); + private final KeyguardStateMonitor.StateCallback mCallback; + private DrawnListener mDrawnListenerWhenConnect; private static final class KeyguardState { @@ -119,9 +117,10 @@ public class KeyguardServiceDelegate { } }; - public KeyguardServiceDelegate(Context context) { + public KeyguardServiceDelegate(Context context, KeyguardStateMonitor.StateCallback callback) { mContext = context; mHandler = UiThread.getHandler(); + mCallback = callback; } public void bindService(Context context) { @@ -155,7 +154,7 @@ public class KeyguardServiceDelegate { public void onServiceConnected(ComponentName name, IBinder service) { if (DEBUG) Log.v(TAG, "*** Keyguard connected (yay!)"); mKeyguardService = new KeyguardServiceWrapper(mContext, - IKeyguardService.Stub.asInterface(service)); + IKeyguardService.Stub.asInterface(service), mCallback); if (mKeyguardState.systemIsReady) { // If the system is ready, it means keyguard crashed and restarted. mKeyguardService.onSystemReady(); diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java index b457f8d8ed82..c4a0dd364d76 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java @@ -39,9 +39,10 @@ public class KeyguardServiceWrapper implements IKeyguardService { private IKeyguardService mService; private String TAG = "KeyguardServiceWrapper"; - public KeyguardServiceWrapper(Context context, IKeyguardService service) { + public KeyguardServiceWrapper(Context context, IKeyguardService service, + KeyguardStateMonitor.StateCallback callback) { mService = service; - mKeyguardStateMonitor = new KeyguardStateMonitor(context, service); + mKeyguardStateMonitor = new KeyguardStateMonitor(context, service, callback); } @Override // Binder interface diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java index f19f0aa4f978..941cd4441e23 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java @@ -49,10 +49,12 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub { private int mCurrentUserId; private final LockPatternUtils mLockPatternUtils; + private final StateCallback mCallback; - public KeyguardStateMonitor(Context context, IKeyguardService service) { + public KeyguardStateMonitor(Context context, IKeyguardService service, StateCallback callback) { mLockPatternUtils = new LockPatternUtils(context); mCurrentUserId = ActivityManager.getCurrentUser(); + mCallback = callback; try { service.addStateMonitorCallback(this); @@ -107,6 +109,7 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub { @Override // Binder interface public void onTrustedChanged(boolean trusted) { mTrusted = trusted; + mCallback.onTrustedChanged(); } @Override // Binder interface @@ -114,6 +117,10 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub { mHasLockscreenWallpaper = hasLockscreenWallpaper; } + public interface StateCallback { + void onTrustedChanged(); + } + public void dump(String prefix, PrintWriter pw) { pw.println(prefix + TAG); prefix += " "; diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 622eece52ca1..05e6f96b1b0c 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -66,6 +66,7 @@ import android.view.animation.Animation; import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.function.Consumer; class AppTokenList extends ArrayList<AppWindowToken> { } @@ -1269,6 +1270,22 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } @Override + void forAllWindows(Consumer<WindowState> callback, boolean traverseTopToBottom) { + // For legacy reasons we process the TaskStack.mExitingAppTokens first in DisplayContent + // before the non-exiting app tokens. So, we skip the exiting app tokens here. + // TODO: Investigate if we need to continue to do this or if we can just process them + // in-order. + if (mIsExiting && !waitingForReplacement()) { + return; + } + forAllWindowsUnchecked(callback, traverseTopToBottom); + } + + void forAllWindowsUnchecked(Consumer<WindowState> callback, boolean traverseTopToBottom) { + super.forAllWindows(callback, traverseTopToBottom); + } + + @Override AppWindowToken asAppWindowToken() { // I am an app window token! return this; diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java index 5bfece414446..cf5cecdaf797 100644 --- a/services/core/java/com/android/server/wm/BoundsAnimationController.java +++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java @@ -96,8 +96,8 @@ public class BoundsAnimationController { private final class BoundsAnimator extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener { private final AnimateBoundsUser mTarget; - private final Rect mFrom; - private final Rect mTo; + private final Rect mFrom = new Rect(); + private final Rect mTo = new Rect(); private final Rect mTmpRect = new Rect(); private final Rect mTmpTaskBounds = new Rect(); private final boolean mMoveToFullScreen; @@ -117,8 +117,8 @@ public class BoundsAnimationController { boolean moveToFullScreen, boolean replacement) { super(); mTarget = target; - mFrom = from; - mTo = to; + mFrom.set(from); + mTo.set(to); mMoveToFullScreen = moveToFullScreen; mReplacement = replacement; addUpdateListener(this); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index ec4cdf2b0f3d..6a625f45f2b7 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -140,6 +140,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.function.Consumer; /** * Utility class for keeping track of the WindowStates and other pertinent contents of a @@ -1121,8 +1122,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo for (int i = 0; i < windowCount; i++) { final WindowState window = mWindows.get(i); if (window.mAttrs.type == TYPE_TOAST && window.mOwnerUid == uid - && !window.mPermanentlyHidden && !window.mAnimatingExit - && !window.mRemoveOnExit) { + && !window.mPermanentlyHidden && !window.mWindowRemovalAllowed) { return false; } } @@ -3243,6 +3243,42 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } @Override + void forAllWindows(Consumer<WindowState> callback, boolean traverseTopToBottom) { + if (traverseTopToBottom) { + super.forAllWindows(callback, traverseTopToBottom); + forAllExitingAppTokenWindows(callback, traverseTopToBottom); + } else { + forAllExitingAppTokenWindows(callback, traverseTopToBottom); + super.forAllWindows(callback, traverseTopToBottom); + } + } + + private void forAllExitingAppTokenWindows(Consumer<WindowState> callback, + boolean traverseTopToBottom) { + // For legacy reasons we process the TaskStack.mExitingAppTokens first here before the + // app tokens. + // TODO: Investigate if we need to continue to do this or if we can just process them + // in-order. + if (traverseTopToBottom) { + for (int i = mChildren.size() - 1; i >= 0; --i) { + final AppTokenList appTokens = mChildren.get(i).mExitingAppTokens; + for (int j = appTokens.size() - 1; j >= 0; --j) { + appTokens.get(j).forAllWindowsUnchecked(callback, traverseTopToBottom); + } + } + } else { + final int count = mChildren.size(); + for (int i = 0; i < count; ++i) { + final AppTokenList appTokens = mChildren.get(i).mExitingAppTokens; + final int appTokensCount = appTokens.size(); + for (int j = 0; j < appTokensCount; j++) { + appTokens.get(j).forAllWindowsUnchecked(callback, traverseTopToBottom); + } + } + } + } + + @Override int getOrientation() { if (mService.isStackVisibleLocked(DOCKED_STACK_ID) || mService.isStackVisibleLocked(FREEFORM_WORKSPACE_STACK_ID)) { diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java index 1ccf7229cc87..c711b39aabaf 100644 --- a/services/core/java/com/android/server/wm/PinnedStackController.java +++ b/services/core/java/com/android/server/wm/PinnedStackController.java @@ -26,6 +26,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.animation.ValueAnimator; import android.content.res.Resources; import android.graphics.Point; +import android.graphics.PointF; import android.graphics.Rect; import android.os.Handler; import android.os.IBinder; @@ -43,6 +44,7 @@ import android.view.IPinnedStackListener; import com.android.internal.os.BackgroundThread; import com.android.internal.policy.PipMotionHelper; import com.android.internal.policy.PipSnapAlgorithm; +import com.android.server.UiThread; import java.io.PrintWriter; @@ -55,7 +57,7 @@ class PinnedStackController { private final WindowManagerService mService; private final DisplayContent mDisplayContent; - private final Handler mHandler = new Handler(); + private final Handler mHandler = UiThread.getHandler(); private IPinnedStackListener mPinnedStackListener; private final PinnedStackListenerDeathHandler mPinnedStackListenerDeathHandler = @@ -67,6 +69,7 @@ class PinnedStackController { // States that affect how the PIP can be manipulated private boolean mInInteractiveMode; + private boolean mIsMinimized; private boolean mIsImeShowing; private int mImeHeight; private ValueAnimator mBoundsAnimator = null; @@ -101,6 +104,13 @@ class PinnedStackController { } @Override + public void setIsMinimized(final boolean isMinimized) { + mHandler.post(() -> { + mIsMinimized = isMinimized; + }); + } + + @Override public void setSnapToEdge(final boolean snapToEdge) { mHandler.post(() -> { mSnapAlgorithm.setSnapToEdge(snapToEdge); @@ -167,6 +177,25 @@ class PinnedStackController { } /** + * Returns the current bounds (or the default bounds if there are no current bounds) with the + * specified aspect ratio. + */ + Rect getAspectRatioBounds(Rect stackBounds, float aspectRatio) { + // Save the snap fraction, calculate the aspect ratio based on the current bounds + final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds, + getMovementBounds(stackBounds)); + final float radius = PointF.length(stackBounds.width(), stackBounds.height()); + final int height = (int) Math.round(Math.sqrt((radius * radius) / + (aspectRatio * aspectRatio + 1))); + final int width = Math.round(height * aspectRatio); + final int left = (int) (stackBounds.centerX() - width / 2f); + final int top = (int) (stackBounds.centerY() - height / 2f); + stackBounds.set(left, top, left + width, top + height); + mSnapAlgorithm.applySnapFraction(stackBounds, getMovementBounds(stackBounds), snapFraction); + return stackBounds; + } + + /** * @return the default bounds to show the PIP when there is no active PIP. */ Rect getDefaultBounds() { @@ -314,5 +343,6 @@ class PinnedStackController { pw.println(); pw.println(prefix + " mIsImeShowing=" + mIsImeShowing); pw.println(prefix + " mInInteractiveMode=" + mInInteractiveMode); + pw.println(prefix + " mIsMinimized=" + mIsMinimized); } } diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index a0270c605fed..203ba72ca144 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -132,6 +132,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye // perfectly fit the region it would have been cropped to. We may also avoid certain logic we // would otherwise apply while resizing, while resizing in the bounds animating mode. private boolean mBoundsAnimating = false; + private Rect mBoundsAnimationTarget = new Rect(); // Temporary storage for the new bounds that should be used after the configuration change. // Will be cleared once the client retrieves the new bounds via getBoundsForNewConfiguration(). @@ -329,6 +330,30 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye mDisplayContent.getLogicalDisplayRect(out); } + /** + * Sets the bounds animation target bounds. This can't currently be done in onAnimationStart() + * since that is started on the UiThread. + */ + void setAnimatingBounds(Rect bounds) { + if (bounds != null) { + mBoundsAnimationTarget.set(bounds); + } else { + mBoundsAnimationTarget.setEmpty(); + } + } + + /** + * @return the bounds that the task stack is currently being animated towards, or the current + * stack bounds if there is no animation in progress. + */ + void getAnimatingBounds(Rect outBounds) { + if (!mBoundsAnimationTarget.isEmpty()) { + outBounds.set(mBoundsAnimationTarget); + return; + } + getBounds(outBounds); + } + /** Bounds of the stack with other system factors taken into consideration. */ @Override public void getDimBounds(Rect out) { @@ -1391,6 +1416,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye public void onAnimationEnd() { synchronized (mService.mWindowMap) { mBoundsAnimating = false; + mBoundsAnimationTarget.setEmpty(); mService.requestTraversal(); } if (mStackId == PINNED_STACK_ID) { diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index e30ebcb3a6a1..62ad21737a7e 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -22,6 +22,7 @@ import android.view.animation.Animation; import java.util.Comparator; import java.util.LinkedList; +import java.util.function.Consumer; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; @@ -494,6 +495,19 @@ class WindowContainer<E extends WindowContainer> implements Comparable<WindowCon return addIndex; } + void forAllWindows(Consumer<WindowState> callback, boolean traverseTopToBottom) { + if (traverseTopToBottom) { + for (int i = mChildren.size() - 1; i >= 0; --i) { + mChildren.get(i).forAllWindows(callback, traverseTopToBottom); + } + } else { + final int count = mChildren.size(); + for (int i = 0; i < count; i++) { + mChildren.get(i).forAllWindows(callback, traverseTopToBottom); + } + } + } + /** * Returns 1, 0, or -1 depending on if this container is greater than, equal to, or lesser than * the input container in terms of z-order. @@ -562,8 +576,7 @@ class WindowContainer<E extends WindowContainer> implements Comparable<WindowCon void dumpChildrenNames(StringBuilder out, String prefix) { final String childPrefix = prefix + " "; out.append(getName() + "\n"); - final int count = mChildren.size(); - for (int i = 0; i < count; i++) { + for (int i = mChildren.size() - 1; i >= 0; --i) { final WindowContainer wc = mChildren.get(i); out.append(childPrefix + "#" + i + " "); wc.dumpChildrenNames(out, childPrefix); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 5baa72a859eb..5c9dc10f31de 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -172,7 +172,6 @@ import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.app.StatusBarManager.DISABLE_MASK; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; -import static android.util.TypedValue.COMPLEX_UNIT_DIP; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.DOCKED_INVALID; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; @@ -3402,7 +3401,7 @@ public class WindowManagerService extends IWindowManager.Stub public Rect getPictureInPictureDefaultBounds(int displayId) { synchronized (mWindowMap) { if (!mSupportsPictureInPicture) { - return new Rect(); + return null; } final DisplayContent displayContent = mRoot.getDisplayContent(displayId); @@ -3414,7 +3413,7 @@ public class WindowManagerService extends IWindowManager.Stub public Rect getPictureInPictureMovementBounds(int displayId) { synchronized (mWindowMap) { if (!mSupportsPictureInPicture) { - return new Rect(); + return null; } final Rect stackBounds = new Rect(); @@ -3428,6 +3427,47 @@ public class WindowManagerService extends IWindowManager.Stub } } + public void setPictureInPictureAspectRatio(float aspectRatio) { + synchronized (mWindowMap) { + if (!mSupportsPictureInPicture) { + return; + } + + final TaskStack stack = mStackIdToStack.get(PINNED_STACK_ID); + if (stack == null) { + return; + } + + animateResizePinnedStack(getPictureInPictureBounds( + stack.getDisplayContent().getDisplayId(), aspectRatio), -1); + } + } + + public Rect getPictureInPictureBounds(int displayId, float aspectRatio) { + synchronized (mWindowMap) { + if (!mSupportsPictureInPicture) { + return null; + } + + final Rect stackBounds; + final DisplayContent displayContent; + final TaskStack stack = mStackIdToStack.get(PINNED_STACK_ID); + if (stack != null) { + // If the stack exists, then use its final bounds to calculate the new aspect ratio + // bounds. + displayContent = stack.getDisplayContent(); + stackBounds = new Rect(); + stack.getAnimatingBounds(stackBounds); + } else { + // Otherwise, just calculate the aspect ratio bounds from the default bounds + displayContent = mRoot.getDisplayContent(displayId); + stackBounds = displayContent.getPinnedStackController().getDefaultBounds(); + } + return displayContent.getPinnedStackController().getAspectRatioBounds(stackBounds, + aspectRatio); + } + } + /** * Place a TaskStack on a DisplayContent. Will create a new TaskStack if none is found with * specified stackId. @@ -3576,6 +3616,11 @@ public class WindowManagerService extends IWindowManager.Stub notifyKeyguardFlagsChanged(null /* callback */); } + @Override + public void notifyKeyguardTrustedChanged() { + mH.sendEmptyMessage(H.NOTIFY_KEYGUARD_TRUSTED_CHANGED); + } + /** * Re-sizes a stack and its containing tasks. * @param stackId Id of stack to resize. @@ -6114,6 +6159,7 @@ public class WindowManagerService extends IWindowManager.Stub public static final int SEAMLESS_ROTATION_TIMEOUT = 54; public static final int RESTORE_POINTER_ICON = 55; public static final int NOTIFY_KEYGUARD_FLAGS_CHANGED = 56; + public static final int NOTIFY_KEYGUARD_TRUSTED_CHANGED = 57; /** * Used to denote that an integer field in a message will not be used. @@ -6752,6 +6798,11 @@ public class WindowManagerService extends IWindowManager.Stub case NOTIFY_KEYGUARD_FLAGS_CHANGED: { mAmInternal.notifyKeyguardFlagsChanged((Runnable) msg.obj); } + break; + case NOTIFY_KEYGUARD_TRUSTED_CHANGED: { + mAmInternal.notifyKeyguardTrustedChanged(); + } + break; } if (DEBUG_WINDOW_TRACE) { Slog.v(TAG_WM, "handleMessage: exit"); @@ -8201,6 +8252,8 @@ public class WindowManagerService extends IWindowManager.Stub StringBuilder output = new StringBuilder(); mRoot.dumpChildrenNames(output, " "); pw.println(output.toString()); + pw.println(" "); + mRoot.forAllWindows(pw::println, true /* traverseTopToBottom */); } return; } else { @@ -8454,6 +8507,7 @@ public class WindowManagerService extends IWindowManager.Stub } final Rect originalBounds = new Rect(); stack.getBounds(originalBounds); + stack.setAnimatingBounds(bounds); UiThread.getHandler().post(new Runnable() { @Override public void run() { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 972c35911b39..1a5651853818 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -59,6 +59,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Comparator; import java.util.LinkedList; +import java.util.function.Consumer; import static android.app.ActivityManager.StackId; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; @@ -488,7 +489,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP * or some other higher level component said so (e.g. activity manager). * TODO: We should either have different booleans for the removal reason or use a bit-field. */ - private boolean mWindowRemovalAllowed; + boolean mWindowRemovalAllowed; /** * Temp for keeping track of windows that have been removed when @@ -3876,6 +3877,78 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return index; } + @Override + void forAllWindows(Consumer<WindowState> callback, boolean traverseTopToBottom) { + if (mChildren.isEmpty()) { + // The window has no children so we just return it. + callback.accept(this); + return; + } + + if (traverseTopToBottom) { + forAllWindowTopToBottom(callback); + } else { + forAllWindowBottomToTop(callback); + } + } + + private void forAllWindowBottomToTop(Consumer<WindowState> callback) { + // We want to consumer the negative sublayer children first because they need to appear + // below the parent, then this window (the parent), and then the positive sublayer children + // because they need to appear above the parent. + int i = 0; + final int count = mChildren.size(); + WindowState child = mChildren.get(i); + + while (i < count && child.mSubLayer < 0) { + callback.accept(child); + i++; + if (i >= count) { + break; + } + child = mChildren.get(i); + } + + callback.accept(this); + + while (i < count) { + callback.accept(child); + i++; + if (i >= count) { + break; + } + child = mChildren.get(i); + } + } + + private void forAllWindowTopToBottom(Consumer<WindowState> callback) { + // We want to consumer the positive sublayer children first because they need to appear + // above the parent, then this window (the parent), and then the negative sublayer children + // because they need to appear above the parent. + int i = mChildren.size() - 1; + WindowState child = mChildren.get(i); + + while (i >= 0 && child.mSubLayer >= 0) { + callback.accept(child); + --i; + if (i < 0) { + break; + } + child = mChildren.get(i); + } + + callback.accept(this); + + while (i >= 0) { + callback.accept(child); + --i; + if (i < 0) { + break; + } + child = mChildren.get(i); + } + } + boolean isWindowAnimationSet() { if (mWinAnimator.isWindowAnimationSet()) { return true; diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 93f1610e926d..402bcfb863f0 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -196,9 +196,8 @@ class WindowToken extends WindowContainer<WindowState> { */ protected boolean isFirstChildWindowGreaterThanSecond(WindowState newWindow, WindowState existingWindow) { - // By default the first window isn't greater than the second to preserve existing logic of - // how new windows are added to the token - return false; + // New window is considered greater if it has a higher or equal base layer. + return newWindow.mBaseLayer >= existingWindow.mBaseLayer; } void addWindow(final WindowState win) { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index cc96f565564a..3db24fa28d9f 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -27,6 +27,7 @@ import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources.Theme; import android.os.BaseBundle; +import android.os.Binder; import android.os.Build; import android.os.Environment; import android.os.FactoryTest; @@ -184,6 +185,8 @@ public final class SystemServer { "com.android.server.content.ContentService$Lifecycle"; private static final String WALLPAPER_SERVICE_CLASS = "com.android.server.wallpaper.WallpaperManagerService$Lifecycle"; + private static final String AUTO_FILL_MANAGER_SERVICE_CLASS = + "com.android.server.autofill.AutoFillManagerService"; private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst"; @@ -267,6 +270,9 @@ public final class SystemServer { SystemProperties.set("persist.sys.localevar", ""); } + // The system server should never make non-oneway calls + Binder.setWarnOnBlocking(true); + // Here we go! Slog.i(TAG, "Entered the Android system server!"); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN, SystemClock.uptimeMillis()); @@ -1362,6 +1368,10 @@ public final class SystemServer { mSystemServiceManager.startService(RetailDemoModeService.class); traceEnd(); + traceBeginAndSlog("StartAutoFillService"); + mSystemServiceManager.startService(AUTO_FILL_MANAGER_SERVICE_CLASS); + traceEnd(); + // It is now time to start up the app processes... traceBeginAndSlog("MakeVibratorServiceReady"); diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java index 07b26e83e934..6b919df290bb 100644 --- a/services/print/java/com/android/server/print/RemotePrintSpooler.java +++ b/services/print/java/com/android/server/print/RemotePrintSpooler.java @@ -43,9 +43,12 @@ import android.printservice.PrintService; import android.util.Slog; import android.util.TimedRemoteCaller; +import com.android.internal.os.TransferPipe; + import libcore.io.IoUtils; import java.io.FileDescriptor; +import java.io.IOException; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.List; @@ -572,13 +575,11 @@ final class RemotePrintSpooler { .append((mRemoteInstance != null) ? "true" : "false").println(); pw.flush(); - try { - getRemoteInstanceLazy().asBinder().dump(fd, new String[]{prefix}); - } catch (TimeoutException te) { - /* ignore */ - } catch (RemoteException re) { - /* ignore */ + TransferPipe.dumpAsync(getRemoteInstanceLazy().asBinder(), fd, + new String[] { prefix }); + } catch (IOException | TimeoutException | RemoteException e) { + pw.println("Failed to dump remote instance: " + e); } } } diff --git a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java index 4570a4b432a8..d20e351157cb 100644 --- a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java +++ b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java @@ -226,11 +226,11 @@ public class BuzzBeepBlinkTest { if (insistent) { n.flags |= Notification.FLAG_INSISTENT; } - StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid, mPid, - mScore, n, mUser, System.currentTimeMillis()); - NotificationRecord r = new NotificationRecord(getContext(), sbn, - new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "misc", - NotificationManager.IMPORTANCE_DEFAULT)); + NotificationChannel channel = + new NotificationChannel("test", "test", NotificationManager.IMPORTANCE_HIGH); + StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, channel, id, mTag, mUid, + mPid, n, mUser, null, System.currentTimeMillis()); + NotificationRecord r = new NotificationRecord(getContext(), sbn); mService.addNotification(r); return r; } diff --git a/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java b/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java index 22b674b93b09..6c3f44778ae6 100644 --- a/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java +++ b/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java @@ -31,6 +31,7 @@ import org.mockito.MockitoAnnotations; import android.app.AlarmManager; import android.app.Notification; import android.app.NotificationChannel; +import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.os.UserHandle; @@ -68,7 +69,10 @@ public class GroupHelperTest { if (groupKey != null) { nb.setGroup(groupKey); } - return new StatusBarNotification(pkg, pkg, id, tag, 0, 0, 0, nb.build(), user); + NotificationChannel channel = + new NotificationChannel("test", "test", NotificationManager.IMPORTANCE_LOW); + return new StatusBarNotification(pkg, pkg, channel, id, tag, 0, 0, nb.build(), user, null, + System.currentTimeMillis()); } private StatusBarNotification getSbn(String pkg, int id, String tag, diff --git a/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java b/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java index 305b5e0acd84..6bc96754e200 100644 --- a/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java +++ b/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java @@ -69,9 +69,9 @@ public class ImportanceExtractorTest { .setDefaults(Notification.DEFAULT_SOUND); Notification n = builder.build(); - StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, mId, mTag, mUid, - mPid, mScore, n, mUser, System.currentTimeMillis()); - NotificationRecord r = new NotificationRecord(getContext(), sbn, channel); + StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, channel, mId, mTag, mUid, + mPid, n, mUser, null, System.currentTimeMillis()); + NotificationRecord r = new NotificationRecord(getContext(), sbn); return r; } diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java index 629146f19006..92c67b5b0aa9 100644 --- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java +++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java @@ -98,8 +98,8 @@ public class RankingHelperTest { .setWhen(1205) .build(); mRecordGroupGSortA = new NotificationRecord(getContext(), new StatusBarNotification( - "package", "package", 1, null, 0, 0, 0, mNotiGroupGSortA, user), - getDefaultChannel()); + "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiGroupGSortA, user, + null, System.currentTimeMillis())); mNotiGroupGSortB = new Notification.Builder(getContext()) .setContentTitle("B") @@ -108,24 +108,24 @@ public class RankingHelperTest { .setWhen(1200) .build(); mRecordGroupGSortB = new NotificationRecord(getContext(), new StatusBarNotification( - "package", "package", 1, null, 0, 0, 0, mNotiGroupGSortB, user), - getDefaultChannel()); + "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiGroupGSortB, user, + null, System.currentTimeMillis())); mNotiNoGroup = new Notification.Builder(getContext()) .setContentTitle("C") .setWhen(1201) .build(); mRecordNoGroup = new NotificationRecord(getContext(), new StatusBarNotification( - "package", "package", 1, null, 0, 0, 0, mNotiNoGroup, user), - getDefaultChannel()); + "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiNoGroup, user, + null, System.currentTimeMillis())); mNotiNoGroup2 = new Notification.Builder(getContext()) .setContentTitle("D") .setWhen(1202) .build(); mRecordNoGroup2 = new NotificationRecord(getContext(), new StatusBarNotification( - "package", "package", 1, null, 0, 0, 0, mNotiNoGroup2, user), - getDefaultChannel()); + "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiNoGroup2, user, + null, System.currentTimeMillis())); mNotiNoGroupSortA = new Notification.Builder(getContext()) .setContentTitle("E") @@ -133,8 +133,8 @@ public class RankingHelperTest { .setSortKey("A") .build(); mRecordNoGroupSortA = new NotificationRecord(getContext(), new StatusBarNotification( - "package", "package", 1, null, 0, 0, 0, mNotiNoGroupSortA, user), - getDefaultChannel()); + "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiNoGroupSortA, user, + null, System.currentTimeMillis())); final ApplicationInfo legacy = new ApplicationInfo(); legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1; diff --git a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java index ffc45ea96e91..7a3ee7f4c068 100644 --- a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java @@ -178,9 +178,13 @@ public class SnoozeHelperTest { .setWhen(1205) .build(); return new NotificationRecord(getContext(), new StatusBarNotification( - pkg, pkg, id, tag, 0, 0, 0, n, user), - new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name", - NotificationManager.IMPORTANCE_HIGH)); + pkg, pkg, getDefaultChannel(), id, tag, 0, 0, n, user, null, + System.currentTimeMillis())); + } + + private NotificationChannel getDefaultChannel() { + return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name", + NotificationManager.IMPORTANCE_LOW); } } diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java index d933cb377ef0..772b2a2c4737 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java @@ -33,12 +33,11 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; /** - * Tests for the {@link WindowState} class. + * Tests for the {@link AppWindowToken} class. * * Build/Install/Run: * bit FrameworksServicesTests:com.android.server.wm.AppWindowTokenTests diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java new file mode 100644 index 000000000000..0533efc6d862 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2016 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.wm; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import android.content.Context; +import android.content.res.Configuration; +import android.os.IBinder; +import android.platform.test.annotations.Presubmit; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.view.Display; +import android.view.IWindow; +import android.view.WindowManager; + +import java.util.ArrayList; + +import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID; +import static android.app.AppOpsManager.OP_NONE; +import static android.content.res.Configuration.EMPTY; +import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; +import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; + +/** + * Tests for the {@link DisplayContent} class. + * + * Build/Install/Run: + * bit FrameworksServicesTests:com.android.server.wm.DisplayContentTests + */ +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class DisplayContentTests { + + private static WindowManagerService sWm = null; + private final IWindow mIWindow = new TestIWindow(); + private final Session mMockSession = mock(Session.class); + private Display mDisplay; + private int mNextStackId = FIRST_DYNAMIC_STACK_ID; + private int mNextTaskId = 0; + + @Before + public void setUp() throws Exception { + final Context context = InstrumentationRegistry.getTargetContext(); + sWm = TestWindowManagerPolicy.getWindowManagerService(context); + mDisplay = context.getDisplay(); + } + + @Test + public void testForAllWindows() throws Exception { + final DisplayContent dc = new DisplayContent(mDisplay, sWm, null, null); + final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, dc, "wallpaper"); + final WindowState imeWindow = createWindow(null, TYPE_INPUT_METHOD, dc, "ime"); + final WindowState imeDialogWindow = createWindow(null, TYPE_INPUT_METHOD_DIALOG, dc, + "ime dialog"); + final WindowState statusBarWindow = createWindow(null, TYPE_STATUS_BAR, dc, "status bar"); + final WindowState navBarWindow = createWindow(null, TYPE_NAVIGATION_BAR, + statusBarWindow.mToken, "nav bar"); + final WindowState appWindow = createWindow(null, TYPE_BASE_APPLICATION, dc, "app"); + final WindowState negChildAppWindow = createWindow(appWindow, TYPE_APPLICATION_MEDIA, + appWindow.mToken, "negative app child"); + final WindowState posChildAppWindow = createWindow(appWindow, + TYPE_APPLICATION_ATTACHED_DIALOG, appWindow.mToken, "positive app child"); + final WindowState exitingAppWindow = createWindow(null, TYPE_BASE_APPLICATION, dc, + "exiting app"); + final AppWindowToken exitingAppToken = exitingAppWindow.mAppToken; + exitingAppToken.mIsExiting = true; + exitingAppToken.mTask.mStack.mExitingAppTokens.add(exitingAppToken); + + final ArrayList<WindowState> windows = new ArrayList(); + + // Test forward traversal. + dc.forAllWindows(windows::add, false /* traverseTopToBottom */); + + assertEquals(wallpaperWindow, windows.get(0)); + assertEquals(exitingAppWindow, windows.get(1)); + assertEquals(negChildAppWindow, windows.get(2)); + assertEquals(appWindow, windows.get(3)); + assertEquals(posChildAppWindow, windows.get(4)); + assertEquals(statusBarWindow, windows.get(5)); + assertEquals(navBarWindow, windows.get(6)); + assertEquals(imeWindow, windows.get(7)); + assertEquals(imeDialogWindow, windows.get(8)); + + // Test backward traversal. + windows.clear(); + dc.forAllWindows(windows::add, true /* traverseTopToBottom */); + + assertEquals(wallpaperWindow, windows.get(8)); + assertEquals(exitingAppWindow, windows.get(7)); + assertEquals(negChildAppWindow, windows.get(6)); + assertEquals(appWindow, windows.get(5)); + assertEquals(posChildAppWindow, windows.get(4)); + assertEquals(statusBarWindow, windows.get(3)); + assertEquals(navBarWindow, windows.get(2)); + assertEquals(imeWindow, windows.get(1)); + assertEquals(imeDialogWindow, windows.get(0)); + } + + private WindowToken createWindowToken(DisplayContent dc, int type) { + if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) { + return new WindowToken(sWm, mock(IBinder.class), type, false, dc); + } + + final int stackId = mNextStackId++; + dc.addStackToDisplay(stackId, true); + final TaskStack stack = sWm.mStackIdToStack.get(stackId); + final Task task = new Task(mNextTaskId++, stack, 0, sWm, null, EMPTY, false); + stack.addTask(task, true); + final AppWindowToken token = new AppWindowToken(sWm, null, false, dc); + task.addAppToken(0, token, 0, false); + return token; + } + + private WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) { + final WindowToken token = createWindowToken(dc, type); + return createWindow(parent, type, token, name); + } + + private WindowState createWindow(WindowState parent, int type, WindowToken token, String name) { + final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type); + attrs.setTitle(name); + + final WindowState w = new WindowState(sWm, mMockSession, mIWindow, token, parent, OP_NONE, + 0, attrs, 0, 0); + // TODO: Probably better to make this call in the WindowState ctor to avoid errors with + // adding it to the token... + token.addWindow(w); + return w; + } +} diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java index 5326a192bf73..d12d672ab5cc 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java @@ -39,7 +39,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; /** - * Tests for the {@link WindowState} class. + * Tests for the {@link WindowToken} class. * * Build/Install/Run: * bit FrameworksServicesTests:com.android.server.wm.WindowTokenTests diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 62625bdf953e..c99e22a42ffa 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -843,6 +843,7 @@ public final class Call { private String mParentId = null; private int mState; private List<String> mCannedTextResponses = null; + private String mCallingPackage; private String mRemainingPostDialSequence; private VideoCallImpl mVideoCallImpl; private Details mDetails; @@ -1330,19 +1331,22 @@ public final class Call { } /** {@hide} */ - Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter) { + Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, String callingPackage) { mPhone = phone; mTelecomCallId = telecomCallId; mInCallAdapter = inCallAdapter; mState = STATE_NEW; + mCallingPackage = callingPackage; } /** {@hide} */ - Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, int state) { + Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, int state, + String callingPackage) { mPhone = phone; mTelecomCallId = telecomCallId; mInCallAdapter = inCallAdapter; mState = state; + mCallingPackage = callingPackage; } /** {@hide} */ @@ -1352,6 +1356,7 @@ public final class Call { /** {@hide} */ final void internalUpdate(ParcelableCall parcelableCall, Map<String, Call> callIdMap) { + // First, we update the internal state as far as possible before firing any updates. Details details = Details.createFromParcelableCall(parcelableCall); boolean detailsChanged = !Objects.equals(mDetails, details); @@ -1367,7 +1372,7 @@ public final class Call { cannedTextResponsesChanged = true; } - VideoCallImpl newVideoCallImpl = parcelableCall.getVideoCallImpl(); + VideoCallImpl newVideoCallImpl = parcelableCall.getVideoCallImpl(mCallingPackage); boolean videoCallChanged = parcelableCall.isVideoCallProviderChanged() && !Objects.equals(mVideoCallImpl, newVideoCallImpl); if (videoCallChanged) { diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 8f9c7585ced6..2b9a50851186 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -25,6 +25,7 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.hardware.camera2.CameraManager; import android.net.Uri; +import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -785,7 +786,7 @@ public abstract class Connection extends Conferenceable { public static final int SESSION_EVENT_TX_STOP = 4; /** - * A camera failure has occurred for the selected camera. The {@link InCallService} can use + * A camera failure has occurred for the selected camera. The {@link VideoProvider} can use * this as a cue to inform the user the camera is not available. * @see #handleCallSessionEvent(int) */ @@ -793,13 +794,21 @@ public abstract class Connection extends Conferenceable { /** * Issued after {@link #SESSION_EVENT_CAMERA_FAILURE} when the camera is once again ready - * for operation. The {@link InCallService} can use this as a cue to inform the user that + * for operation. The {@link VideoProvider} can use this as a cue to inform the user that * the camera has become available again. * @see #handleCallSessionEvent(int) */ public static final int SESSION_EVENT_CAMERA_READY = 6; /** + * Session event raised by Telecom when + * {@link android.telecom.InCallService.VideoCall#setCamera(String)} is called and the + * caller does not have the necessary {@link android.Manifest.permission#CAMERA} permission. + * @see #handleCallSessionEvent(int) + */ + public static final int SESSION_EVENT_CAMERA_PERMISSION_ERROR = 7; + + /** * Session modify request was successful. * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile) */ @@ -848,6 +857,8 @@ public abstract class Connection extends Conferenceable { private static final String SESSION_EVENT_TX_STOP_STR = "TX_STOP"; private static final String SESSION_EVENT_CAMERA_FAILURE_STR = "CAMERA_FAIL"; private static final String SESSION_EVENT_CAMERA_READY_STR = "CAMERA_READY"; + private static final String SESSION_EVENT_CAMERA_PERMISSION_ERROR_STR = + "CAMERA_PERMISSION_ERROR"; private static final String SESSION_EVENT_UNKNOWN_STR = "UNKNOWN"; private VideoProvider.VideoProviderHandler mMessageHandler; @@ -906,8 +917,17 @@ public abstract class Connection extends Conferenceable { break; } case MSG_SET_CAMERA: - onSetCamera((String) msg.obj); - break; + { + SomeArgs args = (SomeArgs) msg.obj; + try { + onSetCamera((String) args.arg1); + onSetCamera((String) args.arg1, (String) args.arg2, args.argi1, + args.argi2); + } finally { + args.recycle(); + } + } + break; case MSG_SET_PREVIEW_SURFACE: onSetPreviewSurface((Surface) msg.obj); break; @@ -962,8 +982,19 @@ public abstract class Connection extends Conferenceable { MSG_REMOVE_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget(); } - public void setCamera(String cameraId) { - mMessageHandler.obtainMessage(MSG_SET_CAMERA, cameraId).sendToTarget(); + public void setCamera(String cameraId, String callingPackageName) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = cameraId; + // Propagate the calling package; originally determined in + // android.telecom.InCallService.VideoCall#setCamera(String) from the calling + // process. + args.arg2 = callingPackageName; + // Pass along the uid and pid of the calling app; this gets lost when we put the + // message onto the handler. These are required for Telecom to perform a permission + // check to see if the calling app is able to use the camera. + args.argi1 = Binder.getCallingUid(); + args.argi2 = Binder.getCallingPid(); + mMessageHandler.obtainMessage(MSG_SET_CAMERA, args).sendToTarget(); } public void setPreviewSurface(Surface surface) { @@ -1048,6 +1079,29 @@ public abstract class Connection extends Conferenceable { public abstract void onSetCamera(String cameraId); /** + * Sets the camera to be used for the outgoing video. + * <p> + * The {@link VideoProvider} should respond by communicating the capabilities of the chosen + * camera via + * {@link VideoProvider#changeCameraCapabilities(VideoProfile.CameraCapabilities)}. + * <p> + * This prototype is used internally to ensure that the calling package name, UID and PID + * are sent to Telecom so that can perform a camera permission check on the caller. + * <p> + * Sent from the {@link InCallService} via + * {@link InCallService.VideoCall#setCamera(String)}. + * + * @param cameraId The id of the camera (use ids as reported by + * {@link CameraManager#getCameraIdList()}). + * @param callingPackageName The AppOpps package name of the caller. + * @param callingUid The UID of the caller. + * @param callingPid The PID of the caller. + * @hide + */ + public void onSetCamera(String cameraId, String callingPackageName, int callingUid, + int callingPid) {} + + /** * Sets the surface to be used for displaying a preview of what the user's camera is * currently capturing. When video transmission is enabled, this is the video signal which * is sent to the remote device. @@ -1233,7 +1287,8 @@ public abstract class Connection extends Conferenceable { * {@link VideoProvider#SESSION_EVENT_TX_START}, * {@link VideoProvider#SESSION_EVENT_TX_STOP}, * {@link VideoProvider#SESSION_EVENT_CAMERA_FAILURE}, - * {@link VideoProvider#SESSION_EVENT_CAMERA_READY}. + * {@link VideoProvider#SESSION_EVENT_CAMERA_READY}, + * {@link VideoProvider#SESSION_EVENT_CAMERA_FAILURE}. */ public void handleCallSessionEvent(int event) { if (mVideoCallbacks != null) { @@ -1382,6 +1437,8 @@ public abstract class Connection extends Conferenceable { return SESSION_EVENT_TX_START_STR; case SESSION_EVENT_TX_STOP: return SESSION_EVENT_TX_STOP_STR; + case SESSION_EVENT_CAMERA_PERMISSION_ERROR: + return SESSION_EVENT_CAMERA_PERMISSION_ERROR_STR; default: return SESSION_EVENT_UNKNOWN_STR + " " + event; } diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java index 69de89d5ed74..5d68aaeda988 100644 --- a/telecomm/java/android/telecom/InCallService.java +++ b/telecomm/java/android/telecom/InCallService.java @@ -87,7 +87,8 @@ public abstract class InCallService extends Service { switch (msg.what) { case MSG_SET_IN_CALL_ADAPTER: - mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj)); + String callingPackage = getApplicationContext().getOpPackageName(); + mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj), callingPackage); mPhone.addListener(mPhoneListener); onPhoneCreated(mPhone); break; @@ -664,7 +665,8 @@ public abstract class InCallService extends Service { * {@link Connection.VideoProvider#SESSION_EVENT_TX_START}, * {@link Connection.VideoProvider#SESSION_EVENT_TX_STOP}, * {@link Connection.VideoProvider#SESSION_EVENT_CAMERA_FAILURE}, - * {@link Connection.VideoProvider#SESSION_EVENT_CAMERA_READY}. + * {@link Connection.VideoProvider#SESSION_EVENT_CAMERA_READY}, + * {@link Connection.VideoProvider#SESSION_EVENT_CAMERA_PERMISSION_ERROR}. */ public abstract void onCallSessionEvent(int event); diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java index 4a6fd7c16c1a..1900cb9857d9 100644 --- a/telecomm/java/android/telecom/ParcelableCall.java +++ b/telecomm/java/android/telecom/ParcelableCall.java @@ -182,10 +182,10 @@ public final class ParcelableCall implements Parcelable { * @return The video call. */ - public VideoCallImpl getVideoCallImpl() { + public VideoCallImpl getVideoCallImpl(String callingPackageName) { if (mVideoCall == null && mVideoCallProvider != null) { try { - mVideoCall = new VideoCallImpl(mVideoCallProvider); + mVideoCall = new VideoCallImpl(mVideoCallProvider, callingPackageName); } catch (RemoteException ignored) { // Ignore RemoteException. } diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java index a4ef5601e551..30ec5b350307 100644 --- a/telecomm/java/android/telecom/Phone.java +++ b/telecomm/java/android/telecom/Phone.java @@ -125,13 +125,16 @@ public final class Phone { private boolean mCanAddCall = true; - Phone(InCallAdapter adapter) { + private final String mCallingPackage; + + Phone(InCallAdapter adapter, String callingPackage) { mInCallAdapter = adapter; + mCallingPackage = callingPackage; } final void internalAddCall(ParcelableCall parcelableCall) { Call call = new Call(this, parcelableCall.getId(), mInCallAdapter, - parcelableCall.getState()); + parcelableCall.getState(), mCallingPackage); mCallByTelecomCallId.put(parcelableCall.getId(), call); mCalls.add(call); checkCallTree(parcelableCall); diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java index 0e4f53e4881a..77e0e5486127 100644 --- a/telecomm/java/android/telecom/RemoteConnection.java +++ b/telecomm/java/android/telecom/RemoteConnection.java @@ -408,6 +408,8 @@ public final class RemoteConnection { private final IVideoProvider mVideoProviderBinder; + private final String mCallingPackage; + /** * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is * load factor before resizing, 1 means we only expect a single thread to @@ -416,8 +418,9 @@ public final class RemoteConnection { private final Set<Callback> mCallbacks = Collections.newSetFromMap( new ConcurrentHashMap<Callback, Boolean>(8, 0.9f, 1)); - VideoProvider(IVideoProvider videoProviderBinder) { + VideoProvider(IVideoProvider videoProviderBinder, String callingPackage) { mVideoProviderBinder = videoProviderBinder; + mCallingPackage = callingPackage; try { mVideoProviderBinder.addVideoCallback(mVideoCallbackServant.getStub().asBinder()); } catch (RemoteException e) { @@ -452,7 +455,7 @@ public final class RemoteConnection { */ public void setCamera(String cameraId) { try { - mVideoProviderBinder.setCamera(cameraId); + mVideoProviderBinder.setCamera(cameraId, mCallingPackage); } catch (RemoteException e) { } } @@ -628,7 +631,7 @@ public final class RemoteConnection { * @hide */ RemoteConnection(String callId, IConnectionService connectionService, - ParcelableConnection connection) { + ParcelableConnection connection, String callingPackage) { mConnectionId = callId; mConnectionService = connectionService; mConnected = true; @@ -640,7 +643,7 @@ public final class RemoteConnection { mVideoState = connection.getVideoState(); IVideoProvider videoProvider = connection.getVideoProvider(); if (videoProvider != null) { - mVideoProvider = new RemoteConnection.VideoProvider(videoProvider); + mVideoProvider = new RemoteConnection.VideoProvider(videoProvider, callingPackage); } else { mVideoProvider = null; } diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java index 0a8470af7a7d..d8a226a3b529 100644 --- a/telecomm/java/android/telecom/RemoteConnectionService.java +++ b/telecomm/java/android/telecom/RemoteConnectionService.java @@ -283,9 +283,13 @@ final class RemoteConnectionService { @Override public void setVideoProvider(String callId, IVideoProvider videoProvider, Session.Info sessionInfo) { + + String callingPackage = mOurConnectionServiceImpl.getApplicationContext() + .getOpPackageName(); RemoteConnection.VideoProvider remoteVideoProvider = null; if (videoProvider != null) { - remoteVideoProvider = new RemoteConnection.VideoProvider(videoProvider); + remoteVideoProvider = new RemoteConnection.VideoProvider(videoProvider, + callingPackage); } findConnectionForAction(callId, "setVideoProvider") .setVideoProvider(remoteVideoProvider); @@ -351,8 +355,10 @@ final class RemoteConnectionService { @Override public void addExistingConnection(String callId, ParcelableConnection connection, Session.Info sessionInfo) { + String callingPackage = mOurConnectionServiceImpl.getApplicationContext(). + getOpPackageName(); RemoteConnection remoteConnection = new RemoteConnection(callId, - mOutgoingConnectionServiceRpc, connection); + mOutgoingConnectionServiceRpc, connection, callingPackage); mConnectionById.put(callId, remoteConnection); remoteConnection.registerCallback(new RemoteConnection.Callback() { @Override diff --git a/telecomm/java/android/telecom/VideoCallImpl.java b/telecomm/java/android/telecom/VideoCallImpl.java index e54abeebb880..d8ede5c21316 100644 --- a/telecomm/java/android/telecom/VideoCallImpl.java +++ b/telecomm/java/android/telecom/VideoCallImpl.java @@ -43,6 +43,7 @@ public class VideoCallImpl extends VideoCall { private VideoCall.Callback mCallback; private int mVideoQuality = VideoProfile.QUALITY_UNKNOWN; private int mVideoState = VideoProfile.STATE_AUDIO_ONLY; + private final String mCallingPackageName; private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { @Override @@ -197,12 +198,13 @@ public class VideoCallImpl extends VideoCall { private Handler mHandler; - VideoCallImpl(IVideoProvider videoProvider) throws RemoteException { + VideoCallImpl(IVideoProvider videoProvider, String callingPackageName) throws RemoteException { mVideoProvider = videoProvider; mVideoProvider.asBinder().linkToDeath(mDeathRecipient, 0); mBinder = new VideoCallListenerBinder(); mVideoProvider.addVideoCallback(mBinder); + mCallingPackageName = callingPackageName; } public void destroy() { @@ -240,7 +242,8 @@ public class VideoCallImpl extends VideoCall { /** {@inheritDoc} */ public void setCamera(String cameraId) { try { - mVideoProvider.setCamera(cameraId); + Log.w(this, "setCamera: cameraId=%s, calling=%s", cameraId, mCallingPackageName); + mVideoProvider.setCamera(cameraId, mCallingPackageName); } catch (RemoteException e) { } } diff --git a/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl b/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl index 68e5fd48e1ac..a109e90243fe 100644 --- a/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl +++ b/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl @@ -30,7 +30,7 @@ oneway interface IVideoProvider { void removeVideoCallback(IBinder videoCallbackBinder); - void setCamera(String cameraId); + void setCamera(String cameraId, in String mCallingPackageName); void setPreviewSurface(in Surface surface); diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java index bb2b4478433d..32f487bb5dbe 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/telephony/java/android/telephony/PhoneStateListener.java @@ -233,7 +233,7 @@ public class PhoneStateListener { * @hide */ /** @hide */ - protected int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + protected Integer mSubId; private final Handler mHandler; @@ -242,7 +242,7 @@ public class PhoneStateListener { * This class requires Looper.myLooper() not return null. */ public PhoneStateListener() { - this(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, Looper.myLooper()); + this(null, Looper.myLooper()); } /** @@ -251,7 +251,7 @@ public class PhoneStateListener { * @hide */ public PhoneStateListener(Looper looper) { - this(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, looper); + this(null, looper); } /** @@ -260,7 +260,7 @@ public class PhoneStateListener { * own non-null Looper use PhoneStateListener(int subId, Looper looper) below. * @hide */ - public PhoneStateListener(int subId) { + public PhoneStateListener(Integer subId) { this(subId, Looper.myLooper()); } @@ -269,7 +269,7 @@ public class PhoneStateListener { * and non-null Looper. * @hide */ - public PhoneStateListener(int subId, Looper looper) { + public PhoneStateListener(Integer subId, Looper looper) { if (DBG) log("ctor: subId=" + subId + " looper=" + looper); mSubId = subId; mHandler = new Handler(looper) { diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index dcf2f0696bfd..20dd012b07e1 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -263,6 +263,22 @@ public class TelephonyManager { return new TelephonyManager(mContext, subId); } + /** + * Create a new TelephonyManager object pinned to the subscription ID associated with the given + * phone account. + * + * @return a TelephonyManager that uses the given phone account for all calls, or {@code null} + * if the phone account does not correspond to a valid subscription ID. + */ + @Nullable + public TelephonyManager createForPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) { + int subId = getSubIdForPhoneAccountHandle(phoneAccountHandle); + if (!SubscriptionManager.isValidSubscriptionId(subId)) { + return null; + } + return new TelephonyManager(mContext, subId); + } + /** {@hide} */ public boolean isMultiSimEnabled() { return (multiSimConfig.equals("dsds") || multiSimConfig.equals("dsda") || @@ -3019,6 +3035,12 @@ public class TelephonyManager { if (mContext == null) return; try { Boolean notifyNow = (getITelephony() != null); + // If the listener has not explicitly set the subId (for example, created with the + // default constructor), replace the subId so it will listen to the account the + // telephony manager is created with. + if (listener.mSubId == null) { + listener.mSubId = mSubId; + } sRegistry.listenForSubscriber(listener.mSubId, getOpPackageName(), listener.callback, events, notifyNow); } catch (RemoteException ex) { @@ -5446,6 +5468,19 @@ public class TelephonyManager { return retval; } + private int getSubIdForPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) { + int retval = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + try { + ITelecomService service = getTelecomService(); + if (service != null) { + retval = getSubIdForPhoneAccount(service.getPhoneAccount(phoneAccountHandle)); + } + } catch (RemoteException e) { + } + + return retval; + } + /** * Resets telephony manager settings back to factory defaults. * @@ -5495,6 +5530,16 @@ public class TelephonyManager { } /** + * Returns the current {@link ServiceState} information. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + */ + public ServiceState getServiceState() { + return getServiceStateForSubscriber(getSubId()); + } + + /** * Returns the service state information on specified subscription. Callers require * either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE to retrieve the information. * @hide diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java index a30b3629d5c4..9f7261dc6019 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java @@ -59,13 +59,14 @@ public class TetherInterfaceStateMachineTest { @Mock private INetworkStatsService mStatsService; @Mock private IControlsTethering mTetherHelper; @Mock private InterfaceConfiguration mInterfaceConfiguration; + @Mock private IPv6TetheringInterfaceServices mIPv6TetheringInterfaceServices; private final TestLooper mLooper = new TestLooper(); private TetherInterfaceStateMachine mTestedSm; private void initStateMachine(int interfaceType) throws Exception { mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(), interfaceType, - mNMService, mStatsService, mTetherHelper); + mNMService, mStatsService, mTetherHelper, mIPv6TetheringInterfaceServices); mTestedSm.start(); // Starting the state machine always puts us in a consistent state and notifies // the test of the world that we've changed from an unknown to available state. @@ -91,7 +92,8 @@ public class TetherInterfaceStateMachineTest { @Test public void startsOutAvailable() { mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(), - ConnectivityManager.TETHERING_BLUETOOTH, mNMService, mStatsService, mTetherHelper); + ConnectivityManager.TETHERING_BLUETOOTH, mNMService, mStatsService, mTetherHelper, + mIPv6TetheringInterfaceServices); mTestedSm.start(); mLooper.dispatchAll(); verify(mTetherHelper).notifyInterfaceStateChange( @@ -274,4 +276,4 @@ public class TetherInterfaceStateMachineTest { upstreamIface); mLooper.dispatchAll(); } -}
\ No newline at end of file +} diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png Binary files differindex 9bf302ad6906..7b58539f41d8 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml index 88c9cbcb250a..c8a5fec71f09 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml @@ -3,7 +3,7 @@ <!-- Base application theme. --> <style name="AppTheme" parent="android:Theme.Material.Light.DarkActionBar"> <item name="myattr">@integer/ten</item> - <!-- Customize your theme here. --> + <item name="android:animateFirstView">false</item> </style> </resources> diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java index e7c6cc9faa19..55cfd6d41b16 100644 --- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java +++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java @@ -309,15 +309,7 @@ public class Main { /** Test activity.xml */ @Test public void testActivity() throws ClassNotFoundException { - try { - renderAndVerify("activity.xml", "activity.png"); - } catch (AssertionError e) { - // This is a KI in CalendarWidget and DatePicker rendering. - // Tracker bug: http://b.android.com/214370 - if (!e.getLocalizedMessage().startsWith("Images differ (by 6.5%)")) { - throw e; - } - } + renderAndVerify("activity.xml", "activity.png"); } @Test diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 017525b8511b..bc382848a59b 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -16,6 +16,7 @@ package android.net.wifi; +import android.net.wifi.hotspot2.PasspointConfiguration; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.ScanSettings; @@ -63,6 +64,12 @@ interface IWifiManager int modifyPasspointManagementObject(String fqdn, in List<PasspointManagementObjectDefinition> mos); + boolean addPasspointConfiguration(in PasspointConfiguration config); + + boolean removePasspointConfiguration(in String fqdn); + + List<PasspointConfiguration> getPasspointConfigurations(); + void queryPasspointIcon(long bssid, String fileName); int matchProviderWithCurrentNetwork(String fqdn); diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java index 8d5efba8620e..e48f7bdb27ee 100644 --- a/wifi/java/android/net/wifi/WifiInfo.java +++ b/wifi/java/android/net/wifi/WifiInfo.java @@ -22,6 +22,7 @@ import android.net.NetworkInfo.DetailedState; import android.net.NetworkUtils; import android.text.TextUtils; +import java.lang.Math; import java.net.InetAddress; import java.net.Inet4Address; import java.net.UnknownHostException; @@ -136,6 +137,15 @@ public class WifiInfo implements Parcelable { */ public double rxSuccessRate; + private static final long RESET_TIME_STAMP = Long.MIN_VALUE; + private static final long FILTER_TIME_CONSTANT = 3000; + /** + * This factor is used to adjust the rate output under the new algorithm + * such that the result is comparable to the previous algorithm. + */ + private static final long OUTPUT_SCALE_FACTOR = 5000; + private long mLastPacketCountUpdateTimeStamp; + /** * @hide */ @@ -157,10 +167,9 @@ public class WifiInfo implements Parcelable { public int score; /** - * TODO: get actual timestamp and calculate true rates * @hide */ - public void updatePacketRates(WifiLinkLayerStats stats) { + public void updatePacketRates(WifiLinkLayerStats stats, long timeStamp) { if (stats != null) { long txgood = stats.txmpdu_be + stats.txmpdu_bk + stats.txmpdu_vi + stats.txmpdu_vo; long txretries = stats.retries_be + stats.retries_bk @@ -169,18 +178,28 @@ public class WifiInfo implements Parcelable { long txbad = stats.lostmpdu_be + stats.lostmpdu_bk + stats.lostmpdu_vi + stats.lostmpdu_vo; - if (txBad <= txbad + if (mLastPacketCountUpdateTimeStamp != RESET_TIME_STAMP + && mLastPacketCountUpdateTimeStamp < timeStamp + && txBad <= txbad && txSuccess <= txgood && rxSuccess <= rxgood && txRetries <= txretries) { - txBadRate = (txBadRate * 0.5) - + ((double) (txbad - txBad) * 0.5); - txSuccessRate = (txSuccessRate * 0.5) - + ((double) (txgood - txSuccess) * 0.5); - rxSuccessRate = (rxSuccessRate * 0.5) - + ((double) (rxgood - rxSuccess) * 0.5); - txRetriesRate = (txRetriesRate * 0.5) - + ((double) (txretries - txRetries) * 0.5); + long timeDelta = timeStamp - mLastPacketCountUpdateTimeStamp; + double lastSampleWeight = Math.exp(-1.0 * timeDelta / FILTER_TIME_CONSTANT); + double currentSampleWeight = 1.0 - lastSampleWeight; + + txBadRate = txBadRate * lastSampleWeight + + (txbad - txBad) * OUTPUT_SCALE_FACTOR / timeDelta + * currentSampleWeight; + txSuccessRate = txSuccessRate * lastSampleWeight + + (txgood - txSuccess) * OUTPUT_SCALE_FACTOR / timeDelta + * currentSampleWeight; + rxSuccessRate = rxSuccessRate * lastSampleWeight + + (rxgood - rxSuccess) * OUTPUT_SCALE_FACTOR / timeDelta + * currentSampleWeight; + txRetriesRate = txRetriesRate * lastSampleWeight + + (txretries - txRetries) * OUTPUT_SCALE_FACTOR / timeDelta + * currentSampleWeight; } else { txBadRate = 0; txSuccessRate = 0; @@ -191,6 +210,7 @@ public class WifiInfo implements Parcelable { txSuccess = txgood; rxSuccess = rxgood; txRetries = txretries; + mLastPacketCountUpdateTimeStamp = timeStamp; } else { txBad = 0; txSuccess = 0; @@ -200,6 +220,7 @@ public class WifiInfo implements Parcelable { txSuccessRate = 0; rxSuccessRate = 0; txRetriesRate = 0; + mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP; } } @@ -243,6 +264,7 @@ public class WifiInfo implements Parcelable { mRssi = INVALID_RSSI; mLinkSpeed = -1; mFrequency = -1; + mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP; } /** @hide */ @@ -268,6 +290,7 @@ public class WifiInfo implements Parcelable { badRssiCount = 0; linkStuckCount = 0; score = 0; + mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP; } /** @@ -295,6 +318,8 @@ public class WifiInfo implements Parcelable { txRetriesRate = source.txRetriesRate; txSuccessRate = source.txSuccessRate; rxSuccessRate = source.rxSuccessRate; + mLastPacketCountUpdateTimeStamp = + source.mLastPacketCountUpdateTimeStamp; score = source.score; badRssiCount = source.badRssiCount; lowRssiCount = source.lowRssiCount; diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 18a580e03b6d..98a6ca58b1c3 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -26,6 +26,7 @@ import android.net.DhcpInfo; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; +import android.net.wifi.hotspot2.PasspointConfiguration; import android.os.Binder; import android.os.Build; import android.os.Handler; @@ -871,6 +872,56 @@ public class WifiManager { } /** + * Add a Passpoint configuration. The configuration provides a credential + * for connecting to Passpoint networks that are operated by the Passpoint + * service provider specified in the configuration. + * + * Each configuration is uniquely identified by its FQDN (Fully Qualified Domain + * Name). In the case when there is an existing configuration with the same base + * domain, the new configuration will replace the existing configuration. + * + * @param config The Passpoint configuration to be added + * @return true on success or false on failure + * @hide + */ + public boolean addPasspointConfiguration(PasspointConfiguration config) { + try { + return mService.addPasspointConfiguration(config); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Remove a Passpoint configuration identified by its FQDN (Fully Qualified Domain Name). + * + * @param fqdn The FQDN of the passpoint configuration to be removed + * @return true on success or false on failure + * @hide + */ + public boolean removePasspointConfiguration(String fqdn) { + try { + return mService.removePasspointConfiguration(fqdn); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Return the list of installed Passpoint configurations. + * + * @return A list of PasspointConfiguration or null + * @hide + */ + public List<PasspointConfiguration> getPasspointConfigurations() { + try { + return mService.getPasspointConfigurations(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Query for a Hotspot 2.0 release 2 OSU icon * @param bssid The BSSID of the AP * @param fileName Icon file name diff --git a/wifi/java/android/net/wifi/hotspot2/ConfigBuilder.java b/wifi/java/android/net/wifi/hotspot2/ConfigBuilder.java new file mode 100644 index 000000000000..96db5d02679e --- /dev/null +++ b/wifi/java/android/net/wifi/hotspot2/ConfigBuilder.java @@ -0,0 +1,473 @@ +/** + * Copyright (c) 2016, 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.hotspot2; + +import android.net.wifi.hotspot2.omadm.PPSMOParser; +import android.text.TextUtils; +import android.util.Base64; +import android.util.Log; +import android.util.Pair; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Utility class for building PasspointConfiguration from an installation file. + * + * @hide + */ +public final class ConfigBuilder { + private static final String TAG = "ConfigBuilder"; + + // Header names. + private static final String CONTENT_TYPE = "Content-Type"; + private static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding"; + + // MIME types. + private static final String TYPE_MULTIPART_MIXED = "multipart/mixed"; + private static final String TYPE_WIFI_CONFIG = "application/x-wifi-config"; + private static final String TYPE_PASSPOINT_PROFILE = "application/x-passpoint-profile"; + private static final String TYPE_CA_CERT = "application/x-x509-ca-cert"; + private static final String TYPE_PKCS12 = "application/x-pkcs12"; + + private static final String ENCODING_BASE64 = "base64"; + private static final String BOUNDARY = "boundary="; + + /** + * Class represent a MIME (Multipurpose Internet Mail Extension) part. + */ + private static class MimePart { + /** + * Content type of the part. + */ + public String type = null; + + /** + * Decoded data. + */ + public byte[] data = null; + + /** + * Flag indicating if this is the last part (ending with --{boundary}--). + */ + public boolean isLast = false; + } + + /** + * Class represent the MIME (Multipurpose Internet Mail Extension) header. + */ + private static class MimeHeader { + /** + * Content type. + */ + public String contentType = null; + + /** + * Boundary string (optional), only applies for the outter MIME header. + */ + public String boundary = null; + + /** + * Encoding type. + */ + public String encodingType = null; + } + + + /** + * Parse the Hotspot 2.0 Release 1 configuration data into a {@link PasspointConfiguration} + * object. The configuration data is a base64 encoded MIME multipart data. Below is + * the format of the decoded message: + * + * Content-Type: multipart/mixed; boundary={boundary} + * Content-Transfer-Encoding: base64 + * + * --{boundary} + * Content-Type: application/x-passpoint-profile + * Content-Transfer-Encoding: base64 + * + * [base64 encoded Passpoint profile data] + * --{boundary} + * Content-Type: application/x-x509-ca-cert + * Content-Transfer-Encoding: base64 + * + * [base64 encoded X509 CA certificate data] + * --{boundary} + * Content-Type: application/x-pkcs12 + * Content-Transfer-Encoding: base64 + * + * [base64 encoded PKCS#12 ASN.1 structure containing client certificate chain] + * --{boundary} + * + * @param mimeType MIME type of the encoded data. + * @param data A base64 encoded MIME multipart message containing the Passpoint profile + * (required), CA (Certificate Authority) certificate (optional), and client + * certificate chain (optional). + * @return {@link PasspointConfiguration} + */ + public static PasspointConfiguration buildPasspointConfig(String mimeType, byte[] data) { + // Verify MIME type. + if (!TextUtils.equals(mimeType, TYPE_WIFI_CONFIG)) { + Log.e(TAG, "Unexpected MIME type: " + mimeType); + return null; + } + + try { + // Decode the data. + byte[] decodedData = Base64.decode(new String(data, StandardCharsets.ISO_8859_1), + Base64.DEFAULT); + Map<String, byte[]> mimeParts = parseMimeMultipartMessage(new LineNumberReader( + new InputStreamReader(new ByteArrayInputStream(decodedData), + StandardCharsets.ISO_8859_1))); + return createPasspointConfig(mimeParts); + } catch (IOException | IllegalArgumentException e) { + Log.e(TAG, "Failed to parse installation file: " + e.getMessage()); + return null; + } + } + + /** + * Create a {@link PasspointConfiguration} object from list of MIME (Multipurpose Internet + * Mail Extension) parts. + * + * @param mimeParts Map of content type and content data. + * @return {@link PasspointConfiguration} + * @throws IOException + */ + private static PasspointConfiguration createPasspointConfig(Map<String, byte[]> mimeParts) + throws IOException { + byte[] profileData = mimeParts.get(TYPE_PASSPOINT_PROFILE); + if (profileData == null) { + throw new IOException("Missing Passpoint Profile"); + } + + PasspointConfiguration config = PPSMOParser.parseMOText(new String(profileData)); + if (config == null) { + throw new IOException("Failed to parse Passpoint profile"); + } + + // Credential is needed for storing the certificates and private client key. + if (config.credential == null) { + throw new IOException("Passpoint profile missing credential"); + } + + // Parse CA (Certificate Authority) certificate. + byte[] caCertData = mimeParts.get(TYPE_CA_CERT); + if (caCertData != null) { + try { + config.credential.caCertificate = parseCACert(caCertData); + } catch (CertificateException e) { + throw new IOException("Failed to parse CA Certificate"); + } + } + + // Parse PKCS12 data for client private key and certificate chain. + byte[] pkcs12Data = mimeParts.get(TYPE_PKCS12); + if (pkcs12Data != null) { + try { + Pair<PrivateKey, List<X509Certificate>> clientKey = parsePkcs12(pkcs12Data); + config.credential.clientPrivateKey = clientKey.first; + config.credential.clientCertificateChain = + clientKey.second.toArray(new X509Certificate[clientKey.second.size()]); + } catch(GeneralSecurityException | IOException e) { + throw new IOException("Failed to parse PCKS12 string"); + } + } + return config; + } + + /** + * Parse a MIME (Multipurpose Internet Mail Extension) multipart message from the given + * input stream. + * + * @param in The input stream for reading the message data + * @return A map of a content type and content data pair + * @throws IOException + */ + private static Map<String, byte[]> parseMimeMultipartMessage(LineNumberReader in) + throws IOException { + // Parse the outer MIME header. + MimeHeader header = parseHeaders(in); + if (!TextUtils.equals(header.contentType, TYPE_MULTIPART_MIXED)) { + throw new IOException("Invalid content type: " + header.contentType); + } + if (TextUtils.isEmpty(header.boundary)) { + throw new IOException("Missing boundary string"); + } + if (!TextUtils.equals(header.encodingType, ENCODING_BASE64)) { + throw new IOException("Unexpected encoding: " + header.encodingType); + } + + // Read pass the first boundary string. + for (;;) { + String line = in.readLine(); + if (line == null) { + throw new IOException("Unexpected EOF before first boundary @ " + + in.getLineNumber()); + } + if (line.equals("--" + header.boundary)) { + break; + } + } + + // Parse each MIME part. + Map<String, byte[]> mimeParts = new HashMap<>(); + boolean isLast = false; + do { + MimePart mimePart = parseMimePart(in, header.boundary); + mimeParts.put(mimePart.type, mimePart.data); + isLast = mimePart.isLast; + } while(!isLast); + return mimeParts; + } + + /** + * Parse a MIME (Multipurpose Internet Mail Extension) part. We expect the data to + * be encoded in base64. + * + * @param in Input stream to read the data from + * @param boundary Boundary string indicate the end of the part + * @return {@link MimePart} + * @throws IOException + */ + private static MimePart parseMimePart(LineNumberReader in, String boundary) + throws IOException { + MimeHeader header = parseHeaders(in); + // Expect encoding type to be base64. + if (!TextUtils.equals(header.encodingType, ENCODING_BASE64)) { + throw new IOException("Unexpected encoding type: " + header.encodingType); + } + + // Check for a valid content type. + if (!TextUtils.equals(header.contentType, TYPE_PASSPOINT_PROFILE) && + !TextUtils.equals(header.contentType, TYPE_CA_CERT) && + !TextUtils.equals(header.contentType, TYPE_PKCS12)) { + throw new IOException("Unexpected content type: " + header.contentType); + } + + StringBuilder text = new StringBuilder(); + boolean isLast = false; + String partBoundary = "--" + boundary; + String endBoundary = partBoundary + "--"; + for (;;) { + String line = in.readLine(); + if (line == null) { + throw new IOException("Unexpected EOF file in body @ " + in.getLineNumber()); + } + // Check for boundary line. + if (line.startsWith(partBoundary)) { + if (line.equals(endBoundary)) { + isLast = true; + } + break; + } + text.append(line); + } + + MimePart part = new MimePart(); + part.type = header.contentType; + part.data = Base64.decode(text.toString(), Base64.DEFAULT); + part.isLast = isLast; + return part; + } + + /** + * Parse a MIME (Multipurpose Internet Mail Extension) header from the input stream. + * @param in Input stream to read from. + * @return {@link MimeHeader} + * @throws IOException + */ + private static MimeHeader parseHeaders(LineNumberReader in) + throws IOException { + MimeHeader header = new MimeHeader(); + + // Read the header from the input stream. + Map<String, String> headers = readHeaders(in); + + // Parse each header. + for (Map.Entry<String, String> entry : headers.entrySet()) { + switch (entry.getKey()) { + case CONTENT_TYPE: + Pair<String, String> value = parseContentType(entry.getValue()); + header.contentType = value.first; + header.boundary = value.second; + break; + case CONTENT_TRANSFER_ENCODING: + header.encodingType = entry.getValue(); + break; + default: + throw new IOException("Unexpected header: " + entry.getKey()); + } + } + return header; + } + + /** + * Parse the Content-Type header value. The value will contain the content type string and + * an optional boundary string separated by a ";". Below are examples of valid Content-Type + * header value: + * multipart/mixed; boundary={boundary} + * application/x-passpoint-profile + * + * @param contentType The Content-Type value string + * @return A pair of content type and boundary string + * @throws IOException + */ + private static Pair<String, String> parseContentType(String contentType) throws IOException { + String[] attributes = contentType.toString().split(";"); + String type = null; + String boundary = null; + + if (attributes.length < 1 || attributes.length > 2) { + throw new IOException("Invalid Content-Type: " + contentType); + } + + type = attributes[0].trim(); + if (attributes.length == 2) { + boundary = attributes[1].trim(); + if (!boundary.startsWith(BOUNDARY)) { + throw new IOException("Invalid Content-Type: " + contentType); + } + boundary = boundary.substring(BOUNDARY.length()); + // Remove the leading and trailing quote if present. + if (boundary.length() > 1 && boundary.startsWith("\"") && boundary.endsWith("\"")) { + boundary = boundary.substring(1, boundary.length()-1); + } + } + + return new Pair<String, String>(type, boundary); + } + + /** + * Read the headers from the given input stream. The header section is terminated by + * an empty line. + * + * @param in The input stream to read from + * @return Map of key-value pairs. + * @throws IOException + */ + private static Map<String, String> readHeaders(LineNumberReader in) + throws IOException { + Map<String, String> headers = new HashMap<>(); + String line; + String name = null; + StringBuilder value = null; + for (;;) { + line = in.readLine(); + if (line == null) { + throw new IOException("Missing line @ " + in.getLineNumber()); + } + + // End of headers section. + if (line.length() == 0 || line.trim().length() == 0) { + // Save the previous header line. + if (name != null) { + headers.put(name, value.toString()); + } + break; + } + + int nameEnd = line.indexOf(':'); + if (nameEnd < 0) { + if (value != null) { + // Continuation line for the header value. + value.append(' ').append(line.trim()); + } else { + throw new IOException("Bad header line: '" + line + "' @ " + + in.getLineNumber()); + } + } else { + // New header line detected, make sure it doesn't start with a whitespace. + if (Character.isWhitespace(line.charAt(0))) { + throw new IOException("Illegal blank prefix in header line '" + line + + "' @ " + in.getLineNumber()); + } + + if (name != null) { + // Save the previous header line. + headers.put(name, value.toString()); + } + + // Setup the current header line. + name = line.substring(0, nameEnd).trim(); + value = new StringBuilder(); + value.append(line.substring(nameEnd+1).trim()); + } + } + return headers; + } + + /** + * Parse a CA (Certificate Authority) certificate data and convert it to a + * X509Certificate object. + * + * @param octets Certificate data + * @return X509Certificate + * @throws CertificateException + */ + private static X509Certificate parseCACert(byte[] octets) throws CertificateException { + CertificateFactory factory = CertificateFactory.getInstance("X.509"); + return (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(octets)); + } + + private static Pair<PrivateKey, List<X509Certificate>> parsePkcs12(byte[] octets) + throws GeneralSecurityException, IOException { + KeyStore ks = KeyStore.getInstance("PKCS12"); + ByteArrayInputStream in = new ByteArrayInputStream(octets); + ks.load(in, new char[0]); + in.close(); + + // Only expects one set of key and certificate chain. + if (ks.size() != 1) { + throw new IOException("Unexpected key size: " + ks.size()); + } + + String alias = ks.aliases().nextElement(); + if (alias == null) { + throw new IOException("No alias found"); + } + + PrivateKey clientKey = (PrivateKey) ks.getKey(alias, null); + List<X509Certificate> clientCertificateChain = null; + Certificate[] chain = ks.getCertificateChain(alias); + if (chain != null) { + clientCertificateChain = new ArrayList<>(); + for (Certificate certificate : chain) { + if (!(certificate instanceof X509Certificate)) { + throw new IOException("Unexpceted certificate type: " + + certificate.getClass()); + } + clientCertificateChain.add((X509Certificate) certificate); + } + } + return new Pair<PrivateKey, List<X509Certificate>>(clientKey, clientCertificateChain); + } +}
\ No newline at end of file diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64 new file mode 100644 index 000000000000..8c1eb0867298 --- /dev/null +++ b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64 @@ -0,0 +1,85 @@ +Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu +dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh +cHBsaWNhdGlvbi94LXBhc3Nwb2ludC1wcm9maWxlCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6 +IGJhc2U2NAoKUEUxbmJYUlVjbVZsSUhodGJHNXpQU0p6ZVc1amJXdzZaRzFrWkdZeExqSWlQZ29n +SUR4V1pYSkVWRVErTVM0eVBDOVdaWEpFVkVRKwpDaUFnUEU1dlpHVStDaUFnSUNBOFRtOWtaVTVo +YldVK1VHVnlVSEp2ZG1sa1pYSlRkV0p6WTNKcGNIUnBiMjQ4TDA1dlpHVk9ZVzFsClBnb2dJQ0Fn +UEZKVVVISnZjR1Z5ZEdsbGN6NEtJQ0FnSUNBZ1BGUjVjR1UrQ2lBZ0lDQWdJQ0FnUEVSRVJrNWhi +V1UrZFhKdU9uZG0KWVRwdGJ6cG9iM1J6Y0c5ME1tUnZkREF0Y0dWeWNISnZkbWxrWlhKemRXSnpZ +M0pwY0hScGIyNDZNUzR3UEM5RVJFWk9ZVzFsUGdvZwpJQ0FnSUNBOEwxUjVjR1UrQ2lBZ0lDQThM +MUpVVUhKdmNHVnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVSth +VEF3TVR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFRt +RnRaVDVJYjIxbFUxQTgKTDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lD +QWdJQ0FnUEU1dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdApaVHd2VG05a1pVNWhiV1UrQ2lBZ0lD +QWdJQ0FnSUNBOFZtRnNkV1UrUTJWdWRIVnllU0JJYjNWelpUd3ZWbUZzZFdVK0NpQWdJQ0FnCklD +QWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcx +bFBrWlJSRTQ4TDA1dlpHVk8KWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBtMXBOaTVqYnk1 +MWF6d3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnSUNBZ0lEeE9iMlJsUGdv +Z0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsSnZZVzFwYm1kRGIyNXpiM0owYVhWdFQwazhMMDV2 +ClpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGpFeE1qSXpNeXcwTkRVMU5qWThMMVpo +YkhWbFBnb2dJQ0FnSUNBZ0lEd3YKVG05a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0E4 +VG05a1pUNEtJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStRM0psWkdWdQpkR2xoYkR3dlRtOWtaVTVo +YldVK0NpQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1VtVmhi +RzA4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBuTm9ZV3RsYmk1emRHbHlj +bVZrTG1OdmJUd3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9i +MlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsVnpaWEp1WVcxbApVR0Z6YzNkdmNtUThM +MDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2Iy +UmxUbUZ0ClpUNVZjMlZ5Ym1GdFpUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX +eDFaVDVxWVcxbGN6d3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lD +QWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxCaApjM04zYjNKa1BD +OU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbGx0T1hWYVJFRjNUbmM5UFR3 +dlZtRnNkV1UrCkNpQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0 +S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtWQlVFMWxkR2h2WkR3dlRtOWtaVTVoYldV +K0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0FnSUR4TwpiMlJsVG1G +dFpUNUZRVkJVZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBOFZtRnNkV1Ur +TWpFOEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0Fn +SUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRaVDVKYm01bGNrMWxkR2h2 +WkR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQazFUTFVOSQpRVkF0 +VmpJOEwxWmhiSFZsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThM +MDV2WkdVK0NpQWdJQ0FnCklDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJ +Q0FnSUNBZ1BFNXZaR1ZPWVcxbFBrUnBaMmwwWVd4RFpYSjAKYVdacFkyRjBaVHd2VG05a1pVNWhi +V1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbApQ +a05sY25ScFptbGpZWFJsVkhsd1pUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX +eDFaVDU0TlRBNWRqTThMMVpoCmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lD +QWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEowVTBoQk1q +VTJSbWx1WjJWeVVISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV +KwpNV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpG +bU1XWXhaakZtTVdZeFpqRm1NV1l4ClpqRm1NV1l4Wmp3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNB +OEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWcKSUR4T2IyUmxQZ29nSUNB +Z0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxOSlRUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4 +VG05awpaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrbE5VMGs4TDA1dlpHVk9ZVzFs +UGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzCmRXVSthVzF6YVR3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0Fn +SUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWcKSUNBZ0lDQWdQRTV2 +WkdWT1lXMWxQa1ZCVUZSNWNHVThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBOFZtRnNk +V1UrTWpROApMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEd3ZU +bTlrWlQ0S0lDQWdJQ0FnUEM5T2IyUmxQZ29nCklDQWdQQzlPYjJSbFBnb2dJRHd2VG05a1pUNEtQ +QzlOWjIxMFZISmxaVDRLCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94 +LXg1MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRMUzFD +UlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlowbEtR +VWxNYkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQwSm5U +bFlLUWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYyaGpU +azFxV1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZrRm5V +VEJGZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtOblMw +TkJVVVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIxV1ZY +TldkVzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJOQ01T +dHZSMWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpVClJV +RkRaV1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwyUzJo +U1FqZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dyVUdK +ck1IVjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIzUWtz +M2FFUTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNLMGxh +CmFYQllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFVRzVX +TTJreGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNXVVZH +U1hkWU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJOMDFF +YlVGR1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RWSkJk +MFJuCldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldVUldV +akJVUWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNXaDJZ +MDVCVVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQwaEZD +amxIUlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5VVNY +ZEpWV00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5KT1Ew +OTBhWE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhsT2JX +MVdUQW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhnMmVF +MXVWR3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxXYUhw +aFNFb3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5WnpG +S1VDdHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQTDNS +a1RrNTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJKUmts +RFFWUkZMUzB0TFMwSwotLXtib3VuZGFyeX0tLQo= diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf new file mode 100644 index 000000000000..6d86dd53eb76 --- /dev/null +++ b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf @@ -0,0 +1,73 @@ +Content-Type: multipart/mixed; boundary={boundary} +Content-Transfer-Encoding: base64 + +--{boundary} +Content-Type: application/x-passpoint-profile +Content-Transfer-Encoding: base64 + +PE1nbXRUcmVlIHhtbG5zPSJzeW5jbWw6ZG1kZGYxLjIiPgogIDxWZXJEVEQ+MS4yPC9WZXJEVEQ+ +CiAgPE5vZGU+CiAgICA8Tm9kZU5hbWU+UGVyUHJvdmlkZXJTdWJzY3JpcHRpb248L05vZGVOYW1l +PgogICAgPFJUUHJvcGVydGllcz4KICAgICAgPFR5cGU+CiAgICAgICAgPERERk5hbWU+dXJuOndm +YTptbzpob3RzcG90MmRvdDAtcGVycHJvdmlkZXJzdWJzY3JpcHRpb246MS4wPC9EREZOYW1lPgog +ICAgICA8L1R5cGU+CiAgICA8L1JUUHJvcGVydGllcz4KICAgIDxOb2RlPgogICAgICA8Tm9kZU5h +bWU+aTAwMTwvTm9kZU5hbWU+CiAgICAgIDxOb2RlPgogICAgICAgIDxOb2RlTmFtZT5Ib21lU1A8 +L05vZGVOYW1lPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPkZyaWVuZGx5TmFt +ZTwvTm9kZU5hbWU+CiAgICAgICAgICA8VmFsdWU+Q2VudHVyeSBIb3VzZTwvVmFsdWU+CiAgICAg +ICAgPC9Ob2RlPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPkZRRE48L05vZGVO +YW1lPgogICAgICAgICAgPFZhbHVlPm1pNi5jby51azwvVmFsdWU+CiAgICAgICAgPC9Ob2RlPgog +ICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPlJvYW1pbmdDb25zb3J0aXVtT0k8L05v +ZGVOYW1lPgogICAgICAgICAgPFZhbHVlPjExMjIzMyw0NDU1NjY8L1ZhbHVlPgogICAgICAgIDwv +Tm9kZT4KICAgICAgPC9Ob2RlPgogICAgICA8Tm9kZT4KICAgICAgICA8Tm9kZU5hbWU+Q3JlZGVu +dGlhbDwvTm9kZU5hbWU+CiAgICAgICAgPE5vZGU+CiAgICAgICAgICA8Tm9kZU5hbWU+UmVhbG08 +L05vZGVOYW1lPgogICAgICAgICAgPFZhbHVlPnNoYWtlbi5zdGlycmVkLmNvbTwvVmFsdWU+CiAg +ICAgICAgPC9Ob2RlPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPlVzZXJuYW1l +UGFzc3dvcmQ8L05vZGVOYW1lPgogICAgICAgICAgPE5vZGU+CiAgICAgICAgICAgIDxOb2RlTmFt +ZT5Vc2VybmFtZTwvTm9kZU5hbWU+CiAgICAgICAgICAgIDxWYWx1ZT5qYW1lczwvVmFsdWU+CiAg +ICAgICAgICA8L05vZGU+CiAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAgPE5vZGVOYW1lPlBh +c3N3b3JkPC9Ob2RlTmFtZT4KICAgICAgICAgICAgPFZhbHVlPlltOXVaREF3Tnc9PTwvVmFsdWU+ +CiAgICAgICAgICA8L05vZGU+CiAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAgPE5vZGVOYW1l +PkVBUE1ldGhvZDwvTm9kZU5hbWU+CiAgICAgICAgICAgIDxOb2RlPgogICAgICAgICAgICAgIDxO +b2RlTmFtZT5FQVBUeXBlPC9Ob2RlTmFtZT4KICAgICAgICAgICAgICA8VmFsdWU+MjE8L1ZhbHVl +PgogICAgICAgICAgICA8L05vZGU+CiAgICAgICAgICAgIDxOb2RlPgogICAgICAgICAgICAgIDxO +b2RlTmFtZT5Jbm5lck1ldGhvZDwvTm9kZU5hbWU+CiAgICAgICAgICAgICAgPFZhbHVlPk1TLUNI +QVAtVjI8L1ZhbHVlPgogICAgICAgICAgICA8L05vZGU+CiAgICAgICAgICA8L05vZGU+CiAgICAg +ICAgPC9Ob2RlPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPkRpZ2l0YWxDZXJ0 +aWZpY2F0ZTwvTm9kZU5hbWU+CiAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAgPE5vZGVOYW1l +PkNlcnRpZmljYXRlVHlwZTwvTm9kZU5hbWU+CiAgICAgICAgICAgIDxWYWx1ZT54NTA5djM8L1Zh +bHVlPgogICAgICAgICAgPC9Ob2RlPgogICAgICAgICAgPE5vZGU+CiAgICAgICAgICAgIDxOb2Rl +TmFtZT5DZXJ0U0hBMjU2RmluZ2VyUHJpbnQ8L05vZGVOYW1lPgogICAgICAgICAgICA8VmFsdWU+ +MWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYx +ZjFmMWYxZjwvVmFsdWU+CiAgICAgICAgICA8L05vZGU+CiAgICAgICAgPC9Ob2RlPgogICAgICAg +IDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPlNJTTwvTm9kZU5hbWU+CiAgICAgICAgICA8Tm9k +ZT4KICAgICAgICAgICAgPE5vZGVOYW1lPklNU0k8L05vZGVOYW1lPgogICAgICAgICAgICA8VmFs +dWU+aW1zaTwvVmFsdWU+CiAgICAgICAgICA8L05vZGU+CiAgICAgICAgICA8Tm9kZT4KICAgICAg +ICAgICAgPE5vZGVOYW1lPkVBUFR5cGU8L05vZGVOYW1lPgogICAgICAgICAgICA8VmFsdWU+MjQ8 +L1ZhbHVlPgogICAgICAgICAgPC9Ob2RlPgogICAgICAgIDwvTm9kZT4KICAgICAgPC9Ob2RlPgog +ICAgPC9Ob2RlPgogIDwvTm9kZT4KPC9NZ210VHJlZT4K + +--{boundary} +Content-Type: application/x-x509-ca-cert +Content-Transfer-Encoding: base64 + +LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURLRENDQWhDZ0F3SUJBZ0lKQUlMbEZkd3pM +VnVyTUEwR0NTcUdTSWIzRFFFQkN3VUFNQkl4RURBT0JnTlYKQkFNVEIwVkJVQ0JEUVRFd0hoY05N +VFl3TVRFeU1URTFNREUxV2hjTk1qWXdNVEE1TVRFMU1ERTFXakFTTVJBdwpEZ1lEVlFRREV3ZEZR +VkFnUTBFeE1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBCnpuQVBV +ejI2TXNhZTR3czQzY3pSNDEvSjJRdHJTSVpVS21WVXNWdW1EYllIclBOdlRYS1NNWEFjZXdPUkRR +WVgKUnF2SHZwbjhDc2NCMStvR1hadkh3eGo0elYwV0tvSzJ6ZVhrYXUzdmN5bDNISUt1cEpmcTJU +RUFDZWZWamowdApKVytYMzVQR1dwOS9INXpJVU5WTlZqUzdVbXM4NEl2S2hSQjg1MTJQQjlVeUhh +Z1hZVlg1R1dwQWNWcHlmcmxSCkZJOVFkaGgrUGJrMHV5a3RkYmYvQ2RmZ0hPb2ViclR0d1Jsak0w +b0R0WCsyQ3Y2ajB3Qks3aEQ4cFB2ZjErdXkKR3pjemlnQVUvNEt3N2VacXlkZjlCKzVSdXBSK0la +aXBYNDF4RWlJcktSd3FpNTE3V1d6WGNqYUcyY05iZjQ1MQp4cEg1UG5WM2kxdHEwNGpNR1FVekZ3 +SURBUUFCbzRHQU1INHdIUVlEVlIwT0JCWUVGSXdYNHZzOEJpQmNTY29kCjVub1pIUk04RTQraU1F +SUdBMVVkSXdRN01EbUFGSXdYNHZzOEJpQmNTY29kNW5vWkhSTThFNCtpb1Jha0ZEQVMKTVJBd0Rn +WURWUVFERXdkRlFWQWdRMEV4Z2drQWd1VVYzRE10VzZzd0RBWURWUjBUQkFVd0F3RUIvekFMQmdO +VgpIUThFQkFNQ0FRWXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBRmZRcU9UQTdSdjdLK2x1UTdw +bmFzNEJZd0hFCjlHRVAvdW9odjZLT3kwVEdRRmJyUlRqRm9MVk5COUJaMXltTURaMC9USXdJVWM3 +d2k3YTh0NW1FcVlIMTUzd1cKYVdvb2lTanlMTGh1STRzTnJOQ090aXNkQnEycjJNRlh0NmgwbUFR +WU9QdjhSOEs3L2ZnU3hHRnF6aHlObW1WTAoxcUJKbGR4MzRTcHdzVEFMUVZQYjRoR3dKelpmcjFQ +Y3BFUXg2eE1uVGw4eEVXWkUzTXM5OXVhVXhiUXFJd1J1CkxnQU9rTkNtWTJtODlWaHphSEoxdVY4 +NUFkTS90RCtZc21sbm5qdDlMUkNlamJCaXBqSUdqT1hyZzFKUCtseFYKbXVNNHZIK1AvbWxteHNQ +UHowZDY1YitFR21KWnBvTGtPL3RkTk52Q1l6akpwVEVXcEVzTzZOTWhLWW89Ci0tLS0tRU5EIENF +UlRJRklDQVRFLS0tLS0K +--{boundary}-- diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithInvalidContentType.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithInvalidContentType.base64 new file mode 100644 index 000000000000..906bfb397464 --- /dev/null +++ b/wifi/tests/assets/hsr1/HSR1ProfileWithInvalidContentType.base64 @@ -0,0 +1,85 @@ +Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu +dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh +cHBsaWNhdGlvbi9wYXNzcG9pbnQtcHJvZmlsZQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBi +YXNlNjQKClBFMW5iWFJVY21WbElIaHRiRzV6UFNKemVXNWpiV3c2Wkcxa1pHWXhMaklpUGdvZ0lE +eFdaWEpFVkVRK01TNHlQQzlXWlhKRVZFUSsKQ2lBZ1BFNXZaR1UrQ2lBZ0lDQThUbTlrWlU1aGJX +VStVR1Z5VUhKdmRtbGtaWEpUZFdKelkzSnBjSFJwYjI0OEwwNXZaR1ZPWVcxbApQZ29nSUNBZ1BG +SlVVSEp2Y0dWeWRHbGxjejRLSUNBZ0lDQWdQRlI1Y0dVK0NpQWdJQ0FnSUNBZ1BFUkVSazVoYldV +K2RYSnVPbmRtCllUcHRienBvYjNSemNHOTBNbVJ2ZERBdGNHVnljSEp2ZG1sa1pYSnpkV0p6WTNK +cGNIUnBiMjQ2TVM0d1BDOUVSRVpPWVcxbFBnb2cKSUNBZ0lDQThMMVI1Y0dVK0NpQWdJQ0E4TDFK +VVVISnZjR1Z5ZEdsbGN6NEtJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQThUbTlrWlU1aApiV1UrYVRB +d01Ud3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxUbUZ0 +WlQ1SWIyMWxVMUE4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0Fn +SUNBZ1BFNXZaR1ZPWVcxbFBrWnlhV1Z1Wkd4NVRtRnQKWlR3dlRtOWtaVTVoYldVK0NpQWdJQ0Fn +SUNBZ0lDQThWbUZzZFdVK1EyVnVkSFZ5ZVNCSWIzVnpaVHd2Vm1Gc2RXVStDaUFnSUNBZwpJQ0Fn +UEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQ +a1pSUkU0OEwwNXZaR1ZPCllXMWxQZ29nSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbTFwTmk1amJ5NTFh +end2Vm1Gc2RXVStDaUFnSUNBZ0lDQWdQQzlPYjJSbFBnb2cKSUNBZ0lDQWdJRHhPYjJSbFBnb2dJ +Q0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQbEp2WVcxcGJtZERiMjV6YjNKMGFYVnRUMGs4TDA1dgpa +R1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBqRXhNakl6TXl3ME5EVTFOalk4TDFaaGJI +VmxQZ29nSUNBZ0lDQWdJRHd2ClRtOWtaVDRLSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBOFRt +OWtaVDRLSUNBZ0lDQWdJQ0E4VG05a1pVNWhiV1UrUTNKbFpHVnUKZEdsaGJEd3ZUbTlrWlU1aGJX +VStDaUFnSUNBZ0lDQWdQRTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStVbVZoYkcw +OApMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbk5vWVd0bGJpNXpkR2x5Y21W +a0xtTnZiVHd2Vm1Gc2RXVStDaUFnCklDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJS +bFBnb2dJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQbFZ6WlhKdVlXMWwKVUdGemMzZHZjbVE4TDA1 +dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeE9iMlJs +VG1GdApaVDVWYzJWeWJtRnRaVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeFdZV3gx +WlQ1cVlXMWxjend2Vm1Gc2RXVStDaUFnCklDQWdJQ0FnSUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0Fn +SUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsQmgKYzNOM2IzSmtQQzlP +YjJSbFRtRnRaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGxsdE9YVmFSRUYzVG5jOVBUd3ZW +bUZzZFdVKwpDaUFnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4VG05a1pUNEtJ +Q0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsClBrVkJVRTFsZEdodlpEd3ZUbTlrWlU1aGJXVStD +aUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRa +VDVGUVZCVWVYQmxQQzlPYjJSbFRtRnRaVDRLSUNBZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdVK01q +RThMMVpoYkhWbApQZ29nSUNBZ0lDQWdJQ0FnSUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lE +eE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ0lDQWdJRHhPCmIyUmxUbUZ0WlQ1SmJtNWxjazFsZEdodlpE +d3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGsxVExVTkkKUVZBdFZq +SThMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4TDA1 +dlpHVStDaUFnSUNBZwpJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNB +Z0lDQWdQRTV2WkdWT1lXMWxQa1JwWjJsMFlXeERaWEowCmFXWnBZMkYwWlR3dlRtOWtaVTVoYldV +K0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtO +bGNuUnBabWxqWVhSbFZIbHdaVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeFdZV3gx +WlQ1NE5UQTVkak04TDFaaApiSFZsUGdvZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0Fn +SUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeE9iMlJsClRtRnRaVDVEWlhKMFUwaEJNalUy +Um1sdVoyVnlVSEpwYm5ROEwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdJQ0E4Vm1Gc2RXVSsK +TVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1N +V1l4WmpGbU1XWXhaakZtTVdZeApaakZtTVdZeFpqd3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ0lDQThM +MDV2WkdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnCklEeE9iMlJsUGdvZ0lDQWdJ +Q0FnSUNBZ1BFNXZaR1ZPWVcxbFBsTkpUVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBOFRt +OWsKWlQ0S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQa2xOVTBrOEwwNXZaR1ZPWVcxbFBn +b2dJQ0FnSUNBZ0lDQWdJQ0E4Vm1GcwpkV1UrYVcxemFUd3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ0lD +QThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0FnCklDQWdJQ0FnUEU1dlpH +Vk9ZVzFsUGtWQlVGUjVjR1U4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV +K01qUTgKTDFaaGJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHd2VG05 +a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnUEM5T2IyUmxQZ29nSUR3dlRtOWtaVDRLUEM5 +TloyMTBWSEpsWlQ0SwoKLS17Ym91bmRhcnl9CkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24veC14 +NTA5LWNhLWNlcnQKQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgpMUzB0TFMxQ1JV +ZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VSTFJFTkRRV2hEWjBGM1NVSkJaMGxLUVVs +TWJFWmtkM3BNClZuVnlUVUV3UjBOVGNVZFRTV0l6UkZGRlFrTjNWVUZOUWtsNFJVUkJUMEpuVGxZ +S1FrRk5WRUl3VmtKVlEwSkVVVlJGZDBob1kwNU4KVkZsM1RWUkZlVTFVUlRGTlJFVXhWMmhqVGsx +cVdYZE5WRUUxVFZSRk1VMUVSVEZYYWtGVFRWSkJkd3BFWjFsRVZsRlJSRVYzWkVaUgpWa0ZuVVRC +RmVFMUpTVUpKYWtGT1FtZHJjV2hyYVVjNWR6QkNRVkZGUmtGQlQwTkJVVGhCVFVsSlFrTm5TME5C +VVVWQkNucHVRVkJWCmVqSTJUWE5oWlRSM2N6UXpZM3BTTkRFdlNqSlJkSEpUU1ZwVlMyMVdWWE5X +ZFcxRVlsbEljbEJPZGxSWVMxTk5XRUZqWlhkUFVrUlIKV1ZnS1VuRjJTSFp3YmpoRGMyTkNNU3R2 +UjFoYWRraDNlR28wZWxZd1YwdHZTeko2WlZocllYVXpkbU41YkROSVNVdDFjRXBtY1RKVQpSVUZE +WldaV2Ftb3dkQXBLVnl0WU16VlFSMWR3T1M5SU5YcEpWVTVXVGxacVV6ZFZiWE00TkVsMlMyaFNR +amcxTVRKUVFqbFZlVWhoCloxaFpWbGcxUjFkd1FXTldjSGxtY214U0NrWkpPVkZrYUdnclVHSnJN +SFY1YTNSa1ltWXZRMlJtWjBoUGIyVmljbFIwZDFKc2FrMHcKYjBSMFdDc3lRM1kyYWpCM1FrczNh +RVE0Y0ZCMlpqRXJkWGtLUjNwamVtbG5RVlV2TkV0M04yVmFjWGxrWmpsQ0t6VlNkWEJTSzBsYQph +WEJZTkRGNFJXbEpja3RTZDNGcE5URTNWMWQ2V0dOcVlVY3lZMDVpWmpRMU1RcDRjRWcxVUc1V00y +a3hkSEV3TkdwTlIxRlZla1ozClNVUkJVVUZDYnpSSFFVMUlOSGRJVVZsRVZsSXdUMEpDV1VWR1NY +ZFlOSFp6T0VKcFFtTlRZMjlrQ2pWdWIxcElVazA0UlRRcmFVMUYKU1VkQk1WVmtTWGRSTjAxRWJV +RkdTWGRZTkhaek9FSnBRbU5UWTI5a05XNXZXa2hTVFRoRk5DdHBiMUpoYTBaRVFWTUtUVkpCZDBS +bgpXVVJXVVZGRVJYZGtSbEZXUVdkUk1FVjRaMmRyUVdkMVZWWXpSRTEwVnpaemQwUkJXVVJXVWpC +VVFrRlZkMEYzUlVJdmVrRk1RbWRPClZncElVVGhGUWtGTlEwRlJXWGRFVVZsS1MyOWFTV2gyWTA1 +QlVVVk1RbEZCUkdkblJVSkJSbVpSY1U5VVFUZFNkamRMSzJ4MVVUZHcKYm1Gek5FSlpkMGhGQ2ps +SFJWQXZkVzlvZGpaTFQza3dWRWRSUm1KeVVsUnFSbTlNVms1Q09VSmFNWGx0VFVSYU1DOVVTWGRK +VldNMwpkMmszWVRoME5XMUZjVmxJTVRVemQxY0tZVmR2YjJsVGFubE1UR2gxU1RSelRuSk9RMDkw +YVhOa1FuRXljakpOUmxoME5tZ3diVUZSCldVOVFkamhTT0VzM0wyWm5VM2hIUm5GNmFIbE9iVzFX +VEFveGNVSktiR1I0TXpSVGNIZHpWRUZNVVZaUVlqUm9SM2RLZWxwbWNqRlEKWTNCRlVYZzJlRTF1 +Vkd3NGVFVlhXa1V6VFhNNU9YVmhWWGhpVVhGSmQxSjFDa3huUVU5clRrTnRXVEp0T0RsV2FIcGhT +RW94ZFZZNApOVUZrVFM5MFJDdFpjMjFzYm01cWREbE1Va05sYW1KQ2FYQnFTVWRxVDFoeVp6RktV +Q3RzZUZZS2JYVk5OSFpJSzFBdmJXeHRlSE5RClVIb3daRFkxWWl0RlIyMUtXbkJ2VEd0UEwzUmtU +azUyUTFsNmFrcHdWRVZYY0VWelR6Wk9UV2hMV1c4OUNpMHRMUzB0UlU1RUlFTkYKVWxSSlJrbERR +VlJGTFMwdExTMEsKLS17Ym91bmRhcnl9LS0K diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithMissingBoundary.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithMissingBoundary.base64 new file mode 100644 index 000000000000..3fa97d1cdd76 --- /dev/null +++ b/wifi/tests/assets/hsr1/HSR1ProfileWithMissingBoundary.base64 @@ -0,0 +1,85 @@ +Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu +dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh +cHBsaWNhdGlvbi94LXBhc3Nwb2ludC1wcm9maWxlCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6 +IGJhc2U2NAoKUEUxbmJYUlVjbVZsSUhodGJHNXpQU0p6ZVc1amJXdzZaRzFrWkdZeExqSWlQZ29n +SUR4V1pYSkVWRVErTVM0eVBDOVdaWEpFVkVRKwpDaUFnUEU1dlpHVStDaUFnSUNBOFRtOWtaVTVo +YldVK1VHVnlVSEp2ZG1sa1pYSlRkV0p6WTNKcGNIUnBiMjQ4TDA1dlpHVk9ZVzFsClBnb2dJQ0Fn +UEZKVVVISnZjR1Z5ZEdsbGN6NEtJQ0FnSUNBZ1BGUjVjR1UrQ2lBZ0lDQWdJQ0FnUEVSRVJrNWhi +V1UrZFhKdU9uZG0KWVRwdGJ6cG9iM1J6Y0c5ME1tUnZkREF0Y0dWeWNISnZkbWxrWlhKemRXSnpZ +M0pwY0hScGIyNDZNUzR3UEM5RVJFWk9ZVzFsUGdvZwpJQ0FnSUNBOEwxUjVjR1UrQ2lBZ0lDQThM +MUpVVUhKdmNHVnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVSth +VEF3TVR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFRt +RnRaVDVJYjIxbFUxQTgKTDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lD +QWdJQ0FnUEU1dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdApaVHd2VG05a1pVNWhiV1UrQ2lBZ0lD +QWdJQ0FnSUNBOFZtRnNkV1UrUTJWdWRIVnllU0JJYjNWelpUd3ZWbUZzZFdVK0NpQWdJQ0FnCklD +QWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcx +bFBrWlJSRTQ4TDA1dlpHVk8KWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBtMXBOaTVqYnk1 +MWF6d3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnSUNBZ0lEeE9iMlJsUGdv +Z0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsSnZZVzFwYm1kRGIyNXpiM0owYVhWdFQwazhMMDV2 +ClpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGpFeE1qSXpNeXcwTkRVMU5qWThMMVpo +YkhWbFBnb2dJQ0FnSUNBZ0lEd3YKVG05a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0E4 +VG05a1pUNEtJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStRM0psWkdWdQpkR2xoYkR3dlRtOWtaVTVo +YldVK0NpQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1VtVmhi +RzA4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBuTm9ZV3RsYmk1emRHbHlj +bVZrTG1OdmJUd3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9i +MlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsVnpaWEp1WVcxbApVR0Z6YzNkdmNtUThM +MDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2Iy +UmxUbUZ0ClpUNVZjMlZ5Ym1GdFpUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX +eDFaVDVxWVcxbGN6d3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lD +QWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxCaApjM04zYjNKa1BD +OU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbGx0T1hWYVJFRjNUbmM5UFR3 +dlZtRnNkV1UrCkNpQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0 +S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtWQlVFMWxkR2h2WkR3dlRtOWtaVTVoYldV +K0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0FnSUR4TwpiMlJsVG1G +dFpUNUZRVkJVZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBOFZtRnNkV1Ur +TWpFOEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0Fn +SUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRaVDVKYm01bGNrMWxkR2h2 +WkR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQazFUTFVOSQpRVkF0 +VmpJOEwxWmhiSFZsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThM +MDV2WkdVK0NpQWdJQ0FnCklDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJ +Q0FnSUNBZ1BFNXZaR1ZPWVcxbFBrUnBaMmwwWVd4RFpYSjAKYVdacFkyRjBaVHd2VG05a1pVNWhi +V1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbApQ +a05sY25ScFptbGpZWFJsVkhsd1pUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX +eDFaVDU0TlRBNWRqTThMMVpoCmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lD +QWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEowVTBoQk1q +VTJSbWx1WjJWeVVISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV +KwpNV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpG +bU1XWXhaakZtTVdZeFpqRm1NV1l4ClpqRm1NV1l4Wmp3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNB +OEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWcKSUR4T2IyUmxQZ29nSUNB +Z0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxOSlRUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4 +VG05awpaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrbE5VMGs4TDA1dlpHVk9ZVzFs +UGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzCmRXVSthVzF6YVR3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0Fn +SUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWcKSUNBZ0lDQWdQRTV2 +WkdWT1lXMWxQa1ZCVUZSNWNHVThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBOFZtRnNk +V1UrTWpROApMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEd3ZU +bTlrWlQ0S0lDQWdJQ0FnUEM5T2IyUmxQZ29nCklDQWdQQzlPYjJSbFBnb2dJRHd2VG05a1pUNEtQ +QzlOWjIxMFZISmxaVDRLCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94 +LXg1MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRMUzFD +UlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlowbEtR +VWxNYkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQwSm5U +bFlLUWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYyaGpU +azFxV1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZrRm5V +VEJGZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtOblMw +TkJVVVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIxV1ZY +TldkVzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJOQ01T +dHZSMWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpVClJV +RkRaV1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwyUzJo +U1FqZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dyVUdK +ck1IVjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIzUWtz +M2FFUTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNLMGxh +CmFYQllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFVRzVX +TTJreGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNXVVZH +U1hkWU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJOMDFF +YlVGR1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RWSkJk +MFJuCldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldVUldV +akJVUWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNXaDJZ +MDVCVVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQwaEZD +amxIUlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5VVNY +ZEpWV00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5KT1Ew +OTBhWE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhsT2JX +MVdUQW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhnMmVF +MXVWR3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxXYUhw +aFNFb3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5WnpG +S1VDdHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQTDNS +a1RrNTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJKUmts +RFFWUkZMUzB0TFMwSwo= diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithNonBase64Part.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithNonBase64Part.base64 new file mode 100644 index 000000000000..975f8e539cc3 --- /dev/null +++ b/wifi/tests/assets/hsr1/HSR1ProfileWithNonBase64Part.base64 @@ -0,0 +1,85 @@ +Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu +dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTMyCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh +cHBsaWNhdGlvbi94LXBhc3Nwb2ludC1wcm9maWxlCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6 +IGJhc2U2NAoKUEUxbmJYUlVjbVZsSUhodGJHNXpQU0p6ZVc1amJXdzZaRzFrWkdZeExqSWlQZ29n +SUR4V1pYSkVWRVErTVM0eVBDOVdaWEpFVkVRKwpDaUFnUEU1dlpHVStDaUFnSUNBOFRtOWtaVTVo +YldVK1VHVnlVSEp2ZG1sa1pYSlRkV0p6WTNKcGNIUnBiMjQ4TDA1dlpHVk9ZVzFsClBnb2dJQ0Fn +UEZKVVVISnZjR1Z5ZEdsbGN6NEtJQ0FnSUNBZ1BGUjVjR1UrQ2lBZ0lDQWdJQ0FnUEVSRVJrNWhi +V1UrZFhKdU9uZG0KWVRwdGJ6cG9iM1J6Y0c5ME1tUnZkREF0Y0dWeWNISnZkbWxrWlhKemRXSnpZ +M0pwY0hScGIyNDZNUzR3UEM5RVJFWk9ZVzFsUGdvZwpJQ0FnSUNBOEwxUjVjR1UrQ2lBZ0lDQThM +MUpVVUhKdmNHVnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVSth +VEF3TVR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFRt +RnRaVDVJYjIxbFUxQTgKTDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lD +QWdJQ0FnUEU1dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdApaVHd2VG05a1pVNWhiV1UrQ2lBZ0lD +QWdJQ0FnSUNBOFZtRnNkV1UrUTJWdWRIVnllU0JJYjNWelpUd3ZWbUZzZFdVK0NpQWdJQ0FnCklD +QWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcx +bFBrWlJSRTQ4TDA1dlpHVk8KWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBtMXBOaTVqYnk1 +MWF6d3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnSUNBZ0lEeE9iMlJsUGdv +Z0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsSnZZVzFwYm1kRGIyNXpiM0owYVhWdFQwazhMMDV2 +ClpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGpFeE1qSXpNeXcwTkRVMU5qWThMMVpo +YkhWbFBnb2dJQ0FnSUNBZ0lEd3YKVG05a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0E4 +VG05a1pUNEtJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStRM0psWkdWdQpkR2xoYkR3dlRtOWtaVTVo +YldVK0NpQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1VtVmhi +RzA4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBuTm9ZV3RsYmk1emRHbHlj +bVZrTG1OdmJUd3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9i +MlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsVnpaWEp1WVcxbApVR0Z6YzNkdmNtUThM +MDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2Iy +UmxUbUZ0ClpUNVZjMlZ5Ym1GdFpUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX +eDFaVDVxWVcxbGN6d3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lD +QWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxCaApjM04zYjNKa1BD +OU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbGx0T1hWYVJFRjNUbmM5UFR3 +dlZtRnNkV1UrCkNpQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0 +S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtWQlVFMWxkR2h2WkR3dlRtOWtaVTVoYldV +K0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0FnSUR4TwpiMlJsVG1G +dFpUNUZRVkJVZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBOFZtRnNkV1Ur +TWpFOEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0Fn +SUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRaVDVKYm01bGNrMWxkR2h2 +WkR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQazFUTFVOSQpRVkF0 +VmpJOEwxWmhiSFZsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThM +MDV2WkdVK0NpQWdJQ0FnCklDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJ +Q0FnSUNBZ1BFNXZaR1ZPWVcxbFBrUnBaMmwwWVd4RFpYSjAKYVdacFkyRjBaVHd2VG05a1pVNWhi +V1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbApQ +a05sY25ScFptbGpZWFJsVkhsd1pUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX +eDFaVDU0TlRBNWRqTThMMVpoCmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lD +QWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEowVTBoQk1q +VTJSbWx1WjJWeVVISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV +KwpNV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpG +bU1XWXhaakZtTVdZeFpqRm1NV1l4ClpqRm1NV1l4Wmp3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNB +OEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWcKSUR4T2IyUmxQZ29nSUNB +Z0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxOSlRUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4 +VG05awpaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrbE5VMGs4TDA1dlpHVk9ZVzFs +UGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzCmRXVSthVzF6YVR3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0Fn +SUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWcKSUNBZ0lDQWdQRTV2 +WkdWT1lXMWxQa1ZCVUZSNWNHVThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBOFZtRnNk +V1UrTWpROApMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEd3ZU +bTlrWlQ0S0lDQWdJQ0FnUEM5T2IyUmxQZ29nCklDQWdQQzlPYjJSbFBnb2dJRHd2VG05a1pUNEtQ +QzlOWjIxMFZISmxaVDRLCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94 +LXg1MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRMUzFD +UlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlowbEtR +VWxNYkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQwSm5U +bFlLUWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYyaGpU +azFxV1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZrRm5V +VEJGZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtOblMw +TkJVVVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIxV1ZY +TldkVzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJOQ01T +dHZSMWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpVClJV +RkRaV1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwyUzJo +U1FqZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dyVUdK +ck1IVjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIzUWtz +M2FFUTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNLMGxh +CmFYQllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFVRzVX +TTJreGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNXVVZH +U1hkWU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJOMDFF +YlVGR1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RWSkJk +MFJuCldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldVUldV +akJVUWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNXaDJZ +MDVCVVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQwaEZD +amxIUlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5VVNY +ZEpWV00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5KT1Ew +OTBhWE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhsT2JX +MVdUQW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhnMmVF +MXVWR3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxXYUhw +aFNFb3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5WnpG +S1VDdHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQTDNS +a1RrNTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJKUmts +RFFWUkZMUzB0TFMwSwotLXtib3VuZGFyeX0tLQo= diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithoutProfile.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithoutProfile.base64 new file mode 100644 index 000000000000..833c52751b95 --- /dev/null +++ b/wifi/tests/assets/hsr1/HSR1ProfileWithoutProfile.base64 @@ -0,0 +1,31 @@ +Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu +dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh +cHBsaWNhdGlvbi94LXg1MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNl +NjQKCkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERa +MEYzU1VKQlowbEtRVWxNYkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5R +a2w0UlVSQlQwSm5UbFlLUWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVS +VEZOUkVVeFYyaGpUazFxV1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJF +VjNaRVpSClZrRm5VVEJGZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVU +aEJUVWxKUWtOblMwTkJVVVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRI +SlRTVnBWUzIxV1ZYTldkVzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNI +WndiamhEYzJOQ01TdHZSMWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0 +MWNFcG1jVEpVClJVRkRaV1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpk +VmJYTTRORWwyUzJoU1FqZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2ta +Sk9WRmthR2dyVUdKck1IVjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0Nz +eVEzWTJhakIzUWtzM2FFUTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxD +S3pWU2RYQlNLMGxhCmFYQllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalEx +TVFwNGNFZzFVRzVXTTJreGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxF +VmxJd1QwSkNXVVZHU1hkWU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRC +TVZWa1NYZFJOMDFFYlVGR1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhh +MFpFUVZNS1RWSkJkMFJuCldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBW +elp6ZDBSQldVUldVakJVUWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVV +VmxLUzI5YVNXaDJZMDVCVVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpi +bUZ6TkVKWmQwaEZDamxIUlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1Y +bHRUVVJhTUM5VVNYZEpWV00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRH +aDFTVFJ6VG5KT1EwOTBhWE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUz +aEhSbkY2YUhsT2JXMVdUQW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pG +UQpZM0JGVVhnMmVFMXVWR3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtO +dFdUSnRPRGxXYUhwaFNFb3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhC +cVNVZHFUMWh5WnpGS1VDdHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIx +S1duQnZUR3RQTDNSa1RrNTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVF +SUVORgpVbFJKUmtsRFFWUkZMUzB0TFMwSwotLXtib3VuZGFyeX0tLQo= diff --git a/wifi/tests/assets/hsr1/README.txt b/wifi/tests/assets/hsr1/README.txt new file mode 100644 index 000000000000..d1f8384fc979 --- /dev/null +++ b/wifi/tests/assets/hsr1/README.txt @@ -0,0 +1,5 @@ +HSR1ProfileWithCACert.conf - unencoded installation file that contains a Passpoint profile and a CA Certificate +HSR1ProfileWithCACert.base64 - base64 encoded of the data contained in HSR1ProfileWithCAWith.conf +HSR1ProfileWithNonBase64Part.base64 - base64 encoded installation file that contains a part of non-base64 encoding type +HSR1ProfileWithMissingBoundary.base64 - base64 encoded installation file with missing end-boundary in the MIME data +HSR1ProfileWithInvalidContentType.base64 - base64 encoded installation file with that contains a MIME part with an invalid content type. diff --git a/wifi/tests/src/android/net/wifi/hotspot2/ConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/hotspot2/ConfigBuilderTest.java new file mode 100644 index 000000000000..6095929758f0 --- /dev/null +++ b/wifi/tests/src/android/net/wifi/hotspot2/ConfigBuilderTest.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2016 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.hotspot2; + +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.net.wifi.FakeKeys; +import android.net.wifi.hotspot2.pps.Credential; +import android.net.wifi.hotspot2.pps.HomeSP; +import android.test.suitebuilder.annotation.SmallTest; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Arrays; + +import org.junit.Test; + +/** + * Unit tests for {@link android.net.wifi.hotspot2.ConfigBuilder}. + */ +@SmallTest +public class ConfigBuilderTest { + /** + * Hotspot 2.0 Release 1 installation file that contains a Passpoint profile and a + * CA (Certificate Authority) X.509 certificate {@link FakeKeys#CA_CERT0}. + */ + private static final String PASSPOINT_INSTALLATION_FILE_WITH_CA_CERT = + "assets/hsr1/HSR1ProfileWithCACert.base64"; + private static final String PASSPOINT_INSTALLATION_FILE_WITH_UNENCODED_DATA = + "assets/hsr1/HSR1ProfileWithCACert.conf"; + private static final String PASSPOINT_INSTALLATION_FILE_WITH_INVALID_PART = + "assets/hsr1/HSR1ProfileWithNonBase64Part.base64"; + private static final String PASSPOINT_INSTALLATION_FILE_WITH_MISSING_BOUNDARY = + "assets/hsr1/HSR1ProfileWithMissingBoundary.base64"; + private static final String PASSPOINT_INSTALLATION_FILE_WITH_INVALID_CONTENT_TYPE = + "assets/hsr1/HSR1ProfileWithInvalidContentType.base64"; + private static final String PASSPOINT_INSTALLATION_FILE_WITHOUT_PROFILE = + "assets/hsr1/HSR1ProfileWithoutProfile.base64"; + + /** + * Read the content of the given resource file into a String. + * + * @param filename String name of the file + * @return String + * @throws IOException + */ + private String loadResourceFile(String filename) throws IOException { + InputStream in = getClass().getClassLoader().getResourceAsStream(filename); + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + StringBuilder builder = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + builder.append(line).append("\n"); + } + + return builder.toString(); + } + + /** + * Generate a {@link PasspointConfiguration} that matches the configuration specified in the + * XML file {@link #PASSPOINT_INSTALLATION_FILE_WITH_CA_CERT}. + * + * @return {@link PasspointConfiguration} + */ + private PasspointConfiguration generateConfigurationFromProfile() { + PasspointConfiguration config = new PasspointConfiguration(); + + // HomeSP configuration. + config.homeSp = new HomeSP(); + config.homeSp.friendlyName = "Century House"; + config.homeSp.fqdn = "mi6.co.uk"; + config.homeSp.roamingConsortiumOIs = new long[] {0x112233L, 0x445566L}; + + // Credential configuration. + config.credential = new Credential(); + config.credential.realm = "shaken.stirred.com"; + config.credential.userCredential = new Credential.UserCredential(); + config.credential.userCredential.username = "james"; + config.credential.userCredential.password = "Ym9uZDAwNw=="; + config.credential.userCredential.eapType = 21; + config.credential.userCredential.nonEapInnerMethod = "MS-CHAP-V2"; + config.credential.certCredential = new Credential.CertificateCredential(); + config.credential.certCredential.certType = "x509v3"; + config.credential.certCredential.certSha256FingerPrint = new byte[32]; + Arrays.fill(config.credential.certCredential.certSha256FingerPrint, (byte)0x1f); + config.credential.simCredential = new Credential.SimCredential(); + config.credential.simCredential.imsi = "imsi"; + config.credential.simCredential.eapType = 24; + config.credential.caCertificate = FakeKeys.CA_CERT0; + return config; + } + + /** + * Verify a valid installation file is parsed successfully with the matching contents. + * + * @throws Exception + */ + @Test + public void parseConfigFile() throws Exception { + String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_CA_CERT); + PasspointConfiguration expectedConfig = generateConfigurationFromProfile(); + PasspointConfiguration actualConfig = + ConfigBuilder.buildPasspointConfig( + "application/x-wifi-config", configStr.getBytes()); + assertTrue(actualConfig.equals(expectedConfig)); + } + + /** + * Verify that parsing an installation file with invalid MIME type will fail. + * + * @throws Exception + */ + @Test + public void parseConfigFileWithInvalidMimeType() throws Exception { + String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_CA_CERT); + assertNull(ConfigBuilder.buildPasspointConfig( + "application/wifi-config", configStr.getBytes())); + } + + /** + * Verify that parsing an un-encoded installation file will fail. + * + * @throws Exception + */ + @Test + public void parseConfigFileWithUnencodedData() throws Exception { + String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_UNENCODED_DATA); + assertNull(ConfigBuilder.buildPasspointConfig( + "application/x-wifi-config", configStr.getBytes())); + } + + /** + * Verify that parsing an installation file that contains a non-base64 part will fail. + * + * @throws Exception + */ + @Test + public void parseConfigFileWithInvalidPart() throws Exception { + String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_INVALID_PART); + assertNull(ConfigBuilder.buildPasspointConfig( + "application/x-wifi-config", configStr.getBytes())); + } + + /** + * Verify that parsing an installation file that contains a missing boundary string will fail. + * + * @throws Exception + */ + @Test + public void parseConfigFileWithMissingBoundary() throws Exception { + String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_MISSING_BOUNDARY); + assertNull(ConfigBuilder.buildPasspointConfig( + "application/x-wifi-config", configStr.getBytes())); + } + + /** + * Verify that parsing an installation file that contains a MIME part with an invalid content + * type will fail. + * + * @throws Exception + */ + @Test + public void parseConfigFileWithInvalidContentType() throws Exception { + String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_INVALID_CONTENT_TYPE); + assertNull(ConfigBuilder.buildPasspointConfig( + "application/x-wifi-config", configStr.getBytes())); + } + + /** + * Verify that parsing an installation file that doesn't contain a Passpoint profile will fail. + * + * @throws Exception + */ + @Test + public void parseConfigFileWithoutPasspointProfile() throws Exception { + String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITHOUT_PROFILE); + assertNull(ConfigBuilder.buildPasspointConfig( + "application/x-wifi-config", configStr.getBytes())); + } +}
\ No newline at end of file |