Merge "health: import android.hardware.health@1.0 constants into BatteryManager"
diff --git a/Android.mk b/Android.mk
index 32ebe5b..9ff8d41 100644
--- a/Android.mk
+++ b/Android.mk
@@ -251,6 +251,8 @@
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 f89488d..df8a0eb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -18,6 +18,7 @@
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 @@
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 @@
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 @@
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 @@
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.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.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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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
@@ -36666,6 +36730,7 @@
field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1
field public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 16; // 0x10
field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
+ field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 1024; // 0x400
field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
field public static final int CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE = 256; // 0x100
field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
@@ -37534,6 +37599,7 @@
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 +37622,7 @@
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 +40577,45 @@
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 baac810..c6d08e4 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -28,6 +28,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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
@@ -39723,6 +39742,7 @@
field public static final int CAPABILITY_MULTI_USER = 32; // 0x20
field public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 16; // 0x10
field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
+ field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 1024; // 0x400
field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
field public static final int CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE = 256; // 0x100
field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
@@ -40681,6 +40701,7 @@
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 +40739,7 @@
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 +43744,45 @@
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 f7f5e50..0287612 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -18,6 +18,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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.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.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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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
@@ -36755,6 +36820,7 @@
field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1
field public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 16; // 0x10
field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
+ field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 1024; // 0x400
field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
field public static final int CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE = 256; // 0x100
field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
@@ -37623,6 +37689,7 @@
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 +37712,7 @@
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 +40670,45 @@
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/cmds/locksettings/Android.mk b/cmds/locksettings/Android.mk
new file mode 100644
index 0000000..76766c7
--- /dev/null
+++ b/cmds/locksettings/Android.mk
@@ -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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_MODULE := locksettings
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := locksettings
+LOCAL_SRC_FILES := locksettings
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_PREBUILT)
+
+
diff --git a/cmds/locksettings/locksettings b/cmds/locksettings/locksettings
new file mode 100755
index 0000000..c963b23
--- /dev/null
+++ b/cmds/locksettings/locksettings
@@ -0,0 +1,5 @@
+# Script to start "locksettings" on the device
+#
+base=/system
+export CLASSPATH=$base/framework/locksettings.jar
+exec app_process $base/bin com.android.commands.locksettings.LockSettingsCmd "$@"
diff --git a/cmds/locksettings/src/com/android/commands/locksettings/LockSettingsCmd.java b/cmds/locksettings/src/com/android/commands/locksettings/LockSettingsCmd.java
new file mode 100644
index 0000000..1e426d6
--- /dev/null
+++ b/cmds/locksettings/src/com/android/commands/locksettings/LockSettingsCmd.java
@@ -0,0 +1,65 @@
+/*
+ * 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.commands.locksettings;
+
+import android.os.ResultReceiver;
+import android.os.ServiceManager;
+import android.os.ShellCallback;
+
+import com.android.internal.os.BaseCommand;
+import com.android.internal.widget.ILockSettings;
+
+import java.io.FileDescriptor;
+import java.io.PrintStream;
+
+public final class LockSettingsCmd extends BaseCommand {
+
+ private static final String USAGE =
+ "usage: locksettings set-pattern [--old OLD_CREDENTIAL] NEW_PATTERN\n" +
+ " locksettings set-pin [--old OLD_CREDENTIAL] NEW_PIN\n" +
+ " locksettings set-password [--old OLD_CREDENTIAL] NEW_PASSWORD\n" +
+ " locksettings clear [--old OLD_CREDENTIAL]\n" +
+ "\n" +
+ "locksettings set-pattern: sets a pattern\n" +
+ " A pattern is specified by a non-separated list of numbers that index the cell\n" +
+ " on the pattern in a 1-based manner in left to right and top to bottom order,\n" +
+ " i.e. the top-left cell is indexed with 1, whereas the bottom-right cell\n" +
+ " is indexed with 9. Example: 1234\n" +
+ "\n" +
+ "locksettings set-pin: sets a PIN\n" +
+ "\n" +
+ "locksettings set-password: sets a password\n" +
+ "\n" +
+ "locksettings clear: clears the unlock credential\n";
+
+ public static void main(String[] args) {
+ (new LockSettingsCmd()).run(args);
+ }
+
+ @Override
+ public void onShowUsage(PrintStream out) {
+ out.println(USAGE);
+ }
+
+ @Override
+ public void onRun() throws Exception {
+ ILockSettings lockSettings = ILockSettings.Stub.asInterface(
+ ServiceManager.getService("lock_settings"));
+ lockSettings.asBinder().shellCommand(FileDescriptor.in, FileDescriptor.out,
+ FileDescriptor.err, getRawArgs(), new ShellCallback(), new ResultReceiver(null) {});
+ }
+}
diff --git a/compiled-classes-phone b/compiled-classes-phone
index 8428e41..33e3e04 100644
--- a/compiled-classes-phone
+++ b/compiled-classes-phone
@@ -3610,9 +3610,9 @@
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 0552d34..58f5a78 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -1494,22 +1494,22 @@
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 b3e2f57..b381339 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1929,6 +1929,32 @@
}
/**
+ * 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 df970a4..4a39e4a 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -102,7 +102,7 @@
}
@Override
- public void onUidGone(int uid) {
+ public void onUidGone(int uid, boolean disabled) {
mListener.onUidImportance(uid, RunningAppProcessInfo.IMPORTANCE_GONE);
}
@@ -111,7 +111,7 @@
}
@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 e56fe0f..38e3572 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -194,4 +194,9 @@
* @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 3e8d90b..a3414f4 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -80,6 +80,11 @@
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 @@
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 @@
retHolder = prc.holder;
}
}
-
return retHolder;
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 80267205..9fe34c0 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -499,7 +499,7 @@
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 @@
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 ee81c58..15b99c6 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -94,8 +94,8 @@
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 cae54b6..848464c 100644
--- a/core/java/android/app/IUiModeManager.aidl
+++ b/core/java/android/app/IUiModeManager.aidl
@@ -53,6 +53,16 @@
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 fa8d0c9..64cb9b1 100644
--- a/core/java/android/app/IUidObserver.aidl
+++ b/core/java/android/app/IUidObserver.aidl
@@ -26,7 +26,7 @@
/**
* 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 @@
* 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 2d15beb..45831a3 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -826,7 +826,8 @@
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 @@
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 0046b0e..2e21729 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -241,6 +241,35 @@
}
/**
+ * 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 353c640..3de159a 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -25,6 +25,7 @@
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 @@
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 74302f2..9dfc4b4 100755
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -20,6 +20,7 @@
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 @@
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 a395aa4..0261b1b 100644
--- a/core/java/android/bluetooth/BluetoothAvrcpController.java
+++ b/core/java/android/bluetooth/BluetoothAvrcpController.java
@@ -22,6 +22,7 @@
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 @@
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 f46a3b3..28421eb 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -20,6 +20,7 @@
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 @@
@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 93790fe..c7c64c4 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -20,6 +20,7 @@
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 @@
@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 4949c24..8d77888 100644
--- a/core/java/android/bluetooth/BluetoothHealth.java
+++ b/core/java/android/bluetooth/BluetoothHealth.java
@@ -20,6 +20,7 @@
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 @@
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 252e3d2..e3288f3 100644
--- a/core/java/android/bluetooth/BluetoothInputDevice.java
+++ b/core/java/android/bluetooth/BluetoothInputDevice.java
@@ -22,6 +22,7 @@
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 @@
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 7f57acf..2e73051 100644
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ b/core/java/android/bluetooth/BluetoothMap.java
@@ -371,7 +371,7 @@
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 744f942..2a026a9 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -22,6 +22,7 @@
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 @@
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 eab4c6f..9f00e1a 100644
--- a/core/java/android/bluetooth/BluetoothPbapClient.java
+++ b/core/java/android/bluetooth/BluetoothPbapClient.java
@@ -23,6 +23,7 @@
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 @@
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 e70c936..89c1bf8 100644
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -24,6 +24,7 @@
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 @@
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 6ad442b..dde0ac6 100755
--- a/core/java/android/bluetooth/IBluetoothHeadset.aidl
+++ b/core/java/android/bluetooth/IBluetoothHeadset.aidl
@@ -54,7 +54,7 @@
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 96c59e2..541583f 100755
--- a/core/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl
+++ b/core/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl
@@ -24,7 +24,7 @@
*
* {@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 feccdce..0da4e88 100644
--- a/core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl
@@ -21,7 +21,7 @@
*
* {@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 f53ca94..9e87230 100644
--- a/core/java/android/bluetooth/OobData.java
+++ b/core/java/android/bluetooth/OobData.java
@@ -30,10 +30,24 @@
* @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 OobData() { }
private OobData(Parcel in) {
+ leBluetoothDeviceAddress = in.createByteArray();
securityManagerTk = in.createByteArray();
leSecureConnectionsConfirmation = in.createByteArray();
leSecureConnectionsRandom = in.createByteArray();
@@ -77,6 +92,7 @@
@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 4769bd0..439e1ff 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -422,6 +422,7 @@
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 322cc7b..3964e0a 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3338,6 +3338,14 @@
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 1f013ae..2aa7ac6 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -162,6 +162,12 @@
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 d2c3d75..3fe645c 100644
--- a/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl
+++ b/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl
@@ -25,7 +25,7 @@
*
* @hide
*/
-interface IActivityRecognitionHardwareClient {
+oneway interface IActivityRecognitionHardwareClient {
/**
* Hardware Activity-Recognition availability event.
*
diff --git a/core/java/android/net/metrics/IpManagerEvent.java b/core/java/android/net/metrics/IpManagerEvent.java
index a5b4eb5..e0a026e 100644
--- a/core/java/android/net/metrics/IpManagerEvent.java
+++ b/core/java/android/net/metrics/IpManagerEvent.java
@@ -38,9 +38,15 @@
public static final int PROVISIONING_OK = 1;
public static final int PROVISIONING_FAIL = 2;
public static final int COMPLETE_LIFECYCLE = 3;
+ /** @hide */ public static final int ERROR_STARTING_IPV4 = 4;
+ /** @hide */ public static final int ERROR_STARTING_IPV6 = 5;
+ /** @hide */ public static final int ERROR_STARTING_IPREACHABILITYMONITOR = 6;
/** {@hide} */
- @IntDef(value = {PROVISIONING_OK, PROVISIONING_FAIL, COMPLETE_LIFECYCLE})
+ @IntDef(value = {
+ PROVISIONING_OK, PROVISIONING_FAIL, COMPLETE_LIFECYCLE,
+ ERROR_STARTING_IPV4, ERROR_STARTING_IPV6, ERROR_STARTING_IPREACHABILITYMONITOR,
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface EventType {}
@@ -95,6 +101,7 @@
final static class Decoder {
static final SparseArray<String> constants = MessageUtils.findMessageNames(
- new Class[]{IpManagerEvent.class}, new String[]{"PROVISIONING_", "COMPLETE_"});
+ new Class[]{IpManagerEvent.class},
+ new String[]{"PROVISIONING_", "COMPLETE_", "ERROR_"});
}
}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 7b7533b..0136979 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -75,36 +75,35 @@
/**
* 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 @@
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 @@
* re-enabled.
*/
public static void setDumpDisabled(String msg) {
- synchronized (Binder.class) {
- sDumpDisabled = msg;
- }
+ sDumpDisabled = msg;
}
/**
@@ -400,10 +450,7 @@
}
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 @@
}
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 @@
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 640105c..e11494d 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -36,7 +36,8 @@
}
// Find the service manager
- sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
+ sServiceManager = ServiceManagerNative
+ .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
return sServiceManager;
}
@@ -52,7 +53,7 @@
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 @@
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 b826584..a280e59 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -41,6 +41,8 @@
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 22336c6..79b42ba 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4715,6 +4715,13 @@
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 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 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 0000000..14ce009b
--- /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 0000000..fe21615
--- /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 0000000..2c06234
--- /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 0000000..73d8d5d
--- /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 4a956c6..4b272e9 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -22,11 +22,8 @@
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 8c35901..f639c0d 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -28,7 +28,7 @@
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 @@
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 0000000..4e00c64
--- /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 e606ebf..45011eb 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -77,6 +77,7 @@
* </p>
*/
public abstract class NotificationListenerService extends Service {
+
// TAG = "NotificationListenerService[MySubclass]"
private final String TAG = NotificationListenerService.class.getSimpleName()
+ "[" + getClass().getSimpleName() + "]";
@@ -146,6 +147,48 @@
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 @@
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 @@
@Override
public void onNotificationRemoved(IStatusBarNotificationHolder sbnHolder,
- NotificationRankingUpdate update) {
+ NotificationRankingUpdate update, int reason) {
StatusBarNotification sbn;
try {
sbn = sbnHolder.get();
@@ -941,6 +1010,7 @@
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 @@
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 @@
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 928d5d8..0000000
--- 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 be0b47c..dfb6b86 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 @@
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 @@
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 @@
this.postTime = postTime;
this.key = key();
this.groupKey = groupKey();
+ this.channel = null;
}
public StatusBarNotification(Parcel in) {
@@ -113,6 +112,7 @@
}
this.key = key();
this.groupKey = groupKey();
+ this.channel = NotificationChannel.CREATOR.createFromParcel(in);
}
private String key() {
@@ -182,6 +182,7 @@
} else {
out.writeInt(0);
}
+ this.channel.writeToParcel(out, flags);
}
public int describeContents() {
@@ -208,14 +209,14 @@
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 @@
}
/**
+ * 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 f4eb132..08fb948 100644
--- a/core/java/android/util/Half.java
+++ b/core/java/android/util/Half.java
@@ -27,20 +27,64 @@
* <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 @@
/**
* 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 @@
/**
* 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 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 @@
}
/**
- * 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 @@
* 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 @@
* @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 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 @@
}
}
- 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 @@
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 @@
// 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 @@
* <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 @@
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 @@
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 a81eef8..d59be02 100644
--- a/core/java/android/view/IPinnedStackController.aidl
+++ b/core/java/android/view/IPinnedStackController.aidl
@@ -32,6 +32,11 @@
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 ec852e8..85d10f1 100644
--- a/core/java/android/view/ViewStub.java
+++ b/core/java/android/view/ViewStub.java
@@ -142,11 +142,17 @@
* @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 @@
* @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 @@
* @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 @@
}
}
+ /** @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 @@
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);
+ final View view = inflateViewNoAdd(parent);
+ replaceSelfWithView(view, parent);
- 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);
-
+ mInflatedViewRef = new WeakReference<>(view);
if (mInflateListener != null) {
mInflateListener.onInflate(this, view);
}
@@ -317,4 +345,18 @@
*/
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 8a9bb33..4195d91 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -509,6 +509,11 @@
* 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 b39705e..7104a28 100644
--- a/core/java/android/view/inputmethod/InputContentInfo.java
+++ b/core/java/android/view/inputmethod/InputContentInfo.java
@@ -18,11 +18,14 @@
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 @@
*/
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 @@
@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 @@
* @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 @@
@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 @@
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 b2a77d0..18ce260 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -58,6 +58,7 @@
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 @@
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 @@
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 @@
@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 @@
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 @@
* and can be searched.
*/
private static class ViewTree {
- private final View mRoot;
+ private View mRoot;
private ArrayList<ViewTree> mChildren;
@@ -3643,7 +3659,7 @@
}
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 @@
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 @@
}
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 @@
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/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 3b6073a..d8f7907 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -70,7 +70,7 @@
import com.android.internal.R;
import com.android.internal.app.ResolverActivity.TargetInfo;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.google.android.collect.Lists;
import java.io.File;
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 1e26c92..72a40b7 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -71,7 +71,7 @@
import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.widget.ResolverDrawerLayout;
import java.util.ArrayList;
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index ef725da..a94f308 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -19,7 +19,7 @@
import android.os.Build;
import android.view.View;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
/**
* Log all the things.
diff --git a/core/java/com/android/internal/os/AppFuseMount.java b/core/java/com/android/internal/os/AppFuseMount.java
new file mode 100644
index 0000000..b392186
--- /dev/null
+++ b/core/java/com/android/internal/os/AppFuseMount.java
@@ -0,0 +1,56 @@
+/*
+ * 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.internal.os;
+
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+import java.io.File;
+
+public class AppFuseMount implements Parcelable {
+ final public File mountPoint;
+ final public ParcelFileDescriptor fd;
+
+ public AppFuseMount(File mountPoint, ParcelFileDescriptor fd) {
+ this.mountPoint = mountPoint;
+ this.fd = fd;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(this.mountPoint.getPath());
+ dest.writeParcelable(fd, flags);
+ }
+
+ public static final Parcelable.Creator<AppFuseMount> CREATOR =
+ new Parcelable.Creator<AppFuseMount>() {
+ @Override
+ public AppFuseMount createFromParcel(Parcel in) {
+ return new AppFuseMount(new File(in.readString()), in.readParcelable(null));
+ }
+
+ @Override
+ public AppFuseMount[] newArray(int size) {
+ return new AppFuseMount[size];
+ }
+ };
+}
diff --git a/core/java/com/android/internal/os/TransferPipe.java b/core/java/com/android/internal/os/TransferPipe.java
index e76b395..f904150 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 @@
/**
* 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 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 @@
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 @@
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 @@
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 @@
}
}
+ @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 bed7c1ba..eb75bd4 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.Intent;
import android.net.Uri;
import android.os.Build;
+import android.os.UserHandle;
import android.provider.Settings;
/**
@@ -72,7 +73,7 @@
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 cbacf26..1e2a53b 100644
--- a/core/java/com/android/internal/policy/PipSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
@@ -208,15 +208,19 @@
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 7387b29..886c0e6 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -267,17 +267,17 @@
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 @@
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 bba3d76..38078c1 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -319,12 +319,6 @@
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 @@
// 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 @@
}
} 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 4eebea6..0f7b5a5 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 18bfd4d..1456976 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 89691e9..8f0350a 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 92426c6..37feff8 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 d207c2b..7005afe 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 07c4c69..6c0dc35 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 19d0a73..6809508 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 0000000..a265d2e
--- /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 0000000..d532749
--- /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 7ba46be..b6b0e68 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.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.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 @@
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 4fc7892..5e59a64 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -26,6 +26,7 @@
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 @@
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 7c97e77..5e21dfc 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -738,7 +738,7 @@
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 a204d5c..6973209 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -215,7 +215,7 @@
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 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 8870d2a..e86ad1a 100644
--- a/location/java/android/location/IFusedProvider.aidl
+++ b/location/java/android/location/IFusedProvider.aidl
@@ -22,11 +22,11 @@
* 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 5a5fdc6..d4ff0dd 100644
--- a/location/java/android/location/IGeofenceProvider.aidl
+++ b/location/java/android/location/IGeofenceProvider.aidl
@@ -23,6 +23,6 @@
*
* {@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 4b1e39f..5f6686a 100644
--- a/media/java/android/media/IRingtonePlayer.aidl
+++ b/media/java/android/media/IRingtonePlayer.aidl
@@ -26,14 +26,14 @@
*/
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 c2ca998..f442219 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 b77ff10..531e517 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 2feb51f..0000000
--- 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/Keyguard/src/com/android/keyguard/EmergencyButton.java b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
index 0474df7..d12417b 100644
--- a/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
@@ -34,7 +34,7 @@
import android.widget.Button;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.telephony.IccCardConstants.State;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.policy.EmergencyAffordanceManager;
diff --git a/packages/PrintSpooler/res/values-bs-rBA/strings.xml b/packages/PrintSpooler/res/values-bs-rBA/strings.xml
index a50391f..2450be3 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/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java
index 1bebebc..7b0a291 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java
@@ -48,7 +48,7 @@
import android.widget.ImageView;
import android.widget.TextView;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.printspooler.R;
import java.text.Collator;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 23c6615..4b51917 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -88,7 +88,7 @@
import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.printspooler.R;
import com.android.printspooler.model.MutexFileProvider;
import com.android.printspooler.model.PrintSpoolerProvider;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
index 74582f3..6f0caa2 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
@@ -63,7 +63,7 @@
import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.printspooler.R;
import java.util.ArrayList;
diff --git a/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java b/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
index 381f903..c3acf0b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
@@ -35,7 +35,7 @@
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import java.net.URISyntaxException;
import java.util.Locale;
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 227d0e9..d4c7c7a 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/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 772c344..dedb9ac 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -44,7 +44,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.google.android.collect.Lists;
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e07d865..0b5383a 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/plugin/src/com/android/systemui/plugins/FragmentBase.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FragmentBase.java
new file mode 100644
index 0000000..af55e8b
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FragmentBase.java
@@ -0,0 +1,33 @@
+/*
+ * 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.plugins;
+
+import android.content.Context;
+import android.view.View;
+
+/**
+ * Interface to deal with lack of multiple inheritance
+ *
+ * This interface is designed to be used as a base class for plugin interfaces
+ * that need fragment methods. Plugins should not extend Fragment directly, so
+ * plugins that are fragments should be extending PluginFragment, but in SysUI
+ * these same versions should extend Fragment directly.
+ *
+ * Only methods that are on Fragment should be included here.
+ */
+public interface FragmentBase {
+ View getView();
+ Context getContext();
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginFragment.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginFragment.java
new file mode 100644
index 0000000..a9d1fa9
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginFragment.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.plugins;
+
+import android.annotation.Nullable;
+import android.app.Fragment;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+
+public abstract class PluginFragment extends Fragment implements Plugin {
+
+ private static final String KEY_PLUGIN_PACKAGE = "plugin_package_name";
+ private Context mPluginContext;
+
+ @Override
+ public void onCreate(Context sysuiContext, Context pluginContext) {
+ mPluginContext = pluginContext;
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (savedInstanceState != null) {
+ Context sysuiContext = getContext();
+ Context pluginContext = recreatePluginContext(sysuiContext, savedInstanceState);
+ onCreate(sysuiContext, pluginContext);
+ }
+ if (mPluginContext == null) {
+ throw new RuntimeException("PluginFragments must call super.onCreate("
+ + "Context sysuiContext, Context pluginContext)");
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putString(KEY_PLUGIN_PACKAGE, getContext().getPackageName());
+ }
+
+ private Context recreatePluginContext(Context sysuiContext, Bundle savedInstanceState) {
+ final String pkg = savedInstanceState.getString(KEY_PLUGIN_PACKAGE);
+ try {
+ ApplicationInfo appInfo = sysuiContext.getPackageManager().getApplicationInfo(pkg, 0);
+ return PluginManager.getInstance(sysuiContext).getContext(appInfo, pkg);
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException("Plugin with invalid package? " + pkg, e);
+ }
+ }
+
+ @Override
+ public LayoutInflater getLayoutInflater(Bundle savedInstanceState) {
+ return super.getLayoutInflater(savedInstanceState).cloneInContext(mPluginContext);
+ }
+
+ /**
+ * Should only be called after {@link Plugin#onCreate(Context, Context)}.
+ */
+ @Override
+ public Context getContext() {
+ return mPluginContext != null ? mPluginContext : super.getContext();
+ }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java
index c64b188..62d3ce4 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java
@@ -158,7 +158,11 @@
case PLUGIN_DISCONNECTED:
if (DEBUG) Log.d(TAG, "onPluginDisconnected");
mListener.onPluginDisconnected((T) msg.obj);
- ((T) msg.obj).onDestroy();
+ if (!(msg.obj instanceof PluginFragment)) {
+ // Only call onDestroy for plugins that aren't fragments, as fragments
+ // will get the onDestroy as part of the fragment lifecycle.
+ ((T) msg.obj).onDestroy();
+ }
break;
default:
super.handleMessage(msg);
@@ -186,7 +190,11 @@
for (int i = mPlugins.size() - 1; i >= 0; i--) {
PluginInfo<T> plugin = mPlugins.get(i);
mListener.onPluginDisconnected(plugin.mPlugin);
- plugin.mPlugin.onDestroy();
+ if (!(plugin.mPlugin instanceof PluginFragment)) {
+ // Only call onDestroy for plugins that aren't fragments, as fragments
+ // will get the onDestroy as part of the fragment lifecycle.
+ plugin.mPlugin.onDestroy();
+ }
}
mPlugins.clear();
handleQueryPlugins(null);
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginManager.java
index 85f2e2a..60cf312 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginManager.java
@@ -18,6 +18,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Build;
import android.os.HandlerThread;
@@ -26,6 +28,7 @@
import android.util.ArrayMap;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.plugins.PluginInstanceManager.PluginContextWrapper;
import dalvik.system.PathClassLoader;
@@ -163,6 +166,16 @@
return mParentClassLoader;
}
+ public Context getAllPluginContext(Context context) {
+ return new PluginContextWrapper(context,
+ new AllPluginClassLoader(context.getClassLoader()));
+ }
+
+ public Context getContext(ApplicationInfo info, String pkg) throws NameNotFoundException {
+ ClassLoader classLoader = getClassLoader(info.sourceDir, pkg);
+ return new PluginContextWrapper(mContext.createApplicationContext(info, 0), classLoader);
+ }
+
public static PluginManager getInstance(Context context) {
if (sInstance == null) {
sInstance = new PluginManager(context.getApplicationContext());
@@ -170,6 +183,28 @@
return sInstance;
}
+ private class AllPluginClassLoader extends ClassLoader {
+ public AllPluginClassLoader(ClassLoader classLoader) {
+ super(classLoader);
+ }
+
+ @Override
+ public Class<?> loadClass(String s) throws ClassNotFoundException {
+ try {
+ return super.loadClass(s);
+ } catch (ClassNotFoundException e) {
+ for (ClassLoader classLoader : mClassLoaders.values()) {
+ try {
+ return classLoader.loadClass(s);
+ } catch (ClassNotFoundException e1) {
+ // Will re-throw e if all fail.
+ }
+ }
+ throw e;
+ }
+ }
+ }
+
@VisibleForTesting
public static class PluginInstanceManagerFactory {
public <T extends Plugin> PluginInstanceManager createPluginInstanceManager(Context context,
@@ -180,7 +215,6 @@
}
}
-
// This allows plugins to include any libraries or copied code they want by only including
// classes from the plugin library.
private static class ClassLoaderFilter extends ClassLoader {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSContainer.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
similarity index 93%
rename from packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSContainer.java
rename to packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
index 4947863..a9874fc 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSContainer.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
@@ -25,17 +25,21 @@
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
-public abstract class QSContainer extends FrameLayout {
+import com.android.systemui.plugins.FragmentBase;
+
+/**
+ * Fragment that contains QS in the notification shade. Most of the interface is for
+ * handling the expand/collapsing of the view interaction.
+ */
+public interface QS extends FragmentBase {
public static final String ACTION = "com.android.systemui.action.PLUGIN_QS";
// This should be incremented any time this class or ActivityStarter or BaseStatusBarHeader
// change in incompatible ways.
- public static final int VERSION = 3;
+ public static final int VERSION = 4;
- public QSContainer(@NonNull Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- }
+ String TAG = "QS";
public abstract void setPanelView(HeightListener notificationPanelView);
public abstract BaseStatusBarHeader getHeader();
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
index 7af247e..9ab8ac6 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -15,7 +15,7 @@
limitations under the License.
-->
-<!-- Height is 0 because it will be managed by the QSContainer manually -->
+<!-- Height is 0 because it will be managed by the QS manually -->
<com.android.systemui.qs.customize.QSCustomizer
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 0339e03..f09657f 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -39,15 +39,15 @@
android:clipToPadding="false"
android:clipChildren="false">
- <com.android.systemui.PluginInflateContainer
- android:id="@+id/qs_auto_reinflate_container"
+ <FrameLayout
+ android:id="@+id/qs_frame"
android:layout="@layout/qs_panel"
android:layout_width="@dimen/notification_panel_width"
android:layout_height="match_parent"
android:layout_gravity="@integer/notification_panel_layout_gravity"
android:clipToPadding="false"
android:clipChildren="false"
- systemui:viewType="com.android.systemui.plugins.qs.QSContainer" />
+ systemui:viewType="com.android.systemui.plugins.qs.QS" />
<com.android.systemui.statusbar.stack.NotificationStackScrollLayout
android:id="@+id/notification_stack_scroller"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index ea070b3..5294c9c 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 837d677..601930a 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 f207dbc1..502a94e 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 c4b0f73..1bc9e59 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 e70e485..8105d9e 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 7f7a67f..83c67d4 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 16dfeeb..bf3a614 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 f8ee362..bd599bc 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 0e91713..5ba720c 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 d258943..927e75b 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 845c8b5..69629a9 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 bb340a6..b0a828f 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 faabbfa..b3a457d 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 0f5f447..050d7f8 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 14d2a19..9aca3cc 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 14d2a19..9aca3cc 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 14d2a19..9aca3cc 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 3b92a3a..90c2ca1 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 600d7d5..0a45ac1 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 884eac5..84fadb2 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 b5e6696..d6578a2 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 866904c..e00c5b5 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 144704d..08572fe 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 b5c227e..830bfa7 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 008d061..3b138c2 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 cca60a8..579335d 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 558a90a..48a6d0f 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 1fff051..f482caaa 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 ae7e0c4..7ff63bd 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 ec62e1c..bd0805b 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 80cb977..2f5358e 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 93960e8..6444ae5 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 71ef4bb..7e08a01 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 12d0ed6..8ffb10d 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 f565603..a6d7983 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 37bea17..6ef9f73 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 15f5d63..695279d 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 477ab82..ba71182 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 45771d9..b222555 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 87c48dd..b2a6f27 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 3e0cdfe..cefb57a 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 2d8be38..e013c6b 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 93098ad..94e789a 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 02e614c..5af5979 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 82f5f22..503c8c8 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 fcaa26f..62fc350 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 44d76d5..9e7d2f2 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 1c7bc38..04fb4c2 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 71d6677..a21019a 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 bc9d79f..ce94295 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 37c002c..8ab1de0 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 025c270..c6312e1 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 3ed5782..f87223c 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 13ef0f6..7c12b5fd 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 553b604..f25662b 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 5c7ea4f..d037cda 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 0d42645..e33ebb5 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 9adc9fe..0fbb0bd 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 6739ea1..c4d2378 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 44881bd..23daf91 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 cc11704..90985e7 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 55598bd..9616c07 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 22bd853..667c65d 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 87acdb3..0a95017 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 cc4391ce..afc5ee4 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 2d3c27f..99b8439 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 9fa3f86..c1aeb09 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 f59e69b..0c461b4 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 2d978c4..caa9f97 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 e3827f7..030c97f 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 fd627cc..c086fff 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 415c5b6..ff46ad3 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 aaea5e5..f2d2661 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 4ae3370..48b1e4f 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 7c7bc4a..94c3af1 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 e41a24b..5785af9 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 51e27a3..6562b39 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 887da2e..e3c419b 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 9eea375..0f5d37e 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 37a7e38..331d09e 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 ce636cd..18cb930 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 f09d6e9..74d5d6c 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/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
index e1d6a94..c4698c3 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
@@ -32,8 +32,8 @@
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Handler;
-import android.os.Looper;
import android.provider.Settings;
+
import com.android.systemui.statusbar.policy.BatteryController;
public class BatteryMeterDrawable extends Drawable implements
@@ -182,13 +182,13 @@
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(SHOW_PERCENT_SETTING), false, mSettingObserver);
updateShowPercent();
- mBatteryController.addStateChangedCallback(this);
+ mBatteryController.addCallback(this);
}
public void stopListening() {
mListening = false;
mContext.getContentResolver().unregisterContentObserver(mSettingObserver);
- mBatteryController.removeStateChangedCallback(this);
+ mBatteryController.removeCallback(this);
}
public void disableShowPercent() {
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 4f3ffde..ef1c25d 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -17,7 +17,6 @@
import android.content.Context;
import android.content.res.TypedArray;
-import android.os.Handler;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.view.View;
@@ -73,7 +72,7 @@
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
- mBatteryController.addStateChangedCallback(this);
+ mBatteryController.addCallback(this);
mDrawable.startListening();
TunerService.get(getContext()).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
}
@@ -81,7 +80,7 @@
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
- mBatteryController.removeStateChangedCallback(this);
+ mBatteryController.removeCallback(this);
mDrawable.stopListening();
TunerService.get(getContext()).removeTunable(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 99e7876..6802fd7 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -29,9 +29,11 @@
import android.os.UserHandle;
import android.util.Log;
+import com.android.systemui.fragments.FragmentService;
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 +44,6 @@
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;
@@ -61,6 +62,7 @@
* The classes of the stuff to start.
*/
private final Class<?>[] SERVICES = new Class[] {
+ FragmentService.class,
TunerService.class,
KeyguardViewMediator.class,
Recents.class,
diff --git a/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java b/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
index b1f454e..965ded5 100644
--- a/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
+++ b/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
@@ -35,8 +35,8 @@
import java.io.FileOutputStream;
import java.io.IOException;
-import static com.android.systemui.statusbar.phone.TouchAnalyticsProto.Session;
-import static com.android.systemui.statusbar.phone.TouchAnalyticsProto.Session.PhoneEvent;
+import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session;
+import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.PhoneEvent;
/**
* Tracks touch, sensor and phone events when the lockscreen is on. If the phone is unlocked
diff --git a/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java b/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java
index b39803a..f8b73a1 100644
--- a/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java
+++ b/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java
@@ -22,10 +22,10 @@
import java.util.ArrayList;
-import static com.android.systemui.statusbar.phone.TouchAnalyticsProto.Session;
-import static com.android.systemui.statusbar.phone.TouchAnalyticsProto.Session.PhoneEvent;
-import static com.android.systemui.statusbar.phone.TouchAnalyticsProto.Session.SensorEvent;
-import static com.android.systemui.statusbar.phone.TouchAnalyticsProto.Session.TouchEvent;
+import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session;
+import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.PhoneEvent;
+import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.SensorEvent;
+import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.TouchEvent;
/**
* Collects touch, sensor and phone events and converts the data to
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index b3038b9..870d4d1 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 @@
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/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index bb4ea2d..9cc927d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -34,7 +34,7 @@
import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto;
import com.android.systemui.statusbar.phone.DozeParameters;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 9df8113..9f26b0c 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 @@
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/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
new file mode 100644
index 0000000..5f27b74
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
@@ -0,0 +1,241 @@
+/*
+ * 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.fragments;
+
+import android.annotation.Nullable;
+import android.app.Fragment;
+import android.app.FragmentController;
+import android.app.FragmentHostCallback;
+import android.app.FragmentManager;
+import android.app.FragmentManager.FragmentLifecycleCallbacks;
+import android.app.FragmentManagerNonConfig;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Parcelable;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.settingslib.applications.InterestingConfigChanges;
+import com.android.systemui.SystemUIApplication;
+import com.android.systemui.plugins.PluginManager;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class FragmentHostManager {
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final Context mContext;
+ private final HashMap<String, ArrayList<FragmentListener>> mListeners = new HashMap<>();
+ private final View mRootView;
+ private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges();
+ private final FragmentService mManager;
+
+ private FragmentController mFragments;
+ private FragmentLifecycleCallbacks mLifecycleCallbacks;
+
+ FragmentHostManager(Context context, FragmentService manager, View rootView) {
+ mContext = PluginManager.getInstance(context).getAllPluginContext(context);
+ mManager = manager;
+ mRootView = rootView;
+ mConfigChanges.applyNewConfig(context.getResources());
+ createFragmentHost(null);
+ }
+
+ private void createFragmentHost(Parcelable savedState) {
+ mFragments = FragmentController.createController(new HostCallbacks());
+ mFragments.attachHost(null);
+ // TODO: Remove non-staticness from FragmentLifecycleCallbacks (hopefully).
+ mLifecycleCallbacks = mFragments.getFragmentManager().new FragmentLifecycleCallbacks() {
+ @Override
+ public void onFragmentViewCreated(FragmentManager fm, Fragment f, View v,
+ Bundle savedInstanceState) {
+ FragmentHostManager.this.onFragmentViewCreated(f);
+ }
+
+ @Override
+ public void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {
+ FragmentHostManager.this.onFragmentViewDestroyed(f);
+ }
+ };
+ mFragments.getFragmentManager().registerFragmentLifecycleCallbacks(mLifecycleCallbacks,
+ true);
+ if (savedState != null) {
+ mFragments.restoreAllState(savedState, (FragmentManagerNonConfig) null);
+ }
+ // For now just keep all fragments in the resumed state.
+ mFragments.dispatchCreate();
+ mFragments.dispatchStart();
+ mFragments.dispatchResume();
+ }
+
+ private Parcelable destroyFragmentHost() {
+ mFragments.dispatchPause();
+ Parcelable p = mFragments.saveAllState();
+ mFragments.dispatchStop();
+ mFragments.dispatchDestroy();
+ mFragments.getFragmentManager().unregisterFragmentLifecycleCallbacks(mLifecycleCallbacks);
+ return p;
+ }
+
+ public void addTagListener(String tag, FragmentListener listener) {
+ ArrayList<FragmentListener> listeners = mListeners.get(tag);
+ if (listeners == null) {
+ listeners = new ArrayList<>();
+ mListeners.put(tag, listeners);
+ }
+ listeners.add(listener);
+ Fragment current = getFragmentManager().findFragmentByTag(tag);
+ if (current != null && current.getView() != null) {
+ listener.onFragmentViewCreated(tag, current);
+ }
+ }
+
+ // Shouldn't generally be needed, included for completeness sake.
+ public void removeTagListener(String tag, FragmentListener listener) {
+ ArrayList<FragmentListener> listeners = mListeners.get(tag);
+ if (listeners != null && listeners.remove(listener) && listeners.size() == 0) {
+ mListeners.remove(tag);
+ }
+ }
+
+ private void onFragmentViewCreated(Fragment fragment) {
+ String tag = fragment.getTag();
+
+ ArrayList<FragmentListener> listeners = mListeners.get(tag);
+ if (listeners != null) {
+ listeners.forEach((listener) -> listener.onFragmentViewCreated(tag, fragment));
+ }
+ }
+
+ private void onFragmentViewDestroyed(Fragment fragment) {
+ String tag = fragment.getTag();
+
+ ArrayList<FragmentListener> listeners = mListeners.get(tag);
+ if (listeners != null) {
+ listeners.forEach((listener) -> listener.onFragmentViewDestroyed(tag, fragment));
+ }
+ }
+
+ /**
+ * Called when the configuration changed, return true if the fragments
+ * should be recreated.
+ */
+ protected void onConfigurationChanged(Configuration newConfig) {
+ if (mConfigChanges.applyNewConfig(mContext.getResources())) {
+ // Save the old state.
+ Parcelable p = destroyFragmentHost();
+ // Generate a new fragment host and restore its state.
+ createFragmentHost(p);
+ } else {
+ mFragments.dispatchConfigurationChanged(newConfig);
+ }
+ }
+
+ private void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ // TODO: Do something?
+ }
+
+ private View findViewById(int id) {
+ return mRootView.findViewById(id);
+ }
+
+ /**
+ * Note: Values from this shouldn't be cached as they can change after config changes.
+ */
+ public FragmentManager getFragmentManager() {
+ return mFragments.getFragmentManager();
+ }
+
+ public interface FragmentListener {
+ void onFragmentViewCreated(String tag, Fragment fragment);
+
+ // The facts of lifecycle
+ // When a fragment is destroyed, you should not talk to it any longer.
+ default void onFragmentViewDestroyed(String tag, Fragment fragment) {
+ }
+ }
+
+ public static FragmentHostManager get(View view) {
+ try {
+ return ((SystemUIApplication) view.getContext().getApplicationContext())
+ .getComponent(FragmentService.class).getFragmentHostManager(view);
+ } catch (ClassCastException e) {
+ // TODO: Some auto handling here?
+ throw e;
+ }
+ }
+
+ class HostCallbacks extends FragmentHostCallback<FragmentHostManager> {
+ public HostCallbacks() {
+ super(mContext, FragmentHostManager.this.mHandler, 0);
+ }
+
+ @Override
+ public FragmentHostManager onGetHost() {
+ return FragmentHostManager.this;
+ }
+
+ @Override
+ public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ FragmentHostManager.this.dump(prefix, fd, writer, args);
+ }
+
+ @Override
+ public boolean onShouldSaveFragmentState(Fragment fragment) {
+ return true; // True for now.
+ }
+
+ @Override
+ public LayoutInflater onGetLayoutInflater() {
+ return LayoutInflater.from(mContext);
+ }
+
+ @Override
+ public boolean onUseFragmentManagerInflaterFactory() {
+ return true;
+ }
+
+ @Override
+ public boolean onHasWindowAnimations() {
+ return false;
+ }
+
+ @Override
+ public int onGetWindowAnimations() {
+ return 0;
+ }
+
+ @Override
+ public void onAttachFragment(Fragment fragment) {
+ }
+
+ @Override
+ @Nullable
+ public View onFindViewById(int id) {
+ return FragmentHostManager.this.findViewById(id);
+ }
+
+ @Override
+ public boolean onHasView() {
+ return true;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
new file mode 100644
index 0000000..85cde10
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
@@ -0,0 +1,82 @@
+/*
+ * 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.fragments;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.view.View;
+
+import com.android.systemui.SystemUI;
+import com.android.systemui.SystemUIApplication;
+
+/**
+ * Holds a map of root views to FragmentHostStates and generates them as needed.
+ * Also dispatches the configuration changes to all current FragmentHostStates.
+ */
+public class FragmentService extends SystemUI {
+
+ private static final String TAG = "FragmentService";
+
+ private final ArrayMap<View, FragmentHostState> mHosts = new ArrayMap<>();
+ private final Handler mHandler = new Handler();
+
+ @Override
+ public void start() {
+ putComponent(FragmentService.class, this);
+ }
+
+ public FragmentHostManager getFragmentHostManager(View view) {
+ View root = view.getRootView();
+ FragmentHostState state = mHosts.get(root);
+ if (state == null) {
+ state = new FragmentHostState(root);
+ mHosts.put(root, state);
+ }
+ return state.getFragmentHostManager();
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ for (FragmentHostState state : mHosts.values()) {
+ state.sendConfigurationChange(newConfig);
+ }
+ }
+
+ private class FragmentHostState {
+ private final View mView;
+
+ private FragmentHostManager mFragmentHostManager;
+
+ public FragmentHostState(View view) {
+ mView = view;
+ mFragmentHostManager = new FragmentHostManager(mContext, FragmentService.this, mView);
+ }
+
+ public void sendConfigurationChange(Configuration newConfig) {
+ mHandler.post(() -> handleSendConfigurationChange(newConfig));
+ }
+
+ public FragmentHostManager getFragmentHostManager() {
+ return mFragmentHostManager;
+ }
+
+ private void handleSendConfigurationChange(Configuration newConfig) {
+ mFragmentHostManager.onConfigurationChanged(newConfig);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/PluginFragmentListener.java b/packages/SystemUI/src/com/android/systemui/fragments/PluginFragmentListener.java
new file mode 100644
index 0000000..e107fcd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/fragments/PluginFragmentListener.java
@@ -0,0 +1,86 @@
+/*
+ * 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.fragments;
+
+import android.app.Fragment;
+import android.util.Log;
+import android.view.View;
+
+import com.android.systemui.plugins.FragmentBase;
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.PluginManager;
+
+public class PluginFragmentListener implements PluginListener<Plugin> {
+
+ private static final String TAG = "PluginFragmentListener";
+
+ private final FragmentHostManager mFragmentHostManager;
+ private final PluginManager mPluginManager;
+ private final Class<? extends Fragment> mDefaultClass;
+ private final int mId;
+ private final String mTag;
+ private final Class<? extends FragmentBase> mExpectedInterface;
+
+ public PluginFragmentListener(View view, String tag, int id,
+ Class<? extends Fragment> defaultFragment,
+ Class<? extends FragmentBase> expectedInterface) {
+ mFragmentHostManager = FragmentHostManager.get(view);
+ mPluginManager = PluginManager.getInstance(view.getContext());
+ mExpectedInterface = expectedInterface;
+ mTag = tag;
+ mDefaultClass = defaultFragment;
+ mId = id;
+ }
+
+ public void startListening(String action, int version) {
+ try {
+ setFragment(mDefaultClass.newInstance());
+ } catch (InstantiationException | IllegalAccessException e) {
+ Log.e(TAG, "Couldn't instantiate " + mDefaultClass.getName(), e);
+ }
+ mPluginManager.addPluginListener(action, this, version, false /* Only allow one */);
+ }
+
+ public void stopListening() {
+ mPluginManager.removePluginListener(this);
+ }
+
+ private void setFragment(Fragment fragment) {
+ mFragmentHostManager.getFragmentManager().beginTransaction()
+ .replace(mId, fragment, mTag)
+ .commit();
+ }
+
+ @Override
+ public void onPluginConnected(Plugin plugin) {
+ try {
+ mExpectedInterface.cast(plugin);
+ setFragment((Fragment) plugin);
+ } catch (ClassCastException e) {
+ Log.e(TAG, plugin.getClass().getName() + " must be a Fragment and implement "
+ + mExpectedInterface.getName(), e);
+ }
+ }
+
+ @Override
+ public void onPluginDisconnected(Plugin plugin) {
+ try {
+ setFragment(mDefaultClass.newInstance());
+ } catch (InstantiationException | IllegalAccessException e) {
+ Log.e(TAG, "Couldn't instantiate " + mDefaultClass.getName(), e);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 816d70d..fe9f55f 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 @@
@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 9ae341a..8f1a943 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 @@
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 @@
return;
}
- if (!mKeyguardDonePending) {
- KeyguardViewMediator.this.handleKeyguardDone(true /* authenticated */);
- }
+ tryKeyguardDone(true);
if (strongAuth) {
mUpdateMonitor.reportSuccessfulStrongAuthUnlockAttempt();
}
@@ -545,7 +544,8 @@
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 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 @@
// 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 void handleDismiss(boolean allowWhileOccluded) {
if (mShowing && (allowWhileOccluded || !mOccluded)) {
- mStatusBarKeyguardViewManager.dismiss();
+ mStatusBarKeyguardViewManager.dismissAndCollapse();
}
}
@@ -1493,6 +1493,16 @@
}
};
+ 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 @@
}
};
+ private final Runnable mHideAnimationFinishedRunnable = () -> {
+ mHideAnimationRunning = false;
+ tryKeyguardDone(true);
+ };
+
/**
* Handle message sent by {@link #hideLocked()}
* @see #HIDE
@@ -1697,16 +1712,10 @@
return;
}
mHiding = true;
- if (mShowing && !mOccluded) {
- if (!mHideAnimationRun) {
- mStatusBarKeyguardViewManager.startPreHideAnimation(mKeyguardGoingAwayRunnable);
- } else {
- mKeyguardGoingAwayRunnable.run();
- }
- } else {
- // Don't try to rely on WindowManager - if Keyguard wasn't showing, window
- // manager won't start the exit animation.
+ if (mShowing && !mOccluded) {
+ mKeyguardGoingAwayRunnable.run();
+ } else {
handleStartKeyguardExitAnimation(
SystemClock.uptimeMillis() + mHideAnimation.getStartOffset(),
mHideAnimation.getDuration());
@@ -1806,7 +1815,7 @@
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleVerifyUnlock");
setShowingLocked(true);
- mStatusBarKeyguardViewManager.verifyUnlock();
+ mStatusBarKeyguardViewManager.dismissAndCollapse();
}
Trace.endSection();
}
@@ -1969,7 +1978,11 @@
}
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 0000000..e8e8a4d
--- /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 a359380..b24d199 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 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.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.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 @@
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 @@
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 @@
}
};
- 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 @@
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 @@
// Reset back to default
mEnableSwipeToDismiss = true;
mEnableDragToDismiss = true;
+ mEnableMinimizing = false;
+ setMinimizedState(false);
mEnableTapThrough = false;
mIsTappingThrough = false;
mEnableSnapToEdge = false;
@@ -211,6 +234,9 @@
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 @@
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 @@
}
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();
+ for (PipTouchGesture gesture : mGestures) {
+ if (gesture.onUp(mTouchState)) {
+ break;
}
- } 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();
- }
- }
- } 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 @@
}
/**
+ * 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 @@
}
/**
+ * @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 @@
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 @@
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 @@
}
/**
+ * 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 @@
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 0000000..80af5a6
--- /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/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index e1cd143..dc42adc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -20,7 +20,7 @@
import android.view.View.OnLayoutChangeListener;
import android.widget.TextView;
-import com.android.systemui.plugins.qs.QSContainer;
+import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.PagedTileLayout.PageListener;
import com.android.systemui.qs.QSPanel.QSTileLayout;
import com.android.systemui.qs.QSTile.Host.Callback;
@@ -47,7 +47,7 @@
private final ArrayList<View> mTopFiveQs = new ArrayList<>();
private final QuickQSPanel mQuickQsPanel;
private final QSPanel mQsPanel;
- private final QSContainer mQsContainer;
+ private final QS mQs;
private PagedTileLayout mPagedLayout;
@@ -67,12 +67,15 @@
private float mLastPosition;
private QSTileHost mHost;
- public QSAnimator(QSContainer container, QuickQSPanel quickPanel, QSPanel panel) {
- mQsContainer = container;
+ public QSAnimator(QS qs, QuickQSPanel quickPanel, QSPanel panel) {
+ mQs = qs;
mQuickQsPanel = quickPanel;
mQsPanel = panel;
mQsPanel.addOnAttachStateChangeListener(this);
- container.addOnLayoutChangeListener(this);
+ qs.getView().addOnLayoutChangeListener(this);
+ if (mQsPanel.isAttachedToWindow()) {
+ onViewAttachedToWindow(null);
+ }
QSTileLayout tileLayout = mQsPanel.getTileLayout();
if (tileLayout instanceof PagedTileLayout) {
mPagedLayout = ((PagedTileLayout) tileLayout);
@@ -102,7 +105,7 @@
@Override
public void onViewAttachedToWindow(View v) {
- TunerService.get(mQsContainer.getContext()).addTunable(this, ALLOW_FANCY_ANIMATION,
+ TunerService.get(mQs.getContext()).addTunable(this, ALLOW_FANCY_ANIMATION,
MOVE_FULL_ROWS, QuickQSPanel.NUM_QUICK_TILES);
}
@@ -111,7 +114,7 @@
if (mHost != null) {
mHost.removeCallback(this);
}
- TunerService.get(mQsContainer.getContext()).removeTunable(this);
+ TunerService.get(mQs.getContext()).removeTunable(this);
}
@Override
@@ -124,7 +127,7 @@
} else if (MOVE_FULL_ROWS.equals(key)) {
mFullRows = newValue == null || Integer.parseInt(newValue) != 0;
} else if (QuickQSPanel.NUM_QUICK_TILES.equals(key)) {
- mNumQuickTiles = mQuickQsPanel.getNumQuickTiles(mQsContainer.getContext());
+ mNumQuickTiles = mQuickQsPanel.getNumQuickTiles(mQs.getContext());
clearAnimationState();
}
updateAnimators();
@@ -166,13 +169,14 @@
}
final TextView label = ((QSTileView) tileView).getLabel();
final View tileIcon = tileView.getIcon().getIconView();
+ View view = mQs.getView();
if (count < mNumQuickTiles && mAllowFancy) {
// Quick tiles.
QSTileBaseView quickTileView = mQuickQsPanel.getTileView(tile);
lastX = loc1[0];
- getRelativePosition(loc1, quickTileView.getIcon().getIconView(), mQsContainer);
- getRelativePosition(loc2, tileIcon, mQsContainer);
+ getRelativePosition(loc1, quickTileView.getIcon().getIconView(), view);
+ getRelativePosition(loc2, tileIcon, view);
final int xDiff = loc2[0] - loc1[0];
final int yDiff = loc2[1] - loc1[1];
lastXDiff = loc1[0] - lastX;
@@ -198,7 +202,7 @@
// This makes the extra icons seems as if they are coming from positions in the
// quick panel.
loc1[0] += lastXDiff;
- getRelativePosition(loc2, tileIcon, mQsContainer);
+ getRelativePosition(loc2, tileIcon, view);
final int xDiff = loc2[0] - loc1[0];
final int yDiff = loc2[1] - loc1[1];
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index f345172..f4da5ec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -16,53 +16,34 @@
package com.android.systemui.qs;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
+import android.widget.FrameLayout;
-import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSContainer;
+import com.android.systemui.plugins.qs.QS.HeightListener;
import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
import com.android.systemui.statusbar.phone.QSTileHost;
import com.android.systemui.statusbar.phone.QuickStatusBarHeader;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
/**
* Wrapper view with background which contains {@link QSPanel} and {@link BaseStatusBarHeader}
- *
- * Also manages animations for the QS Header and Panel.
*/
-public class QSContainerImpl extends QSContainer {
- private static final String TAG = "QSContainer";
- private static final boolean DEBUG = false;
+public class QSContainerImpl extends FrameLayout {
private final Point mSizePoint = new Point();
- private final Rect mQsBounds = new Rect();
private int mHeightOverride = -1;
- protected QSPanel mQSPanel;
- private QSDetail mQSDetail;
- protected QuickStatusBarHeader mHeader;
+ protected View mQSPanel;
+ private View mQSDetail;
+ protected View mHeader;
protected float mQsExpansion;
- private boolean mQsExpanded;
- private boolean mHeaderAnimating;
- private boolean mKeyguardShowing;
- private boolean mStackScrollerOverscrolling;
-
- private long mDelay;
- private QSAnimator mQSAnimator;
private QSCustomizer mQSCustomizer;
- private HeightListener mPanelView;
- private boolean mListening;
public QSContainerImpl(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -71,31 +52,10 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mQSPanel = (QSPanel) findViewById(R.id.quick_settings_panel);
- mQSDetail = (QSDetail) findViewById(R.id.qs_detail);
- mHeader = (QuickStatusBarHeader) findViewById(R.id.header);
- mQSDetail.setQsPanel(mQSPanel, mHeader);
- mQSAnimator = new QSAnimator(this, (QuickQSPanel) mHeader.findViewById(R.id.quick_qs_panel),
- mQSPanel);
+ mQSPanel = findViewById(R.id.quick_settings_panel);
+ mQSDetail = findViewById(R.id.qs_detail);
+ mHeader = findViewById(R.id.header);
mQSCustomizer = (QSCustomizer) findViewById(R.id.qs_customize);
- mQSCustomizer.setQsContainer(this);
- }
-
- @Override
- public void onRtlPropertiesChanged(int layoutDirection) {
- super.onRtlPropertiesChanged(layoutDirection);
- mQSAnimator.onRtlChanged();
- }
-
- public void setHost(QSTileHost qsh) {
- mQSPanel.setHost(qsh, mQSCustomizer);
- mHeader.setQSPanel(mQSPanel);
- mQSDetail.setHost(qsh);
- mQSAnimator.setHost(qsh);
- }
-
- public void setPanelView(HeightListener panelView) {
- mPanelView = panelView;
}
@Override
@@ -111,8 +71,8 @@
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
- // QSCustomizer is always be the height of the screen, but do this after
- // other measuring to avoid changing the height of the QSContainer.
+ // QSCustomizer will always be the height of the screen, but do this after
+ // other measuring to avoid changing the height of the QS.
getDisplay().getRealSize(mSizePoint);
mQSCustomizer.measure(widthMeasureSpec,
MeasureSpec.makeMeasureSpec(mSizePoint.y, MeasureSpec.EXACTLY));
@@ -124,10 +84,6 @@
updateBottom();
}
- public boolean isCustomizing() {
- return mQSCustomizer.isCustomizing();
- }
-
/**
* Overrides the height of this view (post-layout), so that the content is clipped to that
* height and the background is set to that height.
@@ -139,41 +95,7 @@
updateBottom();
}
- @Override
- public void setContainer(ViewGroup container) {
- if (container instanceof NotificationsQuickSettingsContainer) {
- mQSCustomizer.setContainer((NotificationsQuickSettingsContainer) container);
- }
- }
-
- /**
- * The height this view wants to be. This is different from {@link #getMeasuredHeight} such that
- * during closing the detail panel, this already returns the smaller height.
- */
- public int getDesiredHeight() {
- if (isCustomizing()) {
- return getHeight();
- }
- if (mQSDetail.isClosingDetail()) {
- int panelHeight = ((LayoutParams) mQSPanel.getLayoutParams()).topMargin
- + mQSPanel.getMeasuredHeight();
- return panelHeight + getPaddingBottom();
- } else {
- return getMeasuredHeight();
- }
- }
-
- public void notifyCustomizeChanged() {
- // The customize state changed, so our height changed.
- updateBottom();
- mQSPanel.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE);
- mHeader.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE);
- // Let the panel know the position changed and it needs to update where notifications
- // and whatnot are.
- mPanelView.onQsHeightChanged();
- }
-
- private void updateBottom() {
+ void updateBottom() {
int height = calculateContainerHeight();
setBottom(getTop() + height);
mQSDetail.setBottom(getTop() + height);
@@ -182,162 +104,12 @@
protected int calculateContainerHeight() {
int heightOverride = mHeightOverride != -1 ? mHeightOverride : getMeasuredHeight();
return mQSCustomizer.isCustomizing() ? mQSCustomizer.getHeight()
- : (int) (mQsExpansion * (heightOverride - mHeader.getCollapsedHeight()))
- + mHeader.getCollapsedHeight();
+ : (int) (mQsExpansion * (heightOverride - mHeader.getHeight()))
+ + mHeader.getHeight();
}
- private void updateQsState() {
- boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling || mHeaderAnimating;
- mQSPanel.setExpanded(mQsExpanded);
- mQSDetail.setExpanded(mQsExpanded);
- mHeader.setVisibility((mQsExpanded || !mKeyguardShowing || mHeaderAnimating)
- ? View.VISIBLE
- : View.INVISIBLE);
- mHeader.setExpanded((mKeyguardShowing && !mHeaderAnimating)
- || (mQsExpanded && !mStackScrollerOverscrolling));
- mQSPanel.setVisibility(expandVisually ? View.VISIBLE : View.INVISIBLE);
- }
-
- public BaseStatusBarHeader getHeader() {
- return mHeader;
- }
-
- public QSPanel getQsPanel() {
- return mQSPanel;
- }
-
- public QSCustomizer getCustomizer() {
- return mQSCustomizer;
- }
-
- public boolean isShowingDetail() {
- return mQSPanel.isShowingCustomize() || mQSDetail.isShowingDetail();
- }
-
- public void setHeaderClickable(boolean clickable) {
- if (DEBUG) Log.d(TAG, "setHeaderClickable " + clickable);
- mHeader.setClickable(clickable);
- }
-
- public void setExpanded(boolean expanded) {
- if (DEBUG) Log.d(TAG, "setExpanded " + expanded);
- mQsExpanded = expanded;
- mQSPanel.setListening(mListening && mQsExpanded);
- updateQsState();
- }
-
- public void setKeyguardShowing(boolean keyguardShowing) {
- if (DEBUG) Log.d(TAG, "setKeyguardShowing " + keyguardShowing);
- mKeyguardShowing = keyguardShowing;
- mQSAnimator.setOnKeyguard(keyguardShowing);
- updateQsState();
- }
-
- public void setOverscrolling(boolean stackScrollerOverscrolling) {
- if (DEBUG) Log.d(TAG, "setOverscrolling " + stackScrollerOverscrolling);
- mStackScrollerOverscrolling = stackScrollerOverscrolling;
- updateQsState();
- }
-
- public void setListening(boolean listening) {
- if (DEBUG) Log.d(TAG, "setListening " + listening);
- mListening = listening;
- mHeader.setListening(listening);
- mQSPanel.setListening(mListening && mQsExpanded);
- }
-
- public void setHeaderListening(boolean listening) {
- mHeader.setListening(listening);
- }
-
- public void setQsExpansion(float expansion, float headerTranslation) {
- if (DEBUG) Log.d(TAG, "setQSExpansion " + expansion + " " + headerTranslation);
+ public void setExpansion(float expansion) {
mQsExpansion = expansion;
- final float translationScaleY = expansion - 1;
- if (!mHeaderAnimating) {
- setTranslationY(mKeyguardShowing ? (translationScaleY * mHeader.getHeight())
- : headerTranslation);
- }
- mHeader.setExpansion(mKeyguardShowing ? 1 : expansion);
- mQSPanel.setTranslationY(translationScaleY * mQSPanel.getHeight());
- mQSDetail.setFullyExpanded(expansion == 1);
- mQSAnimator.setPosition(expansion);
updateBottom();
-
- // Set bounds on the QS panel so it doesn't run over the header.
- mQsBounds.top = (int) (mQSPanel.getHeight() * (1 - expansion));
- mQsBounds.right = mQSPanel.getWidth();
- mQsBounds.bottom = mQSPanel.getHeight();
- mQSPanel.setClipBounds(mQsBounds);
- }
-
- public void animateHeaderSlidingIn(long delay) {
- if (DEBUG) Log.d(TAG, "animateHeaderSlidingIn");
- // If the QS is already expanded we don't need to slide in the header as it's already
- // visible.
- if (!mQsExpanded) {
- mHeaderAnimating = true;
- mDelay = delay;
- getViewTreeObserver().addOnPreDrawListener(mStartHeaderSlidingIn);
- }
- }
-
- public void animateHeaderSlidingOut() {
- if (DEBUG) Log.d(TAG, "animateHeaderSlidingOut");
- mHeaderAnimating = true;
- animate().y(-mHeader.getHeight())
- .setStartDelay(0)
- .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- animate().setListener(null);
- mHeaderAnimating = false;
- updateQsState();
- }
- })
- .start();
- }
-
- @Override
- public void closeDetail() {
- mQSPanel.closeDetail();
- }
-
- private final ViewTreeObserver.OnPreDrawListener mStartHeaderSlidingIn
- = new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- getViewTreeObserver().removeOnPreDrawListener(this);
- animate()
- .translationY(0f)
- .setStartDelay(mDelay)
- .setDuration(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .setListener(mAnimateHeaderSlidingInListener)
- .start();
- setY(-mHeader.getHeight());
- return true;
- }
- };
-
- private final Animator.AnimatorListener mAnimateHeaderSlidingInListener
- = new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mHeaderAnimating = false;
- updateQsState();
- }
- };
-
- public int getQsMinExpansionHeight() {
- return mHeader.getHeight();
- }
-
- @Override
- public void hideImmediately() {
- animate().cancel();
- setY(-mHeader.getHeight());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 2b9320b..5027144 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -35,9 +35,9 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSContainer.BaseStatusBarHeader;
-import com.android.systemui.plugins.qs.QSContainer.Callback;
-import com.android.systemui.plugins.qs.QSContainer.DetailAdapter;
+import com.android.systemui.plugins.qs.QS.BaseStatusBarHeader;
+import com.android.systemui.plugins.qs.QS.Callback;
+import com.android.systemui.plugins.qs.QS.DetailAdapter;
import com.android.systemui.statusbar.phone.QSTileHost;
public class QSDetail extends LinearLayout {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
new file mode 100644
index 0000000..c8f1670
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -0,0 +1,300 @@
+/*
+ * 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.qs;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.annotation.Nullable;
+import android.app.Fragment;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.widget.FrameLayout.LayoutParams;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
+import com.android.systemui.statusbar.phone.QSTileHost;
+import com.android.systemui.statusbar.phone.QuickStatusBarHeader;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
+
+public class QSFragment extends Fragment implements QS {
+ private static final String TAG = "QS";
+ private static final boolean DEBUG = false;
+
+ private final Rect mQsBounds = new Rect();
+ private boolean mQsExpanded;
+ private boolean mHeaderAnimating;
+ private boolean mKeyguardShowing;
+ private boolean mStackScrollerOverscrolling;
+
+ private long mDelay;
+
+ private QSAnimator mQSAnimator;
+ private HeightListener mPanelView;
+ protected QuickStatusBarHeader mHeader;
+ private QSCustomizer mQSCustomizer;
+ protected QSPanel mQSPanel;
+ private QSDetail mQSDetail;
+ private boolean mListening;
+ private QSContainerImpl mContainer;
+ private int mLayoutDirection;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.qs_panel, container, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ mQSPanel = (QSPanel) view.findViewById(R.id.quick_settings_panel);
+ mQSDetail = (QSDetail) view.findViewById(R.id.qs_detail);
+ mHeader = (QuickStatusBarHeader) view.findViewById(R.id.header);
+ mContainer = (QSContainerImpl) view;
+
+ mQSDetail.setQsPanel(mQSPanel, mHeader);
+ mQSAnimator = new QSAnimator(this, (QuickQSPanel) mHeader.findViewById(R.id.quick_qs_panel),
+ mQSPanel);
+ mQSCustomizer = (QSCustomizer) view.findViewById(R.id.qs_customize);
+ mQSCustomizer.setQs(this);
+ }
+
+ public void setPanelView(HeightListener panelView) {
+ mPanelView = panelView;
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ if (newConfig.getLayoutDirection() != mLayoutDirection) {
+ mLayoutDirection = newConfig.getLayoutDirection();
+ mQSAnimator.onRtlChanged();
+ }
+ }
+
+ @Override
+ public void setContainer(ViewGroup container) {
+ if (container instanceof NotificationsQuickSettingsContainer) {
+ mQSCustomizer.setContainer((NotificationsQuickSettingsContainer) container);
+ }
+ }
+
+ public boolean isCustomizing() {
+ return mQSCustomizer.isCustomizing();
+ }
+
+ public void setHost(QSTileHost qsh) {
+ mQSPanel.setHost(qsh, mQSCustomizer);
+ mHeader.setQSPanel(mQSPanel);
+ mQSDetail.setHost(qsh);
+ mQSAnimator.setHost(qsh);
+ }
+
+ private void updateQsState() {
+ final boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling
+ || mHeaderAnimating;
+ mQSPanel.setExpanded(mQsExpanded);
+ mQSDetail.setExpanded(mQsExpanded);
+ mHeader.setVisibility((mQsExpanded || !mKeyguardShowing || mHeaderAnimating)
+ ? View.VISIBLE
+ : View.INVISIBLE);
+ mHeader.setExpanded((mKeyguardShowing && !mHeaderAnimating)
+ || (mQsExpanded && !mStackScrollerOverscrolling));
+ mQSPanel.setVisibility(expandVisually ? View.VISIBLE : View.INVISIBLE);
+ }
+
+ public BaseStatusBarHeader getHeader() {
+ return mHeader;
+ }
+
+ public QSPanel getQsPanel() {
+ return mQSPanel;
+ }
+
+ public QSCustomizer getCustomizer() {
+ return mQSCustomizer;
+ }
+
+ public boolean isShowingDetail() {
+ return mQSPanel.isShowingCustomize() || mQSDetail.isShowingDetail();
+ }
+
+ public void setHeaderClickable(boolean clickable) {
+ if (DEBUG) Log.d(TAG, "setHeaderClickable " + clickable);
+ mHeader.setClickable(clickable);
+ }
+
+ public void setExpanded(boolean expanded) {
+ if (DEBUG) Log.d(TAG, "setExpanded " + expanded);
+ mQsExpanded = expanded;
+ mQSPanel.setListening(mListening && mQsExpanded);
+ updateQsState();
+ }
+
+ public void setKeyguardShowing(boolean keyguardShowing) {
+ if (DEBUG) Log.d(TAG, "setKeyguardShowing " + keyguardShowing);
+ mKeyguardShowing = keyguardShowing;
+ mQSAnimator.setOnKeyguard(keyguardShowing);
+ updateQsState();
+ }
+
+ public void setOverscrolling(boolean stackScrollerOverscrolling) {
+ if (DEBUG) Log.d(TAG, "setOverscrolling " + stackScrollerOverscrolling);
+ mStackScrollerOverscrolling = stackScrollerOverscrolling;
+ updateQsState();
+ }
+
+ public void setListening(boolean listening) {
+ if (DEBUG) Log.d(TAG, "setListening " + listening);
+ mListening = listening;
+ mHeader.setListening(listening);
+ mQSPanel.setListening(mListening && mQsExpanded);
+ }
+
+ public void setHeaderListening(boolean listening) {
+ mHeader.setListening(listening);
+ }
+
+ public void setQsExpansion(float expansion, float headerTranslation) {
+ if (DEBUG) Log.d(TAG, "setQSExpansion " + expansion + " " + headerTranslation);
+ mContainer.setExpansion(expansion);
+ final float translationScaleY = expansion - 1;
+ if (!mHeaderAnimating) {
+ getView().setTranslationY(mKeyguardShowing ? (translationScaleY * mHeader.getHeight())
+ : headerTranslation);
+ }
+ mHeader.setExpansion(mKeyguardShowing ? 1 : expansion);
+ mQSPanel.setTranslationY(translationScaleY * mQSPanel.getHeight());
+ mQSDetail.setFullyExpanded(expansion == 1);
+ mQSAnimator.setPosition(expansion);
+
+ // Set bounds on the QS panel so it doesn't run over the header.
+ mQsBounds.top = (int) (mQSPanel.getHeight() * (1 - expansion));
+ mQsBounds.right = mQSPanel.getWidth();
+ mQsBounds.bottom = mQSPanel.getHeight();
+ mQSPanel.setClipBounds(mQsBounds);
+ }
+
+ public void animateHeaderSlidingIn(long delay) {
+ if (DEBUG) Log.d(TAG, "animateHeaderSlidingIn");
+ // If the QS is already expanded we don't need to slide in the header as it's already
+ // visible.
+ if (!mQsExpanded) {
+ mHeaderAnimating = true;
+ mDelay = delay;
+ getView().getViewTreeObserver().addOnPreDrawListener(mStartHeaderSlidingIn);
+ }
+ }
+
+ public void animateHeaderSlidingOut() {
+ if (DEBUG) Log.d(TAG, "animateHeaderSlidingOut");
+ mHeaderAnimating = true;
+ getView().animate().y(-mHeader.getHeight())
+ .setStartDelay(0)
+ .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ getView().animate().setListener(null);
+ mHeaderAnimating = false;
+ updateQsState();
+ }
+ })
+ .start();
+ }
+
+ @Override
+ public void closeDetail() {
+ mQSPanel.closeDetail();
+ }
+
+ public void notifyCustomizeChanged() {
+ // The customize state changed, so our height changed.
+ mContainer.updateBottom();
+ mQSPanel.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE);
+ mHeader.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE);
+ // Let the panel know the position changed and it needs to update where notifications
+ // and whatnot are.
+ mPanelView.onQsHeightChanged();
+ }
+
+ /**
+ * The height this view wants to be. This is different from {@link #getMeasuredHeight} such that
+ * during closing the detail panel, this already returns the smaller height.
+ */
+ public int getDesiredHeight() {
+ if (mQSCustomizer.isCustomizing()) {
+ return getView().getHeight();
+ }
+ if (mQSDetail.isClosingDetail()) {
+ int panelHeight = ((LayoutParams) mQSPanel.getLayoutParams()).topMargin
+ + mQSPanel.getMeasuredHeight();
+ return panelHeight + getView().getPaddingBottom();
+ } else {
+ return getView().getMeasuredHeight();
+ }
+ }
+
+ @Override
+ public void setHeightOverride(int desiredHeight) {
+ mContainer.setHeightOverride(desiredHeight);
+ }
+
+ public int getQsMinExpansionHeight() {
+ return mHeader.getHeight();
+ }
+
+ @Override
+ public void hideImmediately() {
+ getView().animate().cancel();
+ getView().setY(-mHeader.getHeight());
+ }
+
+ private final ViewTreeObserver.OnPreDrawListener mStartHeaderSlidingIn
+ = new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ getView().getViewTreeObserver().removeOnPreDrawListener(this);
+ getView().animate()
+ .translationY(0f)
+ .setStartDelay(mDelay)
+ .setDuration(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .setListener(mAnimateHeaderSlidingInListener)
+ .start();
+ getView().setY(-mHeader.getHeight());
+ return true;
+ }
+ };
+
+ private final Animator.AnimatorListener mAnimateHeaderSlidingInListener
+ = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mHeaderAnimating = false;
+ updateQsState();
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index c699e27..e55ff70 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -28,10 +28,10 @@
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSContainer;
-import com.android.systemui.plugins.qs.QSContainer.DetailAdapter;
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.plugins.qs.QS.DetailAdapter;
import com.android.systemui.qs.QSTile.Host.Callback;
import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.qs.external.CustomTile;
@@ -60,7 +60,7 @@
protected boolean mExpanded;
protected boolean mListening;
- private QSContainer.Callback mCallback;
+ private QS.Callback mCallback;
private BrightnessController mBrightnessController;
protected QSTileHost mHost;
@@ -171,7 +171,7 @@
return mBrightnessView;
}
- public void setCallback(QSContainer.Callback callback) {
+ public void setCallback(QS.Callback callback) {
mCallback = callback;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 39ce324..4341d17 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -29,9 +29,9 @@
import android.util.SparseArray;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.RestrictedLockUtils;
-import com.android.systemui.plugins.qs.QSContainer.DetailAdapter;
+import com.android.systemui.plugins.qs.QS.DetailAdapter;
import com.android.systemui.qs.QSTile.State;
import com.android.systemui.qs.external.TileServices;
import com.android.systemui.statusbar.phone.ManagedProfileController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index cf2c16d..9bbead4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -35,9 +35,9 @@
import android.widget.Toolbar.OnMenuItemClickListener;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto;
import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSContainer;
+import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.QSDetailClipper;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
@@ -69,7 +69,7 @@
private Toolbar mToolbar;
private boolean mCustomizing;
private NotificationsQuickSettingsContainer mNotifQsContainer;
- private QSContainer mQsContainer;
+ private QS mQs;
public QSCustomizer(Context context, AttributeSet attrs) {
super(new ContextThemeWrapper(context, R.style.edit_theme), attrs);
@@ -127,8 +127,8 @@
mNotifQsContainer = notificationsQsContainer;
}
- public void setQsContainer(QSContainer qsContainer) {
- mQsContainer = qsContainer;
+ public void setQs(QS qs) {
+ mQs = qs;
}
public void show(int x, int y) {
@@ -169,7 +169,7 @@
private void setCustomizing(boolean customizing) {
mCustomizing = customizing;
- mQsContainer.notifyCustomizeChanged();
+ mQs.notifyCustomizeChanged();
}
public boolean isCustomizing() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 8d7f6ee..f2c3e61 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -40,7 +40,7 @@
import android.widget.TextView;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto;
import com.android.systemui.R;
import com.android.systemui.qs.QSIconView;
import com.android.systemui.qs.customize.TileAdapter.Holder;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index dc68112..28530d7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -37,7 +37,7 @@
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
index 91a0eb0..342df5e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -143,6 +143,7 @@
}
public void handleDestroy() {
+ setBindAllowed(false);
mServices.getContext().unregisterReceiver(mUninstallReceiver);
mStateManager.handleDestroy();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index 6bc94b2..015a4c0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -306,6 +306,11 @@
}
}
+ public void destroy() {
+ mServices.values().forEach(service -> service.handleDestroy());
+ mContext.unregisterReceiver(mRequestListeningReceiver);
+ }
+
private final BroadcastReceiver mRequestListeningReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index a980a7f..e57cd58 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -26,7 +26,7 @@
import android.widget.Switch;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.qs.GlobalSetting;
import com.android.systemui.qs.QSTile;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
index d89fbfd3c..fc1c1ac 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
@@ -20,8 +20,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.Looper;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.RelativeSizeSpan;
@@ -35,12 +33,12 @@
import android.widget.Checkable;
import android.widget.ImageView;
import android.widget.TextView;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.BatteryInfo;
import com.android.settingslib.graph.UsageView;
import com.android.systemui.BatteryMeterDrawable;
import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSContainer.DetailAdapter;
+import com.android.systemui.plugins.qs.QS.DetailAdapter;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -80,9 +78,9 @@
@Override
public void setListening(boolean listening) {
if (listening) {
- mBatteryController.addStateChangedCallback(this);
+ mBatteryController.addCallback(this);
} else {
- mBatteryController.removeStateChangedCallback(this);
+ mBatteryController.removeCallback(this);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 18bde27..f83b279 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -29,10 +29,10 @@
import android.widget.Switch;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSContainer.DetailAdapter;
+import com.android.systemui.plugins.qs.QS.DetailAdapter;
import com.android.systemui.qs.QSDetailItems;
import com.android.systemui.qs.QSDetailItems.Item;
import com.android.systemui.qs.QSTile;
@@ -67,9 +67,9 @@
@Override
public void setListening(boolean listening) {
if (listening) {
- mController.addStateChangedCallback(mCallback);
+ mController.addCallback(mCallback);
} else {
- mController.removeStateChangedCallback(mCallback);
+ mController.removeCallback(mCallback);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 61bad77..8afa91e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -26,9 +26,9 @@
import android.widget.Button;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSContainer.DetailAdapter;
+import com.android.systemui.plugins.qs.QS.DetailAdapter;
import com.android.systemui.qs.QSDetailItems;
import com.android.systemui.qs.QSDetailItems.Item;
import com.android.systemui.qs.QSTile;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 7de883e..1406c9f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -26,10 +26,10 @@
import android.widget.Button;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSContainer.DetailAdapter;
+import com.android.systemui.plugins.qs.QS.DetailAdapter;
import com.android.systemui.qs.QSIconView;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.SignalTileView;
@@ -68,9 +68,9 @@
@Override
public void setListening(boolean listening) {
if (listening) {
- mController.addSignalCallback(mSignalCallback);
+ mController.addCallback(mSignalCallback);
} else {
- mController.removeSignalCallback(mSignalCallback);
+ mController.removeCallback(mSignalCallback);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index 5ae7a76..77f063d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -22,7 +22,7 @@
import android.widget.Switch;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.SecureSetting;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index aabafe1..65432dc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -19,7 +19,7 @@
import android.widget.Switch;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
@@ -44,9 +44,9 @@
@Override
public void setListening(boolean listening) {
if (listening) {
- mDataSaverController.addListener(this);
+ mDataSaverController.addCallback(this);
} else {
- mDataSaverController.remListener(this);
+ mDataSaverController.removeCallback(this);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index ec4ab51..198375d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -33,11 +33,11 @@
import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.SysUIToast;
-import com.android.systemui.plugins.qs.QSContainer.DetailAdapter;
+import com.android.systemui.plugins.qs.QS.DetailAdapter;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.volume.ZenModePanel;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 0aa723e..5b1638f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -25,7 +25,7 @@
import android.widget.Switch;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.FlashlightController;
@@ -45,13 +45,13 @@
public FlashlightTile(Host host) {
super(host);
mFlashlightController = host.getFlashlightController();
- mFlashlightController.addListener(this);
+ mFlashlightController.addCallback(this);
}
@Override
protected void handleDestroy() {
super.handleDestroy();
- mFlashlightController.removeListener(this);
+ mFlashlightController.removeCallback(this);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 016c4b7..dcee659 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -16,8 +16,6 @@
package com.android.systemui.qs.tiles;
-import android.content.BroadcastReceiver;
-import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.UserManager;
@@ -29,7 +27,7 @@
import android.widget.Switch;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.qs.GlobalSetting;
import com.android.systemui.qs.QSTile;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
index 2a2cc46..f968816 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
@@ -30,7 +30,7 @@
import android.util.Log;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.qs.QSTile;
import java.util.Arrays;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 5b5ecae..8a9a696 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -23,7 +23,7 @@
import android.widget.Switch;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -58,10 +58,10 @@
@Override
public void setListening(boolean listening) {
if (listening) {
- mController.addSettingsChangedCallback(mCallback);
+ mController.addCallback(mCallback);
mKeyguard.addCallback(mCallback);
} else {
- mController.removeSettingsChangedCallback(mCallback);
+ mController.removeCallback(mCallback);
mKeyguard.removeCallback(mCallback);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index c02e5ae..10fde30 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -23,7 +23,7 @@
import com.android.internal.app.NightDisplayController;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index 499eb50..cec5f15 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -24,7 +24,7 @@
import android.widget.Switch;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.RotationLockController;
@@ -61,9 +61,9 @@
public void setListening(boolean listening) {
if (mController == null) return;
if (listening) {
- mController.addRotationLockControllerCallback(mCallback);
+ mController.addCallback(mCallback);
} else {
- mController.removeRotationLockControllerCallback(mCallback);
+ mController.removeCallback(mCallback);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index cd09231c..91d38bd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -24,7 +24,7 @@
import android.view.ViewGroup;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.R;
import com.android.systemui.qs.PseudoGridView;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
index b5fbfe0..246c23e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
@@ -21,8 +21,8 @@
import android.provider.Settings;
import android.util.Pair;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
-import com.android.systemui.plugins.qs.QSContainer.DetailAdapter;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.plugins.qs.QS.DetailAdapter;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
@@ -67,9 +67,9 @@
@Override
public void setListening(boolean listening) {
if (listening) {
- mUserInfoController.addListener(this);
+ mUserInfoController.addCallback(this);
} else {
- mUserInfoController.remListener(this);
+ mUserInfoController.removeCallback(this);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 27306fc..3876c88 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -28,10 +28,10 @@
import android.widget.Switch;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.wifi.AccessPoint;
import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSContainer.DetailAdapter;
+import com.android.systemui.plugins.qs.QS.DetailAdapter;
import com.android.systemui.qs.QSDetailItems;
import com.android.systemui.qs.QSDetailItems.Item;
import com.android.systemui.qs.QSIconView;
@@ -70,9 +70,9 @@
@Override
public void setListening(boolean listening) {
if (listening) {
- mController.addSignalCallback(mSignalCallback);
+ mController.addCallback(mSignalCallback);
} else {
- mController.removeSignalCallback(mSignalCallback);
+ mController.removeCallback(mSignalCallback);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 459e8ec..ce7fbd3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -21,7 +21,7 @@
import android.widget.Switch;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.phone.ManagedProfileController;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index e203c2f..7655e6c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -41,7 +41,7 @@
import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.EventLogConstants;
import com.android.systemui.EventLogTags;
import com.android.systemui.R;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index ec99d20..af1823c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -40,7 +40,7 @@
import android.view.WindowManager.LayoutParams;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.DejankUtils;
import com.android.systemui.Interpolators;
import com.android.keyguard.LatencyTracker;
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 96644b5..9ed4924 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;
@@ -31,7 +32,7 @@
import android.widget.TextView;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsActivity;
@@ -70,11 +71,12 @@
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 @@
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 @@
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 @@
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 @@
}
@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 @@
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 @@
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 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/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index febeacb..3c7012a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -43,7 +43,7 @@
import android.widget.TextView;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 936354e..77c61f8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -47,7 +47,7 @@
import android.widget.ScrollView;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index d44aa84..71f559b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -34,7 +34,7 @@
import android.view.animation.Interpolator;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index e91c9d5..de7def6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -39,7 +39,7 @@
import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
index 3ed2698..e8039c3 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
@@ -33,7 +33,7 @@
import android.widget.ImageView;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
index 803fe48..901d47d 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
@@ -25,7 +25,7 @@
import android.widget.ImageView;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
/** A dialog that provides controls for adjusting the screen brightness. */
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index 15d26f9..2684722 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -35,7 +35,7 @@
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.settingslib.accessibility.AccessibilityUtils;
import com.android.systemui.R;
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 4e34bbc..47d2def 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -54,7 +54,7 @@
import android.widget.FrameLayout;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
import com.android.internal.policy.DockedDividerUtils;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 23acb1b..93ddd17 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -87,7 +87,7 @@
import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.widget.LockPatternUtils;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index caf5447..5173176 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -43,7 +43,7 @@
import android.widget.ImageView;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.NotificationColorUtil;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index f438762..f43fc40 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -59,7 +59,7 @@
import com.android.internal.app.AssistUtils;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto;
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index b10fb31..bb327ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -43,7 +43,7 @@
import android.widget.TextView;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.Utils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 74caa53..68d5cd4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -151,8 +151,8 @@
mBlockEthernet = blockEthernet;
mBlockWifi = blockWifi;
// Re-register to get new callbacks.
- mNC.removeSignalCallback(this);
- mNC.addSignalCallback(this);
+ mNC.removeCallback(this);
+ mNC.addCallback(this);
}
}
@@ -224,7 +224,7 @@
apply();
applyIconTint();
- mNC.addSignalCallback(this);
+ mNC.addCallback(this);
}
@Override
@@ -232,7 +232,7 @@
mMobileSignalGroup.removeAllViews();
TunerService.get(mContext).removeTunable(this);
mSC.removeCallback(this);
- mNC.removeSignalCallback(this);
+ mNC.removeCallback(this);
super.onDetachedFromWindow();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
index 4977202..fc39648 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
@@ -99,7 +99,7 @@
}
@Override
- public void addStateChangedCallback(BatteryController.BatteryStateChangeCallback cb) {
+ public void addCallback(BatteryController.BatteryStateChangeCallback cb) {
mChangeCallbacks.add(cb);
// There is no way to know if the phone is plugged in or charging via bluetooth, so pass
@@ -109,7 +109,7 @@
}
@Override
- public void removeStateChangedCallback(BatteryController.BatteryStateChangeCallback cb) {
+ public void removeCallback(BatteryController.BatteryStateChangeCallback cb) {
mChangeCallbacks.remove(cb);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
index baff680..1c8c317 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
@@ -30,7 +30,7 @@
import android.widget.LinearLayout;
import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSContainer.ActivityStarter;
+import com.android.systemui.plugins.qs.QS.ActivityStarter;
import java.net.URISyntaxException;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
index 66030b9..a3e1b3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
@@ -94,12 +94,12 @@
filter.addAction(BluetoothHeadsetClient.ACTION_AG_EVENT);
mContext.registerReceiver(this, filter);
- mController.addStateChangedCallback(this);
+ mController.addCallback(this);
}
public void stopListening() {
mContext.unregisterReceiver(this);
- mController.removeStateChangedCallback(this);
+ mController.removeCallback(this);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index b742479..a011162 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -42,7 +42,7 @@
host.getHotspotController().addCallback(mHotspotCallback);
}
if (!Prefs.getBoolean(context, Key.QS_DATA_SAVER_ADDED, false)) {
- host.getNetworkController().getDataSaverController().addListener(mDataSaverListener);
+ host.getNetworkController().getDataSaverController().addCallback(mDataSaverListener);
}
if (!Prefs.getBoolean(context, Key.QS_INVERT_COLORS_ADDED, false)) {
mColorsSetting = new SecureSetting(mContext, mHandler,
@@ -69,7 +69,10 @@
}
public void destroy() {
- // TODO: Remove any registered listeners.
+ mColorsSetting.setListening(false);
+ mHost.getHotspotController().removeCallback(mHotspotCallback);
+ mHost.getNetworkController().getDataSaverController().removeCallback(mDataSaverListener);
+ mHost.getManagedProfileController().removeCallback(mProfileCallback);
}
private final ManagedProfileController.Callback mProfileCallback =
@@ -105,7 +108,7 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- mHost.getNetworkController().getDataSaverController().remListener(
+ mHost.getNetworkController().getDataSaverController().removeCallback(
mDataSaverListener);
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index dfb06d7..dbae6b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -66,7 +66,7 @@
import com.android.systemui.plugins.IntentButtonProvider.IntentButton.IconState;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.PluginManager;
-import com.android.systemui.plugins.qs.QSContainer.ActivityStarter;
+import com.android.systemui.plugins.qs.QS.ActivityStarter;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.KeyguardIndicationController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index d94def9..e4c778c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -190,9 +190,9 @@
}
mBatteryListening = listening;
if (mBatteryListening) {
- mBatteryController.addStateChangedCallback(this);
+ mBatteryController.addCallback(this);
} else {
- mBatteryController.removeStateChangedCallback(this);
+ mBatteryController.removeCallback(this);
}
}
@@ -214,7 +214,7 @@
}
public void setUserInfoController(UserInfoController userInfoController) {
- userInfoController.addListener(new UserInfoController.OnUserInfoChangedListener() {
+ userInfoController.addCallback(new UserInfoController.OnUserInfoChangedListener() {
@Override
public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
mMultiUserAvatar.setImageDrawable(picture);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java
index df4566b..dd7f3cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java
@@ -46,7 +46,7 @@
BatteryController batteryController) {
mIconController = iconController;
mBatteryController = batteryController;
- batteryController.addStateChangedCallback(this);
+ batteryController.addCallback(this);
}
public void setFingerprintUnlockController(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java
index 951b096..1a46815 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java
@@ -24,11 +24,14 @@
import android.os.UserHandle;
import android.os.UserManager;
+import com.android.systemui.statusbar.phone.ManagedProfileController.Callback;
+import com.android.systemui.statusbar.policy.CallbackController;
+
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
-public class ManagedProfileController {
+public class ManagedProfileController implements CallbackController<Callback> {
private final List<Callback> mCallbacks = new ArrayList<>();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
index af9454c..4d4f9d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -30,7 +30,7 @@
import android.widget.FrameLayout;
import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSContainer.DetailAdapter;
+import com.android.systemui.plugins.qs.QS.DetailAdapter;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.UserSwitcherController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index 0f800bb..228e8ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -27,7 +27,7 @@
import android.view.ViewConfiguration;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index cda0bfe..69d76e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -21,6 +21,7 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
+import android.app.Fragment;
import android.app.StatusBarManager;
import android.content.Context;
import android.content.pm.ResolveInfo;
@@ -34,6 +35,7 @@
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
@@ -42,15 +44,15 @@
import com.android.internal.logging.MetricsLogger;
import com.android.keyguard.KeyguardStatusView;
-import com.android.systemui.AutoReinflateContainer;
-import com.android.systemui.AutoReinflateContainer.InflateListener;
import com.android.systemui.DejankUtils;
import com.android.systemui.EventLogConstants;
import com.android.systemui.EventLogTags;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.plugins.qs.QSContainer;
+import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
+import com.android.systemui.plugins.qs.QS;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.FlingAnimationUtils;
@@ -70,7 +72,7 @@
ExpandableView.OnHeightChangedListener,
View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener,
KeyguardAffordanceHelper.Callback, NotificationStackScrollLayout.OnEmptySpaceClickListener,
- HeadsUpManager.OnHeadsUpChangedListener, QSContainer.HeightListener {
+ HeadsUpManager.OnHeadsUpChangedListener, QS.HeightListener {
private static final boolean DEBUG = false;
@@ -92,8 +94,8 @@
private KeyguardAffordanceHelper mAfforanceHelper;
private KeyguardUserSwitcher mKeyguardUserSwitcher;
private KeyguardStatusBarView mKeyguardStatusBar;
- protected QSContainer mQsContainer;
- private AutoReinflateContainer mQsAutoReinflateContainer;
+ private QS mQs;
+ private FrameLayout mQsFrame;
private KeyguardStatusView mKeyguardStatusView;
private TextView mClockView;
private View mReserveNotificationSpace;
@@ -236,31 +238,19 @@
mKeyguardBottomArea.setAffordanceHelper(mAfforanceHelper);
mLastOrientation = getResources().getConfiguration().orientation;
- mQsAutoReinflateContainer =
- (AutoReinflateContainer) findViewById(R.id.qs_auto_reinflate_container);
- mQsAutoReinflateContainer.addInflateListener(new InflateListener() {
- @Override
- public void onInflated(View v) {
- mQsContainer = (QSContainer) v.findViewById(R.id.quick_settings_container);
- mQsContainer.setPanelView(NotificationPanelView.this);
- mQsContainer.getHeader().getExpandView()
- .setOnClickListener(NotificationPanelView.this);
+ mQsFrame = (FrameLayout) findViewById(R.id.qs_frame);
+ }
- // recompute internal state when qspanel height changes
- mQsContainer.addOnLayoutChangeListener(new OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom,
- int oldLeft, int oldTop, int oldRight, int oldBottom) {
- final int height = bottom - top;
- final int oldHeight = oldBottom - oldTop;
- if (height != oldHeight) {
- onQsHeightChanged();
- }
- }
- });
- mNotificationStackScroller.setQsContainer(mQsContainer);
- }
- });
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ FragmentHostManager.get(this).addTagListener(QS.TAG, mFragmentListener);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ FragmentHostManager.get(this).removeTagListener(QS.TAG, mFragmentListener);
}
@Override
@@ -288,12 +278,12 @@
int panelWidth = getResources().getDimensionPixelSize(R.dimen.notification_panel_width);
int panelGravity = getResources().getInteger(R.integer.notification_panel_layout_gravity);
FrameLayout.LayoutParams lp =
- (FrameLayout.LayoutParams) mQsAutoReinflateContainer.getLayoutParams();
+ (FrameLayout.LayoutParams) mQsFrame.getLayoutParams();
if (lp.width != panelWidth) {
lp.width = panelWidth;
lp.gravity = panelGravity;
- mQsAutoReinflateContainer.setLayoutParams(lp);
- mQsContainer.post(mUpdateHeader);
+ mQsFrame.setLayoutParams(lp);
+ mQs.getView().post(mUpdateHeader);
}
lp = (FrameLayout.LayoutParams) mNotificationStackScroller.getLayoutParams();
@@ -314,8 +304,10 @@
// Calculate quick setting heights.
int oldMaxHeight = mQsMaxExpansionHeight;
- mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQsContainer.getQsMinExpansionHeight();
- mQsMaxExpansionHeight = mQsContainer.getDesiredHeight();
+ if (mQs != null) {
+ mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight();
+ mQsMaxExpansionHeight = mQs.getDesiredHeight();
+ }
positionClockAndNotifications();
if (mQsExpanded && mQsFullyExpanded) {
mQsExpansionHeight = mQsMaxExpansionHeight;
@@ -337,8 +329,8 @@
// the desired height so when closing the QS detail, it stays smaller after the size change
// animation is finished but the detail view is still being animated away (this animation
// takes longer than the size change animation).
- if (mQsSizeChangeAnimator == null) {
- mQsContainer.setHeightOverride(mQsContainer.getDesiredHeight());
+ if (mQsSizeChangeAnimator == null && mQs != null) {
+ mQs.setHeightOverride(mQs.getDesiredHeight());
}
updateMaxHeadsUpTranslation();
}
@@ -357,7 +349,7 @@
requestScrollerTopPaddingUpdate(false /* animate */);
requestPanelHeightUpdate();
int height = (int) mQsSizeChangeAnimator.getAnimatedValue();
- mQsContainer.setHeightOverride(height);
+ mQs.setHeightOverride(height);
}
});
mQsSizeChangeAnimator.addListener(new AnimatorListenerAdapter() {
@@ -377,7 +369,7 @@
boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending();
int stackScrollerPadding;
if (mStatusBarState != StatusBarState.KEYGUARD) {
- stackScrollerPadding = mQsContainer.getHeader().getHeight() + mQsPeekHeight;
+ stackScrollerPadding = (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight;
mTopPaddingAdjustment = 0;
} else {
mClockPositionAlgorithm.setup(
@@ -490,7 +482,8 @@
public void setQsExpansionEnabled(boolean qsExpansionEnabled) {
mQsExpansionEnabled = qsExpansionEnabled;
- mQsContainer.setHeaderClickable(qsExpansionEnabled);
+ if (mQs == null) return;
+ mQs.setHeaderClickable(qsExpansionEnabled);
}
@Override
@@ -571,7 +564,7 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
- if (mBlockTouches || mQsContainer.isCustomizing()) {
+ if (mBlockTouches || mQs.isCustomizing()) {
return false;
}
initDownStates(event);
@@ -731,7 +724,7 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
- if (mBlockTouches || mQsContainer.isCustomizing()) {
+ if (mBlockTouches || (mQs != null && mQs.isCustomizing())) {
return false;
}
initDownStates(event);
@@ -804,10 +797,10 @@
}
private boolean isInQsArea(float x, float y) {
- return (x >= mQsAutoReinflateContainer.getX()
- && x <= mQsAutoReinflateContainer.getX() + mQsAutoReinflateContainer.getWidth())
+ return (x >= mQsFrame.getX()
+ && x <= mQsFrame.getX() + mQsFrame.getWidth())
&& (y <= mNotificationStackScroller.getBottomMostNotificationBottom()
- || y <= mQsContainer.getY() + mQsContainer.getHeight());
+ || y <= mQs.getView().getY() + mQs.getView().getHeight());
}
private boolean isOpenQsEvent(MotionEvent event) {
@@ -962,7 +955,8 @@
private void setOverScrolling(boolean overscrolling) {
mStackScrollerOverscrolling = overscrolling;
- mQsContainer.setOverscrolling(overscrolling);
+ if (mQs == null) return;
+ mQs.setOverscrolling(overscrolling);
}
private void onQsExpansionStarted() {
@@ -1000,24 +994,28 @@
mStatusBarState = statusBarState;
mKeyguardShowing = keyguardShowing;
- mQsContainer.setKeyguardShowing(mKeyguardShowing);
+ if (mQs != null) {
+ mQs.setKeyguardShowing(mKeyguardShowing);
+ }
if (oldState == StatusBarState.KEYGUARD
&& (goingToFullShade || statusBarState == StatusBarState.SHADE_LOCKED)) {
animateKeyguardStatusBarOut();
long delay = mStatusBarState == StatusBarState.SHADE_LOCKED
? 0 : mStatusBar.calculateGoingToFullShadeDelay();
- mQsContainer.animateHeaderSlidingIn(delay);
+ mQs.animateHeaderSlidingIn(delay);
} else if (oldState == StatusBarState.SHADE_LOCKED
&& statusBarState == StatusBarState.KEYGUARD) {
animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- mQsContainer.animateHeaderSlidingOut();
+ mQs.animateHeaderSlidingOut();
} else {
mKeyguardStatusBar.setAlpha(1f);
mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE);
if (keyguardShowing && oldState != mStatusBarState) {
mKeyguardBottomArea.onKeyguardShowingChanged();
- mQsContainer.hideImmediately();
+ if (mQs != null) {
+ mQs.hideImmediately();
+ }
}
}
if (keyguardShowing) {
@@ -1163,7 +1161,6 @@
}
private void updateQsState() {
- mQsContainer.setExpanded(mQsExpanded);
mNotificationStackScroller.setQsExpanded(mQsExpanded);
mNotificationStackScroller.setScrollingEnabled(
mStatusBarState != StatusBarState.KEYGUARD && (!mQsExpanded
@@ -1176,6 +1173,8 @@
if (mKeyguardUserSwitcher != null && mQsExpanded && !mStackScrollerOverscrolling) {
mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */);
}
+ if (mQs == null) return;
+ mQs.setExpanded(mQsExpanded);
}
private void setQsExpansion(float height) {
@@ -1222,11 +1221,12 @@
}
protected void updateQsExpansion() {
- mQsContainer.setQsExpansion(getQsExpansionFraction(), getHeaderTranslation());
+ if (mQs == null) return;
+ mQs.setQsExpansion(getQsExpansionFraction(), getHeaderTranslation());
}
private String getKeyguardOrLockScreenString() {
- if (mQsContainer.isCustomizing()) {
+ if (mQs.isCustomizing()) {
return getContext().getString(R.string.accessibility_desc_quick_settings_edit);
} else if (mStatusBarState == StatusBarState.KEYGUARD) {
return getContext().getString(R.string.accessibility_desc_lock_screen);
@@ -1357,9 +1357,9 @@
if (!mQsExpansionEnabled || mCollapsedOnDown) {
return false;
}
- View header = mKeyguardShowing ? mKeyguardStatusBar : mQsContainer.getHeader();
- boolean onHeader = x >= mQsAutoReinflateContainer.getX()
- && x <= mQsAutoReinflateContainer.getX() + mQsAutoReinflateContainer.getWidth()
+ View header = mKeyguardShowing ? mKeyguardStatusBar : mQs.getHeader();
+ final boolean onHeader = x >= mQsFrame.getX()
+ && x <= mQsFrame.getX() + mQsFrame.getWidth()
&& y >= header.getTop() && y <= header.getBottom();
if (mQsExpanded) {
return onHeader || (yDiff < 0 && isInQsArea(x, y));
@@ -1621,7 +1621,8 @@
}
// Since there are QS tiles in the header now, we need to make sure we start listening
// immediately so they can be up to date.
- mQsContainer.setHeaderListening(true);
+ if (mQs == null) return;
+ mQs.setHeaderListening(true);
}
@Override
@@ -1659,8 +1660,9 @@
}
private void setListening(boolean listening) {
- mQsContainer.setListening(listening);
mKeyguardStatusBar.setListening(listening);
+ if (mQs == null) return;
+ mQs.setListening(listening);
}
@Override
@@ -1748,7 +1750,7 @@
}
public void onQsHeightChanged() {
- mQsMaxExpansionHeight = mQsContainer.getDesiredHeight();
+ mQsMaxExpansionHeight = mQs != null ? mQs.getDesiredHeight() : 0;
if (mQsExpanded && mQsFullyExpanded) {
mQsExpansionHeight = mQsMaxExpansionHeight;
requestScrollerTopPaddingUpdate(false /* animate */);
@@ -2018,11 +2020,11 @@
}
public boolean isQsDetailShowing() {
- return mQsContainer.isShowingDetail();
+ return mQs.isShowingDetail();
}
public void closeQsDetail() {
- mQsContainer.closeDetail();
+ mQs.closeDetail();
}
@Override
@@ -2110,7 +2112,7 @@
private final Runnable mUpdateHeader = new Runnable() {
@Override
public void run() {
- mQsContainer.getHeader().updateEverything();
+ mQs.getHeader().updateEverything();
}
};
@@ -2257,7 +2259,7 @@
protected void setVerticalPanelTranslation(float translation) {
mNotificationStackScroller.setTranslationX(translation);
- mQsAutoReinflateContainer.setTranslationX(translation);
+ mQsFrame.setTranslationX(translation);
}
protected void updateExpandedHeight(float expandedHeight) {
@@ -2355,4 +2357,38 @@
public void setGroupManager(NotificationGroupManager groupManager) {
mGroupManager = groupManager;
}
+
+ private final FragmentListener mFragmentListener = new FragmentListener() {
+ @Override
+ public void onFragmentViewCreated(String tag, Fragment fragment) {
+ mQs = (QS) fragment;
+ mQs.setPanelView(NotificationPanelView.this);
+ mQs.getHeader().getExpandView().setOnClickListener(NotificationPanelView.this);
+ mQs.setHeaderClickable(mQsExpansionEnabled);
+ mQs.setKeyguardShowing(mKeyguardShowing);
+ mQs.setOverscrolling(mStackScrollerOverscrolling);
+
+ // recompute internal state when qspanel height changes
+ mQs.getView().addOnLayoutChangeListener(
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+ final int height = bottom - top;
+ final int oldHeight = oldBottom - oldTop;
+ if (height != oldHeight) {
+ onQsHeightChanged();
+ }
+ });
+ mNotificationStackScroller.setQsContainer((ViewGroup) mQs.getView());
+ updateQsExpansion();
+ }
+
+ @Override
+ public void onFragmentViewDestroyed(String tag, Fragment fragment) {
+ // Manual handling of fragment lifecycle is only required because this bridges
+ // non-fragment and fragment code. Once we are using a fragment for the notification
+ // panel, mQs will not need to be null cause it will be tied to the same lifecycle.
+ if (fragment == mQs) {
+ mQs = null;
+ }
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index 8b1fcd6..c85584e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import android.app.Fragment;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Canvas;
@@ -25,18 +26,17 @@
import android.view.WindowInsets;
import android.widget.FrameLayout;
-import com.android.systemui.AutoReinflateContainer;
import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSContainer;
+import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.plugins.qs.QS;
/**
* The container with notification stack scroller and quick settings inside.
*/
public class NotificationsQuickSettingsContainer extends FrameLayout
- implements ViewStub.OnInflateListener, AutoReinflateContainer.InflateListener {
+ implements ViewStub.OnInflateListener, FragmentHostManager.FragmentListener {
-
- private AutoReinflateContainer mQsContainer;
+ private FrameLayout mQsFrame;
private View mUserSwitcher;
private View mStackScroller;
private View mKeyguardStatusBar;
@@ -54,8 +54,7 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mQsContainer = (AutoReinflateContainer) findViewById(R.id.qs_auto_reinflate_container);
- mQsContainer.addInflateListener(this);
+ mQsFrame = (FrameLayout) findViewById(R.id.qs_frame);
mStackScroller = findViewById(R.id.notification_stack_scroller);
mStackScrollerMargin = ((LayoutParams) mStackScroller.getLayoutParams()).bottomMargin;
mKeyguardStatusBar = findViewById(R.id.keyguard_header);
@@ -65,9 +64,21 @@
}
@Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ FragmentHostManager.get(this).addTagListener(QS.TAG, this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ FragmentHostManager.get(this).removeTagListener(QS.TAG, this);
+ }
+
+ @Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- reloadWidth(mQsContainer);
+ reloadWidth(mQsFrame);
reloadWidth(mStackScroller);
}
@@ -91,11 +102,11 @@
boolean statusBarVisible = mKeyguardStatusBar.getVisibility() == View.VISIBLE;
final boolean qsBottom = mQsExpanded && !mCustomizerAnimating;
- View stackQsTop = qsBottom ? mStackScroller : mQsContainer;
- View stackQsBottom = !qsBottom ? mStackScroller : mQsContainer;
+ View stackQsTop = qsBottom ? mStackScroller : mQsFrame;
+ View stackQsBottom = !qsBottom ? mStackScroller : mQsFrame;
// Invert the order of the scroll view and user switcher such that the notifications receive
// touches first but the panel gets drawn above.
- if (child == mQsContainer) {
+ if (child == mQsFrame) {
return super.drawChild(canvas, userSwitcherVisible && statusBarVisible ? mUserSwitcher
: statusBarVisible ? mKeyguardStatusBar
: userSwitcherVisible ? mUserSwitcher
@@ -129,8 +140,8 @@
}
@Override
- public void onInflated(View v) {
- QSContainer container = (QSContainer) v;
+ public void onFragmentViewCreated(String tag, Fragment fragment) {
+ QS container = (QS) fragment;
container.setContainer(this);
}
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 c33d91a..31a93f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -90,7 +90,6 @@
import android.os.UserManager;
import android.os.Vibrator;
import android.provider.Settings;
-import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.telecom.TelecomManager;
@@ -118,7 +117,7 @@
import android.widget.TextView;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.keyguard.KeyguardHostView.OnDismissAction;
@@ -126,8 +125,6 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.AutoReinflateContainer;
-import com.android.systemui.AutoReinflateContainer.InflateListener;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.DemoMode;
import com.android.systemui.EventLogConstants;
@@ -141,11 +138,13 @@
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
+import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.fragments.PluginFragmentListener;
import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.plugins.qs.QSContainer.ActivityStarter;
-import com.android.systemui.plugins.qs.QSContainer.BaseStatusBarHeader;
-import com.android.systemui.plugins.qs.QSContainer;
-import com.android.systemui.qs.QSContainerImpl;
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.plugins.qs.QS.ActivityStarter;
+import com.android.systemui.plugins.qs.QS.BaseStatusBarHeader;
+import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.recents.events.EventBus;
@@ -561,6 +560,7 @@
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;
@@ -874,7 +874,7 @@
mLocationController = new LocationControllerImpl(mContext,
mHandlerThread.getLooper()); // will post a notification
mBatteryController = createBatteryController();
- mBatteryController.addStateChangedCallback(new BatteryStateChangeCallback() {
+ mBatteryController.addCallback(new BatteryStateChangeCallback() {
@Override
public void onPowerSaveChanged(boolean isPowerSave) {
mHandler.post(mCheckBarModes);
@@ -922,9 +922,11 @@
}
// Set up the quick settings tile panel
- AutoReinflateContainer container = (AutoReinflateContainer) mStatusBarWindow.findViewById(
- R.id.qs_auto_reinflate_container);
+ View container = mStatusBarWindow.findViewById(R.id.qs_frame);
if (container != null) {
+ FragmentHostManager fragmentHostManager = FragmentHostManager.get(container);
+ new PluginFragmentListener(container, QS.TAG, R.id.qs_frame, QSFragment.class, QS.class)
+ .startListening(QS.ACTION, QS.VERSION);
final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,
mBluetoothController, mLocationController, mRotationLockController,
mNetworkController, mZenModeController, mHotspotController,
@@ -933,21 +935,17 @@
mSecurityController, mBatteryController, mIconController,
mNextAlarmController);
mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow);
- container.addInflateListener(new InflateListener() {
- @Override
- public void onInflated(View v) {
- QSContainer qsContainer = (QSContainer) v.findViewById(
- R.id.quick_settings_container);
- if (qsContainer instanceof QSContainerImpl) {
- ((QSContainerImpl) qsContainer).setHost(qsh);
- mQSPanel = ((QSContainerImpl) qsContainer).getQsPanel();
- mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
- mKeyguardStatusBar.setQSPanel(mQSPanel);
- }
- mHeader = qsContainer.getHeader();
- initSignalCluster(mHeader);
- mHeader.setActivityStarter(PhoneStatusBar.this);
+ fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {
+ QS qs = (QS) f;
+ if (qs instanceof QSFragment) {
+ ((QSFragment) qs).setHost(qsh);
+ mQSPanel = ((QSFragment) qs).getQsPanel();
+ mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
+ mKeyguardStatusBar.setQSPanel(mQSPanel);
}
+ mHeader = qs.getHeader();
+ initSignalCluster(mHeader);
+ mHeader.setActivityStarter(PhoneStatusBar.this);
});
}
@@ -996,6 +994,12 @@
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();
@@ -1030,7 +1034,7 @@
if (emergencyViewStub != null) {
((ViewStub) emergencyViewStub).inflate();
}
- mNetworkController.addSignalCallback(new NetworkController.SignalCallback() {
+ mNetworkController.addCallback(new NetworkController.SignalCallback() {
@Override
public void setIsAirplaneMode(NetworkController.IconState icon) {
recomputeDisableFlags(true /* animate */);
@@ -1649,9 +1653,9 @@
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 +3556,14 @@
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 +3641,10 @@
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);
@@ -3974,9 +3982,9 @@
(SignalClusterView) mKeyguardStatusBar.findViewById(R.id.signal_cluster);
final SignalClusterView signalClusterQs =
(SignalClusterView) mHeader.findViewById(R.id.signal_cluster);
- mNetworkController.removeSignalCallback(signalCluster);
- mNetworkController.removeSignalCallback(signalClusterKeyguard);
- mNetworkController.removeSignalCallback(signalClusterQs);
+ mNetworkController.removeCallback(signalCluster);
+ mNetworkController.removeCallback(signalClusterKeyguard);
+ mNetworkController.removeCallback(signalClusterQs);
if (mQSPanel != null && mQSPanel.getHost() != null) {
mQSPanel.getHost().destroy();
}
@@ -4249,7 +4257,7 @@
}
}, delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
}
- } else {
+ } else if (!mNotificationPanel.isCollapsing()) {
instantCollapseNotificationPanel();
}
updateKeyguardState(staying, false /* fromShadeLocked */);
@@ -4876,7 +4884,7 @@
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/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 7187ec2..032c86b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -47,7 +47,6 @@
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
import com.android.systemui.statusbar.policy.RotationLockController;
import com.android.systemui.statusbar.policy.UserInfoController;
@@ -110,7 +109,7 @@
mCast = cast;
mHotspot = hotspot;
mBluetooth = bluetooth;
- mBluetooth.addStateChangedCallback(this);
+ mBluetooth.addCallback(this);
mNextAlarm = nextAlarm;
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mUserInfoController = userInfoController;
@@ -131,7 +130,7 @@
mSlotHeadset = context.getString(com.android.internal.R.string.status_bar_headset);
mSlotDataSaver = context.getString(com.android.internal.R.string.status_bar_data_saver);
- mRotationLockController.addRotationLockControllerCallback(this);
+ mRotationLockController.addCallback(this);
// listen for broadcasts
IntentFilter filter = new IntentFilter();
@@ -162,7 +161,7 @@
// Alarm clock
mIconController.setIcon(mSlotAlarmClock, R.drawable.stat_sys_alarm, null);
mIconController.setIconVisibility(mSlotAlarmClock, false);
- mNextAlarm.addStateChangedCallback(mNextAlarmCallback);
+ mNextAlarm.addCallback(mNextAlarmCallback);
// zen
mIconController.setIcon(mSlotZen, R.drawable.stat_sys_zen_important, null);
@@ -193,7 +192,7 @@
mIconController.setIcon(mSlotDataSaver, R.drawable.stat_sys_data_saver,
context.getString(R.string.accessibility_data_saver_on));
mIconController.setIconVisibility(mSlotDataSaver, false);
- mDataSaver.addListener(this);
+ mDataSaver.addCallback(this);
}
public void setStatusBarKeyguardViewManager(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index da698d8..fc15477 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -113,6 +113,7 @@
private final AutoTileManager mAutoTiles;
private final ManagedProfileController mProfileController;
private final NextAlarmController mNextAlarmController;
+ private final HandlerThread mHandlerThread;
private View mHeader;
private int mCurrentUser;
@@ -144,10 +145,10 @@
mNextAlarmController = nextAlarmController;
mProfileController = new ManagedProfileController(this);
- final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName(),
+ mHandlerThread = new HandlerThread(QSTileHost.class.getSimpleName(),
Process.THREAD_PRIORITY_BACKGROUND);
- ht.start();
- mLooper = ht.getLooper();
+ mHandlerThread.start();
+ mLooper = mHandlerThread.getLooper();
mServices = new TileServices(this, mLooper);
@@ -169,8 +170,11 @@
}
public void destroy() {
+ mHandlerThread.quitSafely();
+ mTiles.values().forEach(tile -> tile.destroy());
mAutoTiles.destroy();
TunerService.get(mContext).removeTunable(this);
+ mServices.destroy();
}
@Override
@@ -321,12 +325,11 @@
final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
int currentUser = ActivityManager.getCurrentUser();
if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
- for (Map.Entry<String, QSTile<?>> tile : mTiles.entrySet()) {
- if (!tileSpecs.contains(tile.getKey())) {
- if (DEBUG) Log.d(TAG, "Destroying tile: " + tile.getKey());
- tile.getValue().destroy();
- }
- }
+ mTiles.entrySet().stream().filter(tile -> !tileSpecs.contains(tile.getKey())).forEach(
+ tile -> {
+ if (DEBUG) Log.d(TAG, "Destroying tile: " + tile.getKey());
+ tile.getValue().destroy();
+ });
final LinkedHashMap<String, QSTile<?>> newTiles = new LinkedHashMap<>();
for (String tileSpec : tileSpecs) {
QSTile<?> tile = mTiles.get(tileSpec);
@@ -342,9 +345,13 @@
if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec);
try {
tile = createTile(tileSpec);
- if (tile != null && tile.isAvailable()) {
- tile.setTileSpec(tileSpec);
- newTiles.put(tileSpec, tile);
+ if (tile != null) {
+ if (tile.isAvailable()) {
+ tile.setTileSpec(tileSpec);
+ newTiles.put(tileSpec, tile);
+ } else {
+ tile.destroy();
+ }
}
} catch (Throwable t) {
Log.w(TAG, "Error creating tile for spec: " + tileSpec, t);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
index a8b0122..28aed87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -33,14 +33,14 @@
import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto;
import com.android.keyguard.KeyguardStatusView;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSContainer.ActivityStarter;
-import com.android.systemui.plugins.qs.QSContainer.BaseStatusBarHeader;
+import com.android.systemui.plugins.qs.QS.ActivityStarter;
+import com.android.systemui.plugins.qs.QS.BaseStatusBarHeader;
import com.android.systemui.qs.QSPanel;
-import com.android.systemui.plugins.qs.QSContainer.Callback;
+import com.android.systemui.plugins.qs.QS.Callback;
import com.android.systemui.qs.QuickQSPanel;
import com.android.systemui.qs.TouchAnimator;
import com.android.systemui.qs.TouchAnimator.Builder;
@@ -238,7 +238,7 @@
@Override
protected void onDetachedFromWindow() {
setListening(false);
- mHost.getUserInfoController().remListener(this);
+ mHost.getUserInfoController().removeCallback(this);
mHost.getNetworkController().removeEmergencyListener(this);
super.onDetachedFromWindow();
}
@@ -290,9 +290,9 @@
private void updateListeners() {
if (mListening) {
- mNextAlarmController.addStateChangedCallback(this);
+ mNextAlarmController.addCallback(this);
} else {
- mNextAlarmController.removeStateChangedCallback(this);
+ mNextAlarmController.removeCallback(this);
}
}
@@ -368,7 +368,7 @@
}
public void setUserInfoController(UserInfoController userInfoController) {
- userInfoController.addListener(this);
+ userInfoController.addCallback(this);
}
@Override
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 f5c5e56..69decd7 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 @@
if (!afterKeyguardGone) {
mBouncer.showWithDismissAction(r, cancelAction);
} else {
- mBouncer.show(false /* resetSecuritySelection */);
mAfterKeyguardGoneAction = r;
+ mBouncer.show(false /* resetSecuritySelection */);
}
}
updateStates();
@@ -235,10 +235,6 @@
mDeviceWillWakeUp = !mDeviceInteractive;
}
- public void verifyUnlock() {
- dismiss();
- }
-
public void setNeedsInput(boolean needsInput) {
mStatusBarWindowManager.setKeyguardNeedsInput(needsInput);
}
@@ -333,6 +329,7 @@
}
});
} else {
+ executeAfterKeyguardGoneAction();
if (mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING) {
mFingerprintUnlockController.startKeyguardFadingAway();
mPhoneStatusBar.setKeyguardFadingAway(startTime, 0, 240);
@@ -372,7 +369,6 @@
mStatusBarWindowManager.setKeyguardShowing(false);
mBouncer.hide(true /* destroyView */);
mViewMediatorCallback.keyguardGone();
- executeAfterKeyguardGoneAction();
updateStates();
}
}
@@ -423,6 +419,10 @@
/**
* 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/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 559436b..19dcf03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -17,11 +17,13 @@
package com.android.systemui.statusbar.policy;
import com.android.systemui.DemoMode;
+import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-public interface BatteryController extends DemoMode {
+public interface BatteryController extends DemoMode,
+ CallbackController<BatteryStateChangeCallback> {
/**
* Prints the current state of the {@link BatteryController} to the given {@link PrintWriter}.
*/
@@ -37,9 +39,6 @@
*/
boolean isPowerSave();
- void addStateChangedCallback(BatteryStateChangeCallback cb);
- void removeStateChangedCallback(BatteryStateChangeCallback cb);
-
/**
* A listener that will be notified whenever a change in battery level or power save mode
* has occurred.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 6726c92..fc86ac3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -89,7 +89,7 @@
}
@Override
- public void addStateChangedCallback(BatteryController.BatteryStateChangeCallback cb) {
+ public void addCallback(BatteryController.BatteryStateChangeCallback cb) {
synchronized (mChangeCallbacks) {
mChangeCallbacks.add(cb);
}
@@ -99,7 +99,7 @@
}
@Override
- public void removeStateChangedCallback(BatteryController.BatteryStateChangeCallback cb) {
+ public void removeCallback(BatteryController.BatteryStateChangeCallback cb) {
synchronized (mChangeCallbacks) {
mChangeCallbacks.remove(cb);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
index 08675c4..4c1c378 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
@@ -17,13 +17,11 @@
package com.android.systemui.statusbar.policy;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.systemui.statusbar.policy.BluetoothController.Callback;
import java.util.Collection;
-public interface BluetoothController {
- void addStateChangedCallback(Callback callback);
- void removeStateChangedCallback(Callback callback);
-
+public interface BluetoothController extends CallbackController<Callback> {
boolean isBluetoothSupported();
boolean isBluetoothEnabled();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 4f880b4..15c4afe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -105,13 +105,13 @@
}
@Override
- public void addStateChangedCallback(Callback cb) {
+ public void addCallback(Callback cb) {
mHandler.obtainMessage(H.MSG_ADD_CALLBACK, cb).sendToTarget();
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
@Override
- public void removeStateChangedCallback(Callback cb) {
+ public void removeCallback(Callback cb) {
mHandler.obtainMessage(H.MSG_REMOVE_CALLBACK, cb).sendToTarget();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java
new file mode 100644
index 0000000..9042ca6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java
@@ -0,0 +1,20 @@
+/*
+ * 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.statusbar.policy;
+
+public interface CallbackController<T> {
+ void addCallback(T listener);
+ void removeCallback(T listener);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
index 7713e57..6988af7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
@@ -16,11 +16,11 @@
package com.android.systemui.statusbar.policy;
+import com.android.systemui.statusbar.policy.CastController.Callback;
+
import java.util.Set;
-public interface CastController {
- void addCallback(Callback callback);
- void removeCallback(Callback callback);
+public interface CastController extends CallbackController<Callback> {
void setDiscovering(boolean request);
void setCurrentUserId(int currentUserId);
Set<CastDevice> getCastDevices();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
index 0fc71d3..e5f1e68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
@@ -21,9 +21,11 @@
import android.os.Looper;
import android.os.RemoteException;
+import com.android.systemui.statusbar.policy.DataSaverController.Listener;
+
import java.util.ArrayList;
-public class DataSaverController {
+public class DataSaverController implements CallbackController<Listener> {
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final ArrayList<Listener> mListeners = new ArrayList<>();
@@ -41,7 +43,7 @@
}
}
- public void addListener(Listener listener) {
+ public void addCallback(Listener listener) {
synchronized (mListeners) {
mListeners.add(listener);
if (mListeners.size() == 1) {
@@ -51,7 +53,7 @@
listener.onDataSaverChanged(isDataSaverEnabled());
}
- public void remListener(Listener listener) {
+ public void removeCallback(Listener listener) {
synchronized (mListeners) {
mListeners.remove(listener);
if (mListeners.size() == 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
index 4e9fc76..0f77b03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
@@ -27,6 +27,8 @@
import android.text.TextUtils;
import android.util.Log;
+import com.android.systemui.statusbar.policy.FlashlightController.FlashlightListener;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -35,7 +37,7 @@
/**
* Manages the flashlight.
*/
-public class FlashlightController {
+public class FlashlightController implements CallbackController<FlashlightListener> {
private static final String TAG = "FlashlightController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -112,7 +114,7 @@
return mTorchAvailable;
}
- public void addListener(FlashlightListener l) {
+ public void addCallback(FlashlightListener l) {
synchronized (mListeners) {
if (mCameraId == null) {
tryInitCamera();
@@ -122,7 +124,7 @@
}
}
- public void removeListener(FlashlightListener l) {
+ public void removeCallback(FlashlightListener l) {
synchronized (mListeners) {
cleanUpListenersLocked(l);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
index 4622ea4..daf9d6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
@@ -16,9 +16,9 @@
package com.android.systemui.statusbar.policy;
-public interface HotspotController {
- void addCallback(Callback callback);
- void removeCallback(Callback callback);
+import com.android.systemui.statusbar.policy.HotspotController.Callback;
+
+public interface HotspotController extends CallbackController<Callback> {
boolean isHotspotEnabled();
void setHotspotEnabled(boolean enabled);
boolean isHotspotSupported();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
index 44816f9..fafbdd1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
@@ -24,10 +24,12 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.settings.CurrentUserTracker;
+import com.android.systemui.statusbar.policy.KeyguardMonitor.Callback;
import java.util.ArrayList;
-public final class KeyguardMonitor extends KeyguardUpdateMonitorCallback {
+public class KeyguardMonitor extends KeyguardUpdateMonitorCallback
+ implements CallbackController<Callback> {
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
index 29a8981..9a5f1b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
@@ -16,11 +16,11 @@
package com.android.systemui.statusbar.policy;
-public interface LocationController {
+import com.android.systemui.statusbar.policy.LocationController.LocationSettingsChangeCallback;
+
+public interface LocationController extends CallbackController<LocationSettingsChangeCallback> {
boolean isLocationEnabled();
boolean setLocationEnabled(boolean enabled);
- void addSettingsChangedCallback(LocationSettingsChangeCallback cb);
- void removeSettingsChangedCallback(LocationSettingsChangeCallback cb);
/**
* A callback for change in location settings (the user has enabled/disabled location).
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index 8d84be4..cc61605 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -82,12 +82,12 @@
/**
* Add a callback to listen for changes in location settings.
*/
- public void addSettingsChangedCallback(LocationSettingsChangeCallback cb) {
+ public void addCallback(LocationSettingsChangeCallback cb) {
mSettingsChangeCallbacks.add(cb);
mHandler.sendEmptyMessage(H.MSG_LOCATION_SETTINGS_CHANGED);
}
- public void removeSettingsChangedCallback(LocationSettingsChangeCallback cb) {
+ public void removeCallback(LocationSettingsChangeCallback cb) {
mSettingsChangeCallbacks.remove(cb);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 5f1b871..082fe82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -21,14 +21,15 @@
import android.telephony.SubscriptionInfo;
import com.android.settingslib.net.DataUsageController;
import com.android.settingslib.wifi.AccessPoint;
+import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import java.util.List;
-public interface NetworkController {
+public interface NetworkController extends CallbackController<SignalCallback> {
boolean hasMobileDataFeature();
- void addSignalCallback(SignalCallback cb);
- void removeSignalCallback(SignalCallback cb);
+ void addCallback(SignalCallback cb);
+ void removeCallback(SignalCallback cb);
void setWifiEnabled(boolean enabled);
void onUserSwitched(int newUserId);
AccessPointController getAccessPointController();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 37e6a2a..1a9756f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -322,7 +322,7 @@
mCallbackHandler.setEmergencyCallsOnly(mIsEmergency);
}
- public void addSignalCallback(SignalCallback cb) {
+ public void addCallback(SignalCallback cb) {
cb.setSubs(mCurrentSubscriptions);
cb.setIsAirplaneMode(new IconState(mAirplaneMode,
TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext));
@@ -336,7 +336,7 @@
}
@Override
- public void removeSignalCallback(SignalCallback cb) {
+ public void removeCallback(SignalCallback cb) {
mCallbackHandler.setListening(cb, false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmController.java
index 787acc5..28935bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmController.java
@@ -23,11 +23,14 @@
import android.content.IntentFilter;
import android.os.UserHandle;
+import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-public class NextAlarmController extends BroadcastReceiver {
+public class NextAlarmController extends BroadcastReceiver
+ implements CallbackController<NextAlarmChangeCallback> {
private final ArrayList<NextAlarmChangeCallback> mChangeCallbacks = new ArrayList<>();
@@ -48,12 +51,12 @@
pw.print(" mNextAlarm="); pw.println(mNextAlarm);
}
- public void addStateChangedCallback(NextAlarmChangeCallback cb) {
+ public void addCallback(NextAlarmChangeCallback cb) {
mChangeCallbacks.add(cb);
cb.onNextAlarmChanged(mNextAlarm);
}
- public void removeStateChangedCallback(NextAlarmChangeCallback cb) {
+ public void removeCallback(NextAlarmChangeCallback cb) {
mChangeCallbacks.remove(cb);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 7b1f707..44ec283 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -50,7 +50,7 @@
import android.widget.TextView;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
index 93c4691..722874b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
@@ -16,13 +16,14 @@
package com.android.systemui.statusbar.policy;
-public interface RotationLockController extends Listenable {
+import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
+
+public interface RotationLockController extends Listenable,
+ CallbackController<RotationLockControllerCallback> {
int getRotationLockOrientation();
boolean isRotationLockAffordanceVisible();
boolean isRotationLocked();
void setRotationLocked(boolean locked);
- void addRotationLockControllerCallback(RotationLockControllerCallback callback);
- void removeRotationLockControllerCallback(RotationLockControllerCallback callback);
public interface RotationLockControllerCallback {
void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
index c3bcd94..4f96496 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
@@ -42,12 +42,12 @@
setListening(true);
}
- public void addRotationLockControllerCallback(RotationLockControllerCallback callback) {
+ public void addCallback(RotationLockControllerCallback callback) {
mCallbacks.add(callback);
notifyChanged(callback);
}
- public void removeRotationLockControllerCallback(RotationLockControllerCallback callback) {
+ public void removeCallback(RotationLockControllerCallback callback) {
mCallbacks.remove(callback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
index 014afae..43ced48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
@@ -15,7 +15,9 @@
*/
package com.android.systemui.statusbar.policy;
-public interface SecurityController {
+import com.android.systemui.statusbar.policy.SecurityController.SecurityControllerCallback;
+
+public interface SecurityController extends CallbackController<SecurityControllerCallback> {
/** Whether the device has device owner, even if not on this user. */
boolean isDeviceManaged();
boolean hasProfileOwner();
@@ -29,9 +31,6 @@
String getProfileVpnName();
void onUserSwitched(int newUserId);
- void addCallback(SecurityControllerCallback callback);
- void removeCallback(SecurityControllerCallback callback);
-
public interface SecurityControllerCallback {
void onStateChanged();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
index 4a6e215..17b22df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.policy;
-import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -38,10 +37,11 @@
import com.android.internal.util.UserIcons;
import com.android.settingslib.drawable.UserIconDrawable;
import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
import java.util.ArrayList;
-public final class UserInfoController {
+public class UserInfoController implements CallbackController<OnUserInfoChangedListener> {
private static final String TAG = "UserInfoController";
@@ -67,12 +67,12 @@
null, null);
}
- public void addListener(OnUserInfoChangedListener callback) {
+ public void addCallback(OnUserInfoChangedListener callback) {
mCallbacks.add(callback);
callback.onUserInfoChanged(mUserName, mUserDrawable, mUserAccount);
}
- public void remListener(OnUserInfoChangedListener callback) {
+ public void removeCallback(OnUserInfoChangedListener callback) {
mCallbacks.remove(callback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 30d1c54..9d9c908 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -48,16 +48,17 @@
import android.view.ViewGroup;
import android.widget.BaseAdapter;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.UserIcons;
import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.GuestResumeSessionReceiver;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.SystemUISecondaryUserService;
-import com.android.systemui.plugins.qs.QSContainer.DetailAdapter;
+import com.android.systemui.plugins.qs.QS.DetailAdapter;
import com.android.systemui.qs.tiles.UserDetailView;
-import com.android.systemui.plugins.qs.QSContainer.ActivityStarter;
+import com.android.systemui.plugins.qs.QS.ActivityStarter;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import java.io.FileDescriptor;
@@ -640,28 +641,43 @@
refreshUsers(UserHandle.USER_ALL);
}
+ @VisibleForTesting
+ public void addAdapter(WeakReference<BaseUserAdapter> adapter) {
+ mAdapters.add(adapter);
+ }
+
+ @VisibleForTesting
+ public KeyguardMonitor getKeyguardMonitor() {
+ return mKeyguardMonitor;
+ }
+
+ @VisibleForTesting
+ public ArrayList<UserRecord> getUsers() {
+ return mUsers;
+ }
+
public static abstract class BaseUserAdapter extends BaseAdapter {
final UserSwitcherController mController;
protected BaseUserAdapter(UserSwitcherController controller) {
mController = controller;
- controller.mAdapters.add(new WeakReference<>(this));
+ controller.addAdapter(new WeakReference<>(this));
}
@Override
public int getCount() {
- boolean secureKeyguardShowing = mController.mKeyguardMonitor.isShowing()
- && mController.mKeyguardMonitor.isSecure()
- && !mController.mKeyguardMonitor.canSkipBouncer();
+ boolean secureKeyguardShowing = mController.getKeyguardMonitor().isShowing()
+ && mController.getKeyguardMonitor().isSecure()
+ && !mController.getKeyguardMonitor().canSkipBouncer();
if (!secureKeyguardShowing) {
- return mController.mUsers.size();
+ return mController.getUsers().size();
}
// The lock screen is secure and showing. Filter out restricted records.
- final int N = mController.mUsers.size();
+ final int N = mController.getUsers().size();
int count = 0;
for (int i = 0; i < N; i++) {
- if (mController.mUsers.get(i).isRestricted) {
+ if (mController.getUsers().get(i).isRestricted) {
break;
} else {
count++;
@@ -672,7 +688,7 @@
@Override
public UserRecord getItem(int position) {
- return mController.mUsers.get(position);
+ return mController.getUsers().get(position);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
index 0e91b0b..bcdb62d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
@@ -22,9 +22,9 @@
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.ZenRule;
-public interface ZenModeController {
- void addCallback(Callback callback);
- void removeCallback(Callback callback);
+import com.android.systemui.statusbar.policy.ZenModeController.Callback;
+
+public interface ZenModeController extends CallbackController<Callback> {
void setZen(int zen, Uri conditionId, String reason);
int getZen();
ZenRule getManualRule();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index da58d9e..72a0e59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -57,7 +57,7 @@
import android.widget.ScrollView;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.ExpandHelper;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
index 6e08139..9998283 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
@@ -21,7 +21,7 @@
import android.util.ArraySet;
import android.util.AttributeSet;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import static com.android.systemui.BatteryMeterDrawable.SHOW_PERCENT_SETTING;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
index 0a3197c..0a962f1 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
@@ -31,7 +31,7 @@
import android.view.MenuItem;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.DemoMode;
import com.android.systemui.R;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PowerNotificationControlsFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PowerNotificationControlsFragment.java
index 14fccf2..8740a3c 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/PowerNotificationControlsFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/PowerNotificationControlsFragment.java
@@ -16,7 +16,7 @@
package com.android.systemui.tuner;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import android.annotation.Nullable;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java b/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java
index f2f0382..dea2f50 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java
@@ -24,7 +24,7 @@
import android.util.AttributeSet;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.tuner.TunerService.Tunable;
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 0000000..e5bb3d5
--- /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 7f63418..f835e7d 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
@@ -27,7 +27,7 @@
import android.view.MenuItem;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.plugins.PluginPrefs;
@@ -102,7 +102,9 @@
TunerService.showResetRequest(getContext(), new Runnable() {
@Override
public void run() {
- getActivity().finish();
+ if (getActivity() != null) {
+ getActivity().finish();
+ }
}
});
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Events.java b/packages/SystemUI/src/com/android/systemui/volume/Events.java
index 8e0f9b8..ca53bc4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/Events.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/Events.java
@@ -23,7 +23,7 @@
import android.util.Log;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.volume.VolumeDialogController.State;
import java.util.Arrays;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 22d1309..87f6138 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -52,7 +52,7 @@
import android.widget.TextView;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.ZenModeController;
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index b5584f3..6e17cf4 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -25,6 +25,7 @@
<uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+ <uses-permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
index fccb2a2..f3be945 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
@@ -16,26 +16,24 @@
package com.android.keyguard;
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.mock;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import static junit.framework.Assert.*;
-import static org.mockito.Mockito.mock;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class KeyguardMessageAreaTest extends SysuiTestCase {
- private Context mContext = InstrumentationRegistry.getTargetContext();
private Handler mHandler = new Handler(Looper.getMainLooper());
private KeyguardMessageArea mMessageArea;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java b/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java
index 5cb5e68..cb0f7a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java
@@ -17,7 +17,7 @@
package com.android.systemui;
import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertTrue;
+
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyFloat;
import static org.mockito.Mockito.anyString;
@@ -28,27 +28,24 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
-import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class BatteryMeterDrawableTest {
+public class BatteryMeterDrawableTest extends SysuiTestCase {
- private Context mContext;
private Resources mResources;
private BatteryMeterDrawable mBatteryMeter;
@Before
public void setUp() throws Exception {
- mContext = InstrumentationRegistry.getTargetContext();
mResources = mContext.getResources();
mBatteryMeter = new BatteryMeterDrawable(mContext, 0);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/FragmentTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/FragmentTestCase.java
new file mode 100644
index 0000000..f87336c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/FragmentTestCase.java
@@ -0,0 +1,194 @@
+/*
+ * 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;
+
+import android.annotation.Nullable;
+import android.app.Fragment;
+import android.app.FragmentController;
+import android.app.FragmentHostCallback;
+import android.app.FragmentManagerNonConfig;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Parcelable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Base class for fragment class tests. Just adding one for any fragment will push it through
+ * general lifecycle events and ensure no basic leaks are happening. This class also implements
+ * the host for subclasses, so they can push it into desired states and do any unit testing
+ * required.
+ */
+public abstract class FragmentTestCase extends LeakCheckedTest {
+
+ private static final int VIEW_ID = 42;
+ private final Class<? extends Fragment> mCls;
+ private HandlerThread mHandlerThread;
+ private Handler mHandler;
+ private FrameLayout mView;
+ protected FragmentController mFragments;
+ protected Fragment mFragment;
+
+ public FragmentTestCase(Class<? extends Fragment> cls) {
+ mCls = cls;
+ }
+
+ @Before
+ public void setupFragment() throws IllegalAccessException, InstantiationException {
+ mView = new FrameLayout(mContext);
+ mView.setId(VIEW_ID);
+ mHandlerThread = new HandlerThread("FragmentTestThread");
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ mFragment = mCls.newInstance();
+ postAndWait(() -> {
+ mFragments = FragmentController.createController(new HostCallbacks());
+ mFragments.attachHost(null);
+ mFragments.getFragmentManager().beginTransaction()
+ .replace(VIEW_ID, mFragment)
+ .commit();
+ });
+ }
+
+ @After
+ public void tearDown() {
+ if (mFragments != null) {
+ // Set mFragments to null to let it know not to destroy.
+ postAndWait(() -> mFragments.dispatchDestroy());
+ }
+ mHandlerThread.quit();
+ }
+
+ @Test
+ public void testCreateDestroy() {
+ postAndWait(() -> mFragments.dispatchCreate());
+ destroyFragments();
+ }
+
+ @Test
+ public void testStartStop() {
+ postAndWait(() -> mFragments.dispatchStart());
+ postAndWait(() -> mFragments.dispatchStop());
+ }
+
+ @Test
+ public void testResumePause() {
+ postAndWait(() -> mFragments.dispatchResume());
+ postAndWait(() -> mFragments.dispatchPause());
+ }
+
+ @Test
+ public void testRecreate() {
+ postAndWait(() -> mFragments.dispatchResume());
+ postAndWait(() -> {
+ mFragments.dispatchPause();
+ Parcelable p = mFragments.saveAllState();
+ mFragments.dispatchDestroy();
+
+ mFragments = FragmentController.createController(new HostCallbacks());
+ mFragments.attachHost(null);
+ mFragments.restoreAllState(p, (FragmentManagerNonConfig) null);
+ mFragments.dispatchResume();
+ });
+ }
+
+ @Test
+ public void testMultipleResumes() {
+ postAndWait(() -> mFragments.dispatchResume());
+ postAndWait(() -> mFragments.dispatchStop());
+ postAndWait(() -> mFragments.dispatchResume());
+ }
+
+ protected void destroyFragments() {
+ postAndWait(() -> mFragments.dispatchDestroy());
+ mFragments = null;
+ }
+
+ protected void postAndWait(Runnable r) {
+ mHandler.post(r);
+ waitForFragments();
+ }
+
+ protected void waitForFragments() {
+ waitForIdleSync(mHandler);
+ }
+
+ private View findViewById(int id) {
+ return mView.findViewById(id);
+ }
+
+ private class HostCallbacks extends FragmentHostCallback<FragmentTestCase> {
+ public HostCallbacks() {
+ super(getTrackedContext(), FragmentTestCase.this.mHandler, 0);
+ }
+
+ @Override
+ public FragmentTestCase onGetHost() {
+ return FragmentTestCase.this;
+ }
+
+ @Override
+ public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ }
+
+ @Override
+ public boolean onShouldSaveFragmentState(Fragment fragment) {
+ return true; // True for now.
+ }
+
+ @Override
+ public LayoutInflater onGetLayoutInflater() {
+ return LayoutInflater.from(mContext);
+ }
+
+ @Override
+ public boolean onUseFragmentManagerInflaterFactory() {
+ return true;
+ }
+
+ @Override
+ public boolean onHasWindowAnimations() {
+ return false;
+ }
+
+ @Override
+ public int onGetWindowAnimations() {
+ return 0;
+ }
+
+ @Override
+ public void onAttachFragment(Fragment fragment) {
+ }
+
+ @Nullable
+ @Override
+ public View onFindViewById(int id) {
+ return FragmentTestCase.this.findViewById(id);
+ }
+
+ @Override
+ public boolean onHasView() {
+ return true;
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/LeakCheckedTest.java b/packages/SystemUI/tests/src/com/android/systemui/LeakCheckedTest.java
new file mode 100644
index 0000000..d64669d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/LeakCheckedTest.java
@@ -0,0 +1,287 @@
+/*
+ * 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;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentCallbacks;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.systemui.statusbar.phone.ManagedProfileController;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.DataSaverController;
+import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.statusbar.policy.CallbackController;
+import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Base class for tests to check if receivers are left registered, services bound, or other
+ * listeners listening.
+ */
+public class LeakCheckedTest extends SysuiTestCase {
+ private static final String TAG = "LeakCheckedTest";
+
+ private final Map<String, Tracker> mTrackers = new HashMap<>();
+ private final Map<Class, Object> mLeakCheckers = new ArrayMap<>();
+ private TrackingContext mTrackedContext;
+
+ @Rule
+ public TestWatcher successWatcher = new TestWatcher() {
+ @Override
+ protected void succeeded(Description description) {
+ verify();
+ }
+ };
+
+ @Before
+ public void setup() {
+ mTrackedContext = new TrackingContext(mContext);
+ addSupportedLeakCheckers();
+ }
+
+ public <T> T getLeakChecker(Class<T> cls) {
+ T obj = (T) mLeakCheckers.get(cls);
+ if (obj == null) {
+ Assert.fail(cls.getName() + " is not supported by LeakCheckedTest yet");
+ }
+ return obj;
+ }
+
+ public Context getTrackedContext() {
+ return mTrackedContext;
+ }
+
+ private Tracker getTracker(String tag) {
+ Tracker t = mTrackers.get(tag);
+ if (t == null) {
+ t = new Tracker();
+ mTrackers.put(tag, t);
+ }
+ return t;
+ }
+
+ public void verify() {
+ mTrackers.values().forEach(Tracker::verify);
+ }
+
+ public static class Tracker {
+ private Map<Object, LeakInfo> mObjects = new ArrayMap<>();
+
+ LeakInfo getLeakInfo(Object object) {
+ LeakInfo leakInfo = mObjects.get(object);
+ if (leakInfo == null) {
+ leakInfo = new LeakInfo();
+ mObjects.put(object, leakInfo);
+ }
+ return leakInfo;
+ }
+
+ private void verify() {
+ mObjects.values().forEach(LeakInfo::verify);
+ }
+ }
+
+ public static class LeakInfo {
+ private List<Throwable> mThrowables = new ArrayList<>();
+
+ private LeakInfo() {
+ }
+
+ private void addAllocation(Throwable t) {
+ // TODO: Drop off the first element in the stack trace here to have a cleaner stack.
+ mThrowables.add(t);
+ }
+
+ private void clearAllocations() {
+ mThrowables.clear();
+ }
+
+ public void verify() {
+ if (mThrowables.size() == 0) return;
+ Log.e(TAG, "Listener or binding not properly released");
+ for (Throwable t : mThrowables) {
+ Log.e(TAG, "Allocation found", t);
+ }
+ StringWriter writer = new StringWriter();
+ mThrowables.get(0).printStackTrace(new PrintWriter(writer));
+ Assert.fail("Listener or binding not properly released\n"
+ + writer.toString());
+ }
+ }
+
+ private void addSupportedLeakCheckers() {
+ addListening("bluetooth", BluetoothController.class);
+ addListening("location", LocationController.class);
+ addListening("rotation", RotationLockController.class);
+ addListening("zen", ZenModeController.class);
+ addListening("cast", CastController.class);
+ addListening("hotspot", HotspotController.class);
+ addListening("flashlight", FlashlightController.class);
+ addListening("user", UserInfoController.class);
+ addListening("keyguard", KeyguardMonitor.class);
+ addListening("battery", BatteryController.class);
+ addListening("security", SecurityController.class);
+ addListening("profile", ManagedProfileController.class);
+ addListening("alarm", NextAlarmController.class);
+ NetworkController network = addListening("network", NetworkController.class);
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ getTracker("emergency").getLeakInfo(invocation.getArguments()[0])
+ .addAllocation(new Throwable());
+ return null;
+ }
+ }).when(network).addEmergencyListener(any());
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ getTracker("emergency").getLeakInfo(invocation.getArguments()[0]).clearAllocations();
+ return null;
+ }
+ }).when(network).removeEmergencyListener(any());
+ DataSaverController datasaver = addListening("datasaver", DataSaverController.class);
+ when(network.getDataSaverController()).thenReturn(datasaver);
+ }
+
+ private <T extends CallbackController> T addListening(final String tag, Class<T> cls) {
+ T mock = mock(cls);
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ getTracker(tag).getLeakInfo(invocation.getArguments()[0])
+ .addAllocation(new Throwable());
+ return null;
+ }
+ }).when(mock).addCallback(any());
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ getTracker(tag).getLeakInfo(invocation.getArguments()[0]).clearAllocations();
+ return null;
+ }
+ }).when(mock).removeCallback(any());
+ mLeakCheckers.put(cls, mock);
+ return mock;
+ }
+
+ class TrackingContext extends ContextWrapper {
+ public TrackingContext(Context base) {
+ super(base);
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+ getTracker("receiver").getLeakInfo(receiver).addAllocation(new Throwable());
+ return super.registerReceiver(receiver, filter);
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+ String broadcastPermission, Handler scheduler) {
+ getTracker("receiver").getLeakInfo(receiver).addAllocation(new Throwable());
+ return super.registerReceiver(receiver, filter, broadcastPermission, scheduler);
+ }
+
+ @Override
+ public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+ IntentFilter filter, String broadcastPermission, Handler scheduler) {
+ getTracker("receiver").getLeakInfo(receiver).addAllocation(new Throwable());
+ return super.registerReceiverAsUser(receiver, user, filter, broadcastPermission,
+ scheduler);
+ }
+
+ @Override
+ public void unregisterReceiver(BroadcastReceiver receiver) {
+ getTracker("receiver").getLeakInfo(receiver).clearAllocations();
+ super.unregisterReceiver(receiver);
+ }
+
+ @Override
+ public boolean bindService(Intent service, ServiceConnection conn, int flags) {
+ getTracker("service").getLeakInfo(conn).addAllocation(new Throwable());
+ return super.bindService(service, conn, flags);
+ }
+
+ @Override
+ public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
+ Handler handler, UserHandle user) {
+ getTracker("service").getLeakInfo(conn).addAllocation(new Throwable());
+ return super.bindServiceAsUser(service, conn, flags, handler, user);
+ }
+
+ @Override
+ public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
+ UserHandle user) {
+ getTracker("service").getLeakInfo(conn).addAllocation(new Throwable());
+ return super.bindServiceAsUser(service, conn, flags, user);
+ }
+
+ @Override
+ public void unbindService(ServiceConnection conn) {
+ getTracker("service").getLeakInfo(conn).clearAllocations();
+ super.unbindService(conn);
+ }
+
+ @Override
+ public void registerComponentCallbacks(ComponentCallbacks callback) {
+ getTracker("component").getLeakInfo(callback).addAllocation(new Throwable());
+ super.registerComponentCallbacks(callback);
+ }
+
+ @Override
+ public void unregisterComponentCallbacks(ComponentCallbacks callback) {
+ getTracker("component").getLeakInfo(callback).clearAllocations();
+ super.unregisterComponentCallbacks(callback);
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index d943eb6..5dac8e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -20,6 +20,10 @@
import android.os.Handler;
import android.os.Looper;
import android.os.MessageQueue;
+
+import com.android.systemui.utils.TestableContext;
+
+import org.junit.After;
import org.junit.Before;
/**
@@ -28,11 +32,16 @@
public class SysuiTestCase {
private Handler mHandler;
- protected Context mContext;
+ protected TestableContext mContext;
@Before
public void SysuiSetup() throws Exception {
- mContext = InstrumentationRegistry.getTargetContext();
+ mContext = new TestableContext(InstrumentationRegistry.getTargetContext());
+ }
+
+ @After
+ public void cleanup() throws Exception {
+ mContext.getSettingsProvider().clearOverrides(this);
}
protected Context getContext() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
index 39b6412..5c87fb0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
@@ -17,9 +17,11 @@
package com.android.systemui.power;
import static android.test.MoreAsserts.assertNotEqual;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
@@ -29,9 +31,11 @@
import android.app.Notification;
import android.app.NotificationManager;
-import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -39,7 +43,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class PowerNotificationWarningsTest {
+public class PowerNotificationWarningsTest extends SysuiTestCase {
private final NotificationManager mMockNotificationManager = mock(NotificationManager.class);
private PowerNotificationWarnings mPowerNotificationWarnings;
@@ -47,7 +51,7 @@
public void setUp() throws Exception {
// Test Instance.
mPowerNotificationWarnings = new PowerNotificationWarnings(
- InstrumentationRegistry.getTargetContext(), mMockNotificationManager, null);
+ mContext, mMockNotificationManager, null);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
new file mode 100644
index 0000000..6ceaead
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.qs;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.os.Handler;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.systemui.FragmentTestCase;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.phone.QSTileHost;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+@RunWith(AndroidJUnit4.class)
+public class QSFragmentTest extends FragmentTestCase {
+
+ public QSFragmentTest() {
+ super(QSFragment.class);
+ }
+
+ @Test
+ public void testListening() {
+ QSFragment qs = (QSFragment) mFragment;
+ postAndWait(() -> mFragments.dispatchResume());
+ UserSwitcherController userSwitcher = mock(UserSwitcherController.class);
+ KeyguardMonitor keyguardMonitor = getLeakChecker(KeyguardMonitor.class);
+ when(userSwitcher.getKeyguardMonitor()).thenReturn(keyguardMonitor);
+ when(userSwitcher.getUsers()).thenReturn(new ArrayList<>());
+ QSTileHost host = new QSTileHost(getTrackedContext(),
+ mock(PhoneStatusBar.class),
+ getLeakChecker(BluetoothController.class),
+ getLeakChecker(LocationController.class),
+ getLeakChecker(RotationLockController.class),
+ getLeakChecker(NetworkController.class),
+ getLeakChecker(ZenModeController.class),
+ getLeakChecker(HotspotController.class),
+ getLeakChecker(CastController.class),
+ getLeakChecker(FlashlightController.class),
+ userSwitcher,
+ getLeakChecker(UserInfoController.class),
+ keyguardMonitor,
+ getLeakChecker(SecurityController.class),
+ getLeakChecker(BatteryController.class),
+ mock(StatusBarIconController.class),
+ getLeakChecker(NextAlarmController.class));
+ qs.setHost(host);
+ Handler h = new Handler(host.getLooper());
+
+ qs.setListening(true);
+ waitForIdleSync(h);
+
+ qs.setListening(false);
+ waitForIdleSync(h);
+
+ host.destroy();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
index 8eecfcf..5401c30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
@@ -18,6 +18,7 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
+
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
@@ -26,11 +27,12 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+
import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,13 +40,13 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class TileLayoutTest {
- private Context mContext = InstrumentationRegistry.getTargetContext();
- private final TileLayout mTileLayout = new TileLayout(mContext);
+public class TileLayoutTest extends SysuiTestCase {
+ private TileLayout mTileLayout;
private int mLayoutSizeForOneTile;
@Before
public void setUp() throws Exception {
+ mTileLayout = new TileLayout(mContext);
// Layout needs to leave space for the tile margins. Three times the margin size is
// sufficient for any number of columns.
mLayoutSizeForOneTile =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
index d7ff04f..782a489 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
@@ -16,7 +16,7 @@
package com.android.systemui.qs.external;
import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.fail;
+
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
@@ -25,12 +25,9 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.Service;
-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.PackageInfo;
import android.content.pm.ServiceInfo;
@@ -38,19 +35,16 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Process;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.service.quicksettings.IQSService;
import android.service.quicksettings.IQSTileService;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
-import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
-import android.util.ArraySet;
-import android.util.Log;
+
+import com.android.systemui.SysuiTestCase;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -61,7 +55,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class TileLifecycleManagerTest {
+public class TileLifecycleManagerTest extends SysuiTestCase {
private static final int TEST_FAIL_TIMEOUT = 5000;
private final Context mMockContext = Mockito.mock(Context.class);
@@ -78,8 +72,7 @@
@Before
public void setUp() throws Exception {
setPackageEnabled(true);
- mTileServiceComponentName = new ComponentName(
- InstrumentationRegistry.getTargetContext(), "FakeTileService.class");
+ mTileServiceComponentName = new ComponentName(mContext, "FakeTileService.class");
// Stub.asInterface will just return itself.
when(mMockTileService.queryLocalInterface(anyString())).thenReturn(mMockTileService);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 6c9cfe0..136e7c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -35,7 +35,7 @@
import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config;
import com.android.systemui.statusbar.policy.NetworkControllerImpl.SubscriptionDefaults;
import com.android.systemui.SysuiTestCase;
-import org.junit.After;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.TestWatcher;
@@ -118,7 +118,7 @@
// Trigger blank callbacks to always get the current state (some tests don't trigger
// changes from default state).
- mNetworkController.addSignalCallback(mock(SignalCallback.class));
+ mNetworkController.addCallback(mock(SignalCallback.class));
mNetworkController.addEmergencyListener(null);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeContentResolver.java b/packages/SystemUI/tests/src/com/android/systemui/utils/FakeContentResolver.java
new file mode 100644
index 0000000..34f2e01
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/FakeContentResolver.java
@@ -0,0 +1,131 @@
+/*
+ * 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.utils;
+
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.IContentProvider;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.util.ArraySet;
+
+import com.google.android.collect.Maps;
+
+import java.util.Map;
+
+/**
+ * Alternative to a MockContentResolver that falls back to real providers.
+ */
+public class FakeContentResolver extends ContentResolver {
+
+ private final Map<String, ContentProvider> mProviders = Maps.newHashMap();
+ private final ContentResolver mParent;
+ private final ArraySet<ContentProvider> mInUse = new ArraySet<>();
+ private boolean mFallbackToExisting;
+
+ public FakeContentResolver(Context context) {
+ super(context);
+ mParent = context.getContentResolver();
+ mFallbackToExisting = true;
+ }
+
+ /**
+ * Sets whether existing providers should be returned when a mock does not exist.
+ * The default is true.
+ */
+ public void setFallbackToExisting(boolean fallbackToExisting) {
+ mFallbackToExisting = fallbackToExisting;
+ }
+
+ /**
+ * Adds access to a provider based on its authority
+ *
+ * @param name The authority name associated with the provider.
+ * @param provider An instance of {@link android.content.ContentProvider} or one of its
+ * subclasses, or null.
+ */
+ public void addProvider(String name, ContentProvider provider) {
+ mProviders.put(name, provider);
+ }
+
+ @Override
+ protected IContentProvider acquireProvider(Context context, String name) {
+ final ContentProvider provider = mProviders.get(name);
+ if (provider != null) {
+ return provider.getIContentProvider();
+ } else {
+ return mFallbackToExisting ? mParent.acquireProvider(name) : null;
+ }
+ }
+
+ @Override
+ protected IContentProvider acquireExistingProvider(Context context, String name) {
+ final ContentProvider provider = mProviders.get(name);
+ if (provider != null) {
+ return provider.getIContentProvider();
+ } else {
+ return mFallbackToExisting ? mParent.acquireExistingProvider(
+ new Uri.Builder().authority(name).build()) : null;
+ }
+ }
+
+ @Override
+ public boolean releaseProvider(IContentProvider provider) {
+ if (!mFallbackToExisting) return true;
+ if (mInUse.contains(provider)) {
+ mInUse.remove(provider);
+ return true;
+ }
+ return mParent.releaseProvider(provider);
+ }
+
+ @Override
+ protected IContentProvider acquireUnstableProvider(Context c, String name) {
+ final ContentProvider provider = mProviders.get(name);
+ if (provider != null) {
+ return provider.getIContentProvider();
+ } else {
+ return mFallbackToExisting ? mParent.acquireUnstableProvider(name) : null;
+ }
+ }
+
+ @Override
+ public boolean releaseUnstableProvider(IContentProvider icp) {
+ if (!mFallbackToExisting) return true;
+ if (mInUse.contains(icp)) {
+ mInUse.remove(icp);
+ return true;
+ }
+ return mParent.releaseUnstableProvider(icp);
+ }
+
+ @Override
+ public void unstableProviderDied(IContentProvider icp) {
+ if (!mFallbackToExisting) return;
+ if (mInUse.contains(icp)) {
+ return;
+ }
+ mParent.unstableProviderDied(icp);
+ }
+
+ @Override
+ public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
+ if (!mFallbackToExisting) return;
+ if (!mProviders.containsKey(uri.getAuthority())) {
+ super.notifyChange(uri, observer, syncToNetwork);
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProvider.java b/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProvider.java
new file mode 100644
index 0000000..f40fe4c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProvider.java
@@ -0,0 +1,298 @@
+/*
+ * 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.utils;
+
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
+import android.test.mock.MockContentProvider;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.utils.FakeSettingsProvider.SettingOverrider.Builder;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Allows calls to android.provider.Settings to be tested easier. A SettingOverride
+ * can be acquired and a set of specific settings can be set to a value (and not changed
+ * in the system when set), so that they can be tested without breaking the test device.
+ * <p>
+ * To use, in the before method acquire the override add all settings that will affect if
+ * your test passes or not.
+ *
+ * <pre class="prettyprint">
+ * {@literal
+ * mSettingOverride = mTestableContext.getSettingsProvider().acquireOverridesBuilder()
+ * .addSetting("secure", Secure.USER_SETUP_COMPLETE, "0")
+ * .build();
+ * }
+ * </pre>
+ *
+ * Then in the after free up the settings.
+ *
+ * <pre class="prettyprint">
+ * {@literal
+ * mSettingOverride.release();
+ * }
+ * </pre>
+ */
+public class FakeSettingsProvider extends MockContentProvider {
+
+ private static final String TAG = "FakeSettingsProvider";
+ private static final boolean DEBUG = false;
+
+ // Number of times to try to acquire a setting if in use.
+ private static final int MAX_TRIES = 10;
+ // Time to wait for each setting. WAIT_TIMEOUT * MAX_TRIES will be the maximum wait time
+ // for a setting.
+ private static final long WAIT_TIMEOUT = 1000;
+
+ private final Map<String, SettingOverrider> mOverrideMap = new ArrayMap<>();
+ private final Map<SysuiTestCase, List<SettingOverrider>> mOwners = new ArrayMap<>();
+
+ private static FakeSettingsProvider sInstance;
+ private final ContentProviderClient mSettings;
+ private final ContentResolver mResolver;
+
+ private FakeSettingsProvider(ContentProviderClient settings, ContentResolver resolver) {
+ mSettings = settings;
+ mResolver = resolver;
+ }
+
+ public Builder acquireOverridesBuilder(SysuiTestCase test) {
+ return new Builder(this, test);
+ }
+
+ public void clearOverrides(SysuiTestCase test) {
+ List<SettingOverrider> overrides = mOwners.remove(test);
+ if (overrides != null) {
+ overrides.forEach(override -> override.ensureReleased());
+ }
+ }
+
+ public Bundle call(String method, String arg, Bundle extras) {
+ // Methods are "GET_system", "GET_global", "PUT_secure", etc.
+ final String[] commands = method.split("_", 2);
+ final String op = commands[0];
+ final String table = commands[1];
+
+ synchronized (mOverrideMap) {
+ SettingOverrider overrider = mOverrideMap.get(key(table, arg));
+ if (overrider == null) {
+ // Fall through to real settings.
+ try {
+ if (DEBUG) Log.d(TAG, "Falling through to real settings " + method);
+ // TODO: Add our own version of caching to handle this.
+ Bundle call = mSettings.call(method, arg, extras);
+ call.remove(Settings.CALL_METHOD_TRACK_GENERATION_KEY);
+ return call;
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ String value;
+ Bundle out = new Bundle();
+ switch (op) {
+ case "GET":
+ value = overrider.get(table, arg);
+ if (value != null) {
+ out.putString(Settings.NameValueTable.VALUE, value);
+ }
+ break;
+ case "PUT":
+ value = extras.getString(Settings.NameValueTable.VALUE, null);
+ if (value != null) {
+ overrider.put(table, arg, value);
+ } else {
+ overrider.remove(table, arg);
+ }
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown command " + method);
+ }
+ return out;
+ }
+ }
+
+ private void acquireSettings(SettingOverrider overridder, Set<String> keys,
+ SysuiTestCase owner) throws AcquireTimeoutException {
+ synchronized (mOwners) {
+ List<SettingOverrider> list = mOwners.get(owner);
+ if (list == null) {
+ list = new ArrayList<>();
+ mOwners.put(owner, list);
+ }
+ list.add(overridder);
+ }
+ synchronized (mOverrideMap) {
+ for (int i = 0; i < MAX_TRIES; i++) {
+ if (checkKeys(keys, false)) break;
+ try {
+ if (DEBUG) Log.d(TAG, "Waiting for contention to finish");
+ mOverrideMap.wait(WAIT_TIMEOUT);
+ } catch (InterruptedException e) {
+ }
+ }
+ checkKeys(keys, true);
+ for (String key : keys) {
+ if (DEBUG) Log.d(TAG, "Acquiring " + key);
+ mOverrideMap.put(key, overridder);
+ }
+ }
+ }
+
+ private void releaseSettings(Set<String> keys) {
+ synchronized (mOverrideMap) {
+ for (String key : keys) {
+ if (DEBUG) Log.d(TAG, "Releasing " + key);
+ mOverrideMap.remove(key);
+ }
+ if (DEBUG) Log.d(TAG, "Notifying");
+ mOverrideMap.notify();
+ }
+ }
+
+ @VisibleForTesting
+ public Object getLock() {
+ return mOverrideMap;
+ }
+
+ private boolean checkKeys(Set<String> keys, boolean shouldThrow)
+ throws AcquireTimeoutException {
+ for (String key : keys) {
+ if (mOverrideMap.containsKey(key)) {
+ if (shouldThrow) {
+ throw new AcquireTimeoutException("Could not acquire " + key);
+ }
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static class SettingOverrider {
+ private final Set<String> mValidKeys;
+ private final Map<String, String> mValueMap = new ArrayMap<>();
+ private final FakeSettingsProvider mProvider;
+ private boolean mReleased;
+
+ private SettingOverrider(Set<String> keys, FakeSettingsProvider provider) {
+ mValidKeys = new ArraySet<>(keys);
+ mProvider = provider;
+ }
+
+ private void ensureReleased() {
+ if (!mReleased) {
+ release();
+ }
+ }
+
+ public void release() {
+ mProvider.releaseSettings(mValidKeys);
+ mReleased = true;
+ }
+
+ private void putDirect(String key, String value) {
+ mValueMap.put(key, value);
+ }
+
+ public void put(String table, String key, String value) {
+ if (!mValidKeys.contains(key(table, key))) {
+ throw new IllegalArgumentException("Key " + table + " " + key
+ + " not acquired for this overrider");
+ }
+ mValueMap.put(key(table, key), value);
+ }
+
+ public void remove(String table, String key) {
+ if (!mValidKeys.contains(key(table, key))) {
+ throw new IllegalArgumentException("Key " + table + " " + key
+ + " not acquired for this overrider");
+ }
+ mValueMap.remove(key(table, key));
+ }
+
+ public String get(String table, String key) {
+ if (!mValidKeys.contains(key(table, key))) {
+ throw new IllegalArgumentException("Key " + table + " " + key
+ + " not acquired for this overrider");
+ }
+ Log.d(TAG, "Get " + table + " " + key + " " + mValueMap.get(key(table, key)));
+ return mValueMap.get(key(table, key));
+ }
+
+ public static class Builder {
+ private final FakeSettingsProvider mProvider;
+ private final SysuiTestCase mOwner;
+ private Set<String> mKeys = new ArraySet<>();
+ private Map<String, String> mValues = new ArrayMap<>();
+
+ private Builder(FakeSettingsProvider provider, SysuiTestCase test) {
+ mProvider = provider;
+ mOwner = test;
+ }
+
+ public Builder addSetting(String table, String key) {
+ mKeys.add(key(table, key));
+ return this;
+ }
+
+ public Builder addSetting(String table, String key, String value) {
+ addSetting(table, key);
+ mValues.put(key(table, key), value);
+ return this;
+ }
+
+ public SettingOverrider build() throws AcquireTimeoutException {
+ SettingOverrider overrider = new SettingOverrider(mKeys, mProvider);
+ mProvider.acquireSettings(overrider, mKeys, mOwner);
+ mValues.forEach((key, value) -> overrider.putDirect(key, value));
+ return overrider;
+ }
+ }
+ }
+
+ public static class AcquireTimeoutException extends Exception {
+ public AcquireTimeoutException(String str) {
+ super(str);
+ }
+ }
+
+ private static String key(String table, String key) {
+ return table + "_" + key;
+ }
+
+ /**
+ * Since the settings provider is cached inside android.provider.Settings, this must
+ * be gotten statically to ensure there is only one instance referenced.
+ * @param settings
+ */
+ public static FakeSettingsProvider getFakeSettingsProvider(ContentProviderClient settings,
+ ContentResolver resolver) {
+ if (sInstance == null) {
+ sInstance = new FakeSettingsProvider(settings, resolver);
+ }
+ return sInstance;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProviderTest.java
new file mode 100644
index 0000000..63bb5e7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProviderTest.java
@@ -0,0 +1,175 @@
+/*
+ * 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.utils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.ContentResolver;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.provider.Settings.Secure;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.utils.FakeSettingsProvider.AcquireTimeoutException;
+import com.android.systemui.utils.FakeSettingsProvider.SettingOverrider;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class FakeSettingsProviderTest extends SysuiTestCase {
+
+ public static final String NONEXISTENT_SETTING = "nonexistent_setting";
+ private static final String TAG = "FakeSettingsProviderTest";
+ private SettingOverrider mOverrider;
+ private ContentResolver mContentResolver;
+
+ @Before
+ public void setup() throws AcquireTimeoutException {
+ mOverrider = mContext.getSettingsProvider().acquireOverridesBuilder(this)
+ .addSetting("secure", NONEXISTENT_SETTING)
+ .addSetting("global", NONEXISTENT_SETTING, "initial value")
+ .addSetting("global", Global.DEVICE_PROVISIONED)
+ .build();
+ mContentResolver = mContext.getContentResolver();
+ }
+
+ @After
+ public void teardown() {
+ if (mOverrider != null) {
+ mOverrider.release();
+ }
+ }
+
+ @Test
+ public void testInitialValueSecure() {
+ String value = Secure.getString(mContentResolver, NONEXISTENT_SETTING);
+ assertNull(value);
+ }
+
+ @Test
+ public void testInitialValueGlobal() {
+ String value = Global.getString(mContentResolver, NONEXISTENT_SETTING);
+ assertEquals("initial value", value);
+ }
+
+ @Test
+ public void testSeparateTables() {
+ Secure.putString(mContentResolver, NONEXISTENT_SETTING, "something");
+ Global.putString(mContentResolver, NONEXISTENT_SETTING, "else");
+ assertEquals("something", Secure.getString(mContentResolver, NONEXISTENT_SETTING));
+ assertEquals("something", mOverrider.get("secure", NONEXISTENT_SETTING));
+ assertEquals("else", Global.getString(mContentResolver, NONEXISTENT_SETTING));
+ assertEquals("else", mOverrider.get("global", NONEXISTENT_SETTING));
+ }
+
+ @Test
+ public void testPassThrough() {
+ // Grab the value of a setting that is not overridden.
+ assertTrue(Secure.getInt(mContentResolver, Secure.USER_SETUP_COMPLETE, 0) != 0);
+ }
+
+ @Test
+ public void testOverrideExisting() {
+ // Grab the value of a setting that is overridden and will be different than the actual
+ // value.
+ assertNull(Global.getString(mContentResolver, Global.DEVICE_PROVISIONED));
+ }
+
+ @Test
+ public void testRelease() {
+ // Verify different value.
+ assertNull(Global.getString(mContentResolver, Global.DEVICE_PROVISIONED));
+ mOverrider.release();
+ mOverrider = null;
+ // Verify actual value after release.
+ assertEquals("1", Global.getString(mContentResolver, Global.DEVICE_PROVISIONED));
+ }
+
+ @Test
+ public void testAutoRelease() throws Exception {
+ super.cleanup();
+ mContext.getSettingsProvider().acquireOverridesBuilder(this)
+ .addSetting("global", Global.DEVICE_PROVISIONED)
+ .build();
+ }
+
+ @Test
+ public void testContention() throws AcquireTimeoutException, InterruptedException {
+ SettingOverrider[] overriders = new SettingOverrider[2];
+ Object lock = new Object();
+ String secure = "secure";
+ String key = "something shared";
+ String[] result = new String[1];
+ overriders[0] = mContext.getSettingsProvider().acquireOverridesBuilder(this)
+ .addSetting(secure, key, "Some craziness")
+ .build();
+ synchronized (lock) {
+ HandlerThread t = runOnHandler(() -> {
+ try {
+ // Grab the lock that will be used for the settings ownership to ensure
+ // we have some contention going on.
+ synchronized (mContext.getSettingsProvider().getLock()) {
+ synchronized (lock) {
+ // Let the other thread know to release the settings, but it won't
+ // be able to until this thread waits in the build() method.
+ lock.notify();
+ }
+ overriders[1] = mContext.getSettingsProvider()
+ .acquireOverridesBuilder(FakeSettingsProviderTest.this)
+ .addSetting(secure, key, "default value")
+ .build();
+ // Ensure that the default is the one we set, and not left over from
+ // the other setting override.
+ result[0] = Settings.Secure.getString(mContentResolver, key);
+ synchronized (lock) {
+ // Let the main thread know we are done.
+ lock.notify();
+ }
+ }
+ } catch (AcquireTimeoutException e) {
+ Log.e(TAG, "Couldn't acquire setting", e);
+ }
+ });
+ // Wait for the thread to hold the acquire lock, then release the settings.
+ lock.wait();
+ overriders[0].release();
+ // Wait for the thread to be done getting the value.
+ lock.wait();
+ // Quit and cleanup.
+ t.quitSafely();
+ assertNotNull(overriders[1]);
+ overriders[1].release();
+ }
+ // Verify the value was the expected one from the thread's SettingOverride.
+ assertEquals("default value", result[0]);
+ }
+
+ private HandlerThread runOnHandler(Runnable r) {
+ HandlerThread t = new HandlerThread("Test Thread");
+ t.start();
+ new Handler(t.getLooper()).post(r);
+ return t;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java b/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java
new file mode 100644
index 0000000..5179823
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java
@@ -0,0 +1,52 @@
+/*
+ * 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.utils;
+
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.provider.Settings;
+
+public class TestableContext extends ContextWrapper {
+
+ private final FakeContentResolver mFakeContentResolver;
+ private final FakeSettingsProvider mSettingsProvider;
+
+ public TestableContext(Context base) {
+ super(base);
+ mFakeContentResolver = new FakeContentResolver(base);
+ ContentProviderClient settings = base.getContentResolver()
+ .acquireContentProviderClient(Settings.AUTHORITY);
+ mSettingsProvider = FakeSettingsProvider.getFakeSettingsProvider(settings,
+ mFakeContentResolver);
+ mFakeContentResolver.addProvider(Settings.AUTHORITY, mSettingsProvider);
+ }
+
+ public FakeSettingsProvider getSettingsProvider() {
+ return mSettingsProvider;
+ }
+
+ @Override
+ public FakeContentResolver getContentResolver() {
+ return mFakeContentResolver;
+ }
+
+ @Override
+ public Context getApplicationContext() {
+ // Return this so its always a TestableContext.
+ return this;
+ }
+}
diff --git a/services/Android.mk b/services/Android.mk
index 3385bed..2911983 100644
--- a/services/Android.mk
+++ b/services/Android.mk
@@ -22,6 +22,7 @@
core \
accessibility \
appwidget \
+ autofill \
backup \
devicepolicy \
midi \
diff --git a/services/autofill/Android.mk b/services/autofill/Android.mk
new file mode 100644
index 0000000..a1f19fd
--- /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 0000000..3b41877
--- /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 0000000..c780062
--- /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 0000000..4e08ed6
--- /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 0000000..44637c3
--- /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 82897fb..2558045 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1940,13 +1940,9 @@
}
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 @@
@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 6456048..9b3fac3 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -1364,7 +1364,8 @@
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 @@
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/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index 553cb07..830a6ed 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -40,7 +40,7 @@
import android.view.KeyEvent;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.statusbar.StatusBarManagerInternal;
/**
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 2698f95..d1f07a5 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.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.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 @@
+ 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 @@
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 @@
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 @@
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/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index a2207b2..dd342c5 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -49,6 +49,8 @@
import android.os.Parcel;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.os.storage.IMountService;
import android.os.storage.StorageManager;
import android.os.ServiceManager;
@@ -79,6 +81,7 @@
import libcore.util.HexEncoding;
import java.io.ByteArrayOutputStream;
+import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@@ -1585,6 +1588,31 @@
return mStrongAuthTracker.getStrongAuthForUser(userId);
}
+ private boolean isCallerShell() {
+ final int callingUid = Binder.getCallingUid();
+ return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
+ }
+
+ private void enforceShell() {
+ if (!isCallerShell()) {
+ throw new SecurityException("Caller must be shell");
+ }
+ }
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver)
+ throws RemoteException {
+ enforceShell();
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ (new LockSettingsShellCommand(mContext, new LockPatternUtils(mContext))).exec(
+ this, in, out, err, args, callback, resultReceiver);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
private static final String[] VALID_SETTINGS = new String[] {
LockPatternUtils.LOCKOUT_PERMANENT_KEY,
LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
diff --git a/services/core/java/com/android/server/LockSettingsShellCommand.java b/services/core/java/com/android/server/LockSettingsShellCommand.java
new file mode 100644
index 0000000..0efdd51
--- /dev/null
+++ b/services/core/java/com/android/server/LockSettingsShellCommand.java
@@ -0,0 +1,143 @@
+/*
+ * 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;
+
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+import static com.android.internal.widget.LockPatternUtils.stringToPattern;
+
+import android.app.ActivityManagerNative;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ShellCommand;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
+
+class LockSettingsShellCommand extends ShellCommand {
+
+ private static final String COMMAND_SET_PATTERN = "set-pattern";
+ private static final String COMMAND_SET_PIN = "set-pin";
+ private static final String COMMAND_SET_PASSWORD = "set-password";
+ private static final String COMMAND_CLEAR = "clear";
+
+ private int mCurrentUserId;
+ private final LockPatternUtils mLockPatternUtils;
+ private final Context mContext;
+ private String mOld = "";
+ private String mNew = "";
+
+ LockSettingsShellCommand(Context context, LockPatternUtils lockPatternUtils) {
+ mContext = context;
+ mLockPatternUtils = lockPatternUtils;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ try {
+ mCurrentUserId = ActivityManagerNative.getDefault().getCurrentUser().id;
+
+ parseArgs();
+ if (!checkCredential()) {
+ return -1;
+ }
+ switch (cmd) {
+ case COMMAND_SET_PATTERN:
+ runSetPattern();
+ break;
+ case COMMAND_SET_PASSWORD:
+ runSetPassword();
+ break;
+ case COMMAND_SET_PIN:
+ runSetPin();
+ break;
+ case COMMAND_CLEAR:
+ runClear();
+ break;
+ default:
+ getErrPrintWriter().println("Unknown command: " + cmd);
+ break;
+ }
+ return 0;
+ } catch (Exception e) {
+ getErrPrintWriter().println("Error while executing command: " + e);
+ return -1;
+ }
+ }
+
+ @Override
+ public void onHelp() {
+ }
+
+ private void parseArgs() {
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if ("--old".equals(opt)) {
+ mOld = getNextArgRequired();
+ } else {
+ getErrPrintWriter().println("Unknown option: " + opt);
+ throw new IllegalArgumentException();
+ }
+ }
+ mNew = getNextArg();
+ }
+
+ private void runSetPattern() throws RemoteException {
+ mLockPatternUtils.saveLockPattern(stringToPattern(mNew), mOld, mCurrentUserId);
+ getOutPrintWriter().println("Pattern set to '" + mNew + "'");
+ }
+
+ private void runSetPassword() throws RemoteException {
+ mLockPatternUtils.saveLockPassword(mNew, mOld, PASSWORD_QUALITY_ALPHABETIC, mCurrentUserId);
+ getOutPrintWriter().println("Password set to '" + mNew + "'");
+ }
+
+ private void runSetPin() throws RemoteException {
+ mLockPatternUtils.saveLockPassword(mNew, mOld, PASSWORD_QUALITY_NUMERIC, mCurrentUserId);
+ getOutPrintWriter().println("Pin set to '" + mNew + "'");
+ }
+
+ private void runClear() throws RemoteException {
+ mLockPatternUtils.clearLock(mCurrentUserId);
+ getOutPrintWriter().println("Lock credential cleared");
+ }
+
+ private boolean checkCredential() throws RemoteException, RequestThrottledException {
+ final boolean havePassword = mLockPatternUtils.isLockPasswordEnabled(mCurrentUserId);
+ final boolean havePattern = mLockPatternUtils.isLockPatternEnabled(mCurrentUserId);
+ if (havePassword || havePattern) {
+ boolean result;
+ if (havePassword) {
+ result = mLockPatternUtils.checkPassword(mOld, mCurrentUserId);
+ } else {
+ result = mLockPatternUtils.checkPattern(stringToPattern(mOld),
+ mCurrentUserId);
+ }
+ if (result) {
+ return true;
+ } else {
+ getOutPrintWriter().println("Old password '" + mOld + "' didn't match");
+ return false;
+ }
+ } else {
+ return true;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
index 33f9234..0414b47 100644
--- a/services/core/java/com/android/server/MmsServiceBroker.java
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -92,7 +92,7 @@
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 83d6344..f5cda0a 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -42,8 +42,10 @@
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 @@
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 2825cf9..6268697 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.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 @@
}
@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 dc4a52d..136e02c 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 @@
// 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 @@
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 42d16fb..d60f115 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.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.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 @@
String mOrigDebugApp = null;
boolean mOrigWaitForDebugger = false;
boolean mAlwaysFinishActivities = false;
- boolean mLenientBackgroundCheck = false;
boolean mForceResizableActivities;
boolean mSupportsMultiWindow;
boolean mSupportsFreeformWindowManagement;
@@ -1575,6 +1573,10 @@
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 @@
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 @@
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 @@
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 @@
@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 @@
}
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-
if (r == null) {
throw new IllegalStateException("enterPictureInPictureMode: "
+ "Can't find activity for token=" + token);
@@ -7489,21 +7500,55 @@
+ "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));
+ }
- mStackSupervisor.moveActivityToPinnedStackLocked(
- r, "enterPictureInPictureMode", bounds);
+ 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));
+ }
+
+ 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 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 (uidRec == null || alwaysRestrict || uidRec.idle) {
+ boolean ephemeral;
+ if (uidRec == null) {
+ ephemeral = getPackageManagerInternalLocked().isPackageEphemeral(
+ UserHandle.getUserId(uid), packageName);
+ } else {
+ ephemeral = uidRec.ephemeral;
+ }
+
+ 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 (mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND, uid,
packageName) != AppOpsManager.MODE_ALLOWED) {
return ActivityManager.APP_START_MODE_DELAYED;
}
}
-
- } else if (uidRec == null || uidRec.idle) {
- 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 (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 @@
}
@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 @@
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 @@
mDebugApp = mOrigDebugApp = debugApp;
mWaitForDebugger = mOrigWaitForDebugger = waitForDebugger;
mAlwaysFinishActivities = alwaysFinishActivities;
- mLenientBackgroundCheck = lenientBackgroundCheck;
mSupportsLeanbackOnly = supportsLeanbackOnly;
mForceResizableActivities = forceResizable;
if (supportsMultiWindow || forceResizable) {
@@ -13035,6 +13063,10 @@
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 @@
}
}
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 @@
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 @@
} 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 @@
}
}
+ @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 @@
// 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 abeea74..52ad72d 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 @@
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 @@
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 @@
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 @@
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 @@
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/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index be8f21d..facfeb6 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -16,7 +16,7 @@
import android.util.Slog;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 0d79980..1bdef43 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 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 @@
}
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 2a0c6d0..691d6b9 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 @@
// 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/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index c19a571..7a122e6 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -18,7 +18,7 @@
import com.android.internal.app.ProcessMap;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.os.ProcessCpuTracker;
import com.android.server.Watchdog;
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index c6befd7..9e29725 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -17,7 +17,7 @@
package com.android.server.am;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto;
import android.content.ActivityNotFoundException;
import android.content.Context;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 4e69162..8104a43 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.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 @@
}
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 @@
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 722974b..f618fc7 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 @@
# 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 98acc9c..9d8c383 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 @@
}
}
+ /**
+ * @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 @@
&& 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 d24c3a5..d1a15bd 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 @@
int curProcState;
int setProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
long lastBackgroundTime;
+ boolean ephemeral;
boolean idle;
int numProcs;
@@ -43,6 +44,7 @@
int uid;
int change;
int processState;
+ boolean ephemeral;
}
ChangeItem pendingChange;
@@ -64,6 +66,9 @@
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 48238b6..42c31b1 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 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/IpConnectivityEventBuilder.java b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
index f1d01e0..372b2d8 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
@@ -29,14 +29,14 @@
import android.net.metrics.RaEvent;
import android.net.metrics.ValidationProbeEvent;
import android.os.Parcelable;
-import com.android.server.connectivity.metrics.IpConnectivityLogClass;
+import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
-import static com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityEvent;
-import static com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityLog;
-import static com.android.server.connectivity.metrics.IpConnectivityLogClass.NetworkId;
+import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
+import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog;
+import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.NetworkId;
/** {@hide} */
final public class IpConnectivityEventBuilder {
@@ -136,96 +136,107 @@
}
private static void setDhcpErrorEvent(IpConnectivityEvent out, DhcpErrorEvent in) {
- out.dhcpEvent = new IpConnectivityLogClass.DHCPEvent();
- out.dhcpEvent.ifName = in.ifName;
- out.dhcpEvent.errorCode = in.errorCode;
+ IpConnectivityLogClass.DHCPEvent dhcpEvent = new IpConnectivityLogClass.DHCPEvent();
+ dhcpEvent.ifName = in.ifName;
+ dhcpEvent.setErrorCode(in.errorCode);
+ out.setDhcpEvent(dhcpEvent);
}
private static void setDhcpClientEvent(IpConnectivityEvent out, DhcpClientEvent in) {
- out.dhcpEvent = new IpConnectivityLogClass.DHCPEvent();
- out.dhcpEvent.ifName = in.ifName;
- out.dhcpEvent.stateTransition = in.msg;
- out.dhcpEvent.durationMs = in.durationMs;
+ IpConnectivityLogClass.DHCPEvent dhcpEvent = new IpConnectivityLogClass.DHCPEvent();
+ dhcpEvent.ifName = in.ifName;
+ dhcpEvent.setStateTransition(in.msg);
+ dhcpEvent.durationMs = in.durationMs;
+ out.setDhcpEvent(dhcpEvent);
}
private static void setDnsEvent(IpConnectivityEvent out, DnsEvent in) {
- out.dnsLookupBatch = new IpConnectivityLogClass.DNSLookupBatch();
- out.dnsLookupBatch.networkId = netIdOf(in.netId);
- out.dnsLookupBatch.eventTypes = bytesToInts(in.eventTypes);
- out.dnsLookupBatch.returnCodes = bytesToInts(in.returnCodes);
- out.dnsLookupBatch.latenciesMs = in.latenciesMs;
+ IpConnectivityLogClass.DNSLookupBatch dnsLookupBatch = new IpConnectivityLogClass.DNSLookupBatch();
+ dnsLookupBatch.networkId = netIdOf(in.netId);
+ dnsLookupBatch.eventTypes = bytesToInts(in.eventTypes);
+ dnsLookupBatch.returnCodes = bytesToInts(in.returnCodes);
+ dnsLookupBatch.latenciesMs = in.latenciesMs;
+ out.setDnsLookupBatch(dnsLookupBatch);
}
private static void setIpManagerEvent(IpConnectivityEvent out, IpManagerEvent in) {
- out.ipProvisioningEvent = new IpConnectivityLogClass.IpProvisioningEvent();
- out.ipProvisioningEvent.ifName = in.ifName;
- out.ipProvisioningEvent.eventType = in.eventType;
- out.ipProvisioningEvent.latencyMs = (int) in.durationMs;
+ IpConnectivityLogClass.IpProvisioningEvent ipProvisioningEvent = new IpConnectivityLogClass.IpProvisioningEvent();
+ ipProvisioningEvent.ifName = in.ifName;
+ ipProvisioningEvent.eventType = in.eventType;
+ ipProvisioningEvent.latencyMs = (int) in.durationMs;
+ out.setIpProvisioningEvent(ipProvisioningEvent);
}
private static void setIpReachabilityEvent(IpConnectivityEvent out, IpReachabilityEvent in) {
- out.ipReachabilityEvent = new IpConnectivityLogClass.IpReachabilityEvent();
- out.ipReachabilityEvent.ifName = in.ifName;
- out.ipReachabilityEvent.eventType = in.eventType;
+ IpConnectivityLogClass.IpReachabilityEvent ipReachabilityEvent = new IpConnectivityLogClass.IpReachabilityEvent();
+ ipReachabilityEvent.ifName = in.ifName;
+ ipReachabilityEvent.eventType = in.eventType;
+ out.setIpReachabilityEvent(ipReachabilityEvent);
}
private static void setDefaultNetworkEvent(IpConnectivityEvent out, DefaultNetworkEvent in) {
- out.defaultNetworkEvent = new IpConnectivityLogClass.DefaultNetworkEvent();
- out.defaultNetworkEvent.networkId = netIdOf(in.netId);
- out.defaultNetworkEvent.previousNetworkId = netIdOf(in.prevNetId);
- out.defaultNetworkEvent.transportTypes = in.transportTypes;
- out.defaultNetworkEvent.previousNetworkIpSupport = ipSupportOf(in);
+ IpConnectivityLogClass.DefaultNetworkEvent defaultNetworkEvent = new IpConnectivityLogClass.DefaultNetworkEvent();
+ defaultNetworkEvent.networkId = netIdOf(in.netId);
+ defaultNetworkEvent.previousNetworkId = netIdOf(in.prevNetId);
+ defaultNetworkEvent.transportTypes = in.transportTypes;
+ defaultNetworkEvent.previousNetworkIpSupport = ipSupportOf(in);
+ out.setDefaultNetworkEvent(defaultNetworkEvent);
}
private static void setNetworkEvent(IpConnectivityEvent out, NetworkEvent in) {
- out.networkEvent = new IpConnectivityLogClass.NetworkEvent();
- out.networkEvent.networkId = netIdOf(in.netId);
- out.networkEvent.eventType = in.eventType;
- out.networkEvent.latencyMs = (int) in.durationMs;
+ IpConnectivityLogClass.NetworkEvent networkEvent = new IpConnectivityLogClass.NetworkEvent();
+ networkEvent.networkId = netIdOf(in.netId);
+ networkEvent.eventType = in.eventType;
+ networkEvent.latencyMs = (int) in.durationMs;
+ out.setNetworkEvent(networkEvent);
}
private static void setValidationProbeEvent(IpConnectivityEvent out, ValidationProbeEvent in) {
- out.validationProbeEvent = new IpConnectivityLogClass.ValidationProbeEvent();
- out.validationProbeEvent.networkId = netIdOf(in.netId);
- out.validationProbeEvent.latencyMs = (int) in.durationMs;
- out.validationProbeEvent.probeType = in.probeType;
- out.validationProbeEvent.probeResult = in.returnCode;
+ IpConnectivityLogClass.ValidationProbeEvent validationProbeEvent = new IpConnectivityLogClass.ValidationProbeEvent();
+ validationProbeEvent.networkId = netIdOf(in.netId);
+ validationProbeEvent.latencyMs = (int) in.durationMs;
+ validationProbeEvent.probeType = in.probeType;
+ validationProbeEvent.probeResult = in.returnCode;
+ out.setValidationProbeEvent(validationProbeEvent);
}
private static void setApfProgramEvent(IpConnectivityEvent out, ApfProgramEvent in) {
- out.apfProgramEvent = new IpConnectivityLogClass.ApfProgramEvent();
- out.apfProgramEvent.lifetime = in.lifetime;
- out.apfProgramEvent.filteredRas = in.filteredRas;
- out.apfProgramEvent.currentRas = in.currentRas;
- out.apfProgramEvent.programLength = in.programLength;
+ IpConnectivityLogClass.ApfProgramEvent apfProgramEvent = new IpConnectivityLogClass.ApfProgramEvent();
+ apfProgramEvent.lifetime = in.lifetime;
+ apfProgramEvent.filteredRas = in.filteredRas;
+ apfProgramEvent.currentRas = in.currentRas;
+ apfProgramEvent.programLength = in.programLength;
if (isBitSet(in.flags, ApfProgramEvent.FLAG_MULTICAST_FILTER_ON)) {
- out.apfProgramEvent.dropMulticast = true;
+ apfProgramEvent.dropMulticast = true;
}
if (isBitSet(in.flags, ApfProgramEvent.FLAG_HAS_IPV4_ADDRESS)) {
- out.apfProgramEvent.hasIpv4Addr = true;
+ apfProgramEvent.hasIpv4Addr = true;
}
+ out.setApfProgramEvent(apfProgramEvent);
}
private static void setApfStats(IpConnectivityEvent out, ApfStats in) {
- out.apfStatistics = new IpConnectivityLogClass.ApfStatistics();
- out.apfStatistics.durationMs = in.durationMs;
- out.apfStatistics.receivedRas = in.receivedRas;
- out.apfStatistics.matchingRas = in.matchingRas;
- out.apfStatistics.droppedRas = in.droppedRas;
- out.apfStatistics.zeroLifetimeRas = in.zeroLifetimeRas;
- out.apfStatistics.parseErrors = in.parseErrors;
- out.apfStatistics.programUpdates = in.programUpdates;
- out.apfStatistics.maxProgramSize = in.maxProgramSize;
+ IpConnectivityLogClass.ApfStatistics apfStatistics = new IpConnectivityLogClass.ApfStatistics();
+ apfStatistics.durationMs = in.durationMs;
+ apfStatistics.receivedRas = in.receivedRas;
+ apfStatistics.matchingRas = in.matchingRas;
+ apfStatistics.droppedRas = in.droppedRas;
+ apfStatistics.zeroLifetimeRas = in.zeroLifetimeRas;
+ apfStatistics.parseErrors = in.parseErrors;
+ apfStatistics.programUpdates = in.programUpdates;
+ apfStatistics.maxProgramSize = in.maxProgramSize;
+ out.setApfStatistics(apfStatistics);
}
private static void setRaEvent(IpConnectivityEvent out, RaEvent in) {
- out.raEvent = new IpConnectivityLogClass.RaEvent();
- out.raEvent.routerLifetime = in.routerLifetime;
- out.raEvent.prefixValidLifetime = in.prefixValidLifetime;
- out.raEvent.prefixPreferredLifetime = in.prefixPreferredLifetime;
- out.raEvent.routeInfoLifetime = in.routeInfoLifetime;
- out.raEvent.rdnssLifetime = in.rdnssLifetime;
- out.raEvent.dnsslLifetime = in.dnsslLifetime;
+ IpConnectivityLogClass.RaEvent raEvent = new IpConnectivityLogClass.RaEvent();
+ raEvent.routerLifetime = in.routerLifetime;
+ raEvent.prefixValidLifetime = in.prefixValidLifetime;
+ raEvent.prefixPreferredLifetime = in.prefixPreferredLifetime;
+ raEvent.routeInfoLifetime = in.routeInfoLifetime;
+ raEvent.rdnssLifetime = in.rdnssLifetime;
+ raEvent.dnsslLifetime = in.dnsslLifetime;
+ out.setRaEvent(raEvent);
}
private static int[] bytesToInts(byte[] in) {
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
index 641c62f..42f439c 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
@@ -42,7 +42,7 @@
import java.util.ArrayList;
import java.util.function.ToIntFunction;
-import static com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityEvent;
+import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
/** {@hide} */
final public class IpConnectivityMetrics extends SystemService {
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 0beb227..6d96a10 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.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 @@
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 c2c1a8c..dec2f77 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 @@
/**
* @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 @@
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 6ca4e27..37221a9 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 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 971989b..9c762cc 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 @@
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 @@
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 @@
}
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 @@
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 5616fb9..47701b9 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 @@
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/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index 393199d..fbad8de 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -17,7 +17,7 @@
package com.android.server.dreams;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import android.content.ComponentName;
import android.content.Context;
diff --git a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
index 5297589..d65e257 100644
--- a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
+++ b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
@@ -17,7 +17,7 @@
package com.android.server.fingerprint;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import android.content.Context;
import android.hardware.fingerprint.Fingerprint;
diff --git a/services/core/java/com/android/server/fingerprint/EnrollClient.java b/services/core/java/com/android/server/fingerprint/EnrollClient.java
index 640a46f..c70ca7f 100644
--- a/services/core/java/com/android/server/fingerprint/EnrollClient.java
+++ b/services/core/java/com/android/server/fingerprint/EnrollClient.java
@@ -25,7 +25,7 @@
import android.util.Slog;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import java.util.Arrays;
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index df64447..b878099 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 @@
Slog.d(TAG, "Removing jobs for package " + pkgName
+ " in user " + userId);
}
- cancelJobsForUid(pkgUid, true);
+ cancelJobsForUid(pkgUid);
}
} catch (RemoteException|IllegalArgumentException e) {
/*
@@ -455,7 +455,7 @@
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 @@
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 @@
* 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 @@
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 5eb06ed..b44087c 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 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 @@
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 fd99f57..c1506b9 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 @@
}
}
- @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 @@
@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 f3ae2bc..c7f4d6b 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.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.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.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 @@
/** 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 @@
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 @@
}
}
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 @@
// 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 @@
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 @@
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 @@
// 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 @@
// bind to listener services.
mSettingsObserver.observe();
mListeners.onBootPhaseAppsCanStart();
- mRankerServices.onBootPhaseAppsCanStart();
+ mNotificationAssistants.onBootPhaseAppsCanStart();
mConditionProviders.onBootPhaseAppsCanStart();
}
}
@@ -1682,10 +1671,10 @@
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 @@
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 @@
}
@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 @@
}
@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 @@
}
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 @@
}
}
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 @@
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 @@
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 @@
}
}
- // 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 @@
} 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 @@
// 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 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 @@
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 @@
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 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- notifyRemoved(info, oldSbnLightClone, update);
+ notifyRemoved(info, oldSbnLightClone, update, REASON_USER_STOPPED);
}
});
continue;
@@ -4314,7 +4252,7 @@
/**
* 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 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- notifyRemoved(info, sbnLight, update);
+ notifyRemoved(info, sbnLight, update, reason);
}
});
}
@@ -4391,14 +4329,14 @@
}
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 a1256db..965257c 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 @@
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 @@
mUpdateTimeMs = mCreationTimeMs;
mContext = context;
stats = new NotificationUsageStats.SingleNotificationStats();
- mNotificationChannel = channel;
mImportance = defaultImportance();
}
@@ -148,8 +144,8 @@
|| (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 @@
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 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 9778919..ecc03d7 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.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 @@
.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 21b6964..79ef486 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 @@
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 @@
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (this) {
- mContainerService = IMediaContainerService.Stub.asInterface(service);
+ mContainerService = IMediaContainerService.Stub
+ .asInterface(Binder.allowBlocking(service));
notifyAll();
}
}
@@ -18100,8 +18101,10 @@
}
synchronized (mPackages) {
- if (uid == Process.SHELL_UID) {
+ if (uid == Process.SHELL_UID
+ && (pkgSetting.pkgFlags & ApplicationInfo.FLAG_TEST_ONLY) == 0) {
// Shell can only change whole packages between ENABLED and DISABLED_USER states
+ // unless it is a test package.
int oldState = pkgSetting.getEnabled(userId);
if (className == null
&&
@@ -21254,6 +21257,14 @@
}
@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 ad31a32..5a0bee1 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 @@
}
@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 @@
}
@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/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java
index a8bd4d2..3bea663 100644
--- a/services/core/java/com/android/server/policy/GlobalActions.java
+++ b/services/core/java/com/android/server/policy/GlobalActions.java
@@ -19,7 +19,7 @@
import com.android.internal.app.AlertController;
import com.android.internal.app.AlertController.AlertParams;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.policy.EmergencyAffordanceManager;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyProperties;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b7067d2..396c958 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.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 @@
/** {@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 28f36f7..ca641fb 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.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 @@
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 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 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 b457f8d..c4a0dd3 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 @@
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 f19f0aa..941cd44 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 @@
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 @@
@Override // Binder interface
public void onTrustedChanged(boolean trusted) {
mTrusted = trusted;
+ mCallback.onTrustedChanged();
}
@Override // Binder interface
@@ -114,6 +117,10 @@
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/storage/AppFuseBridge.java b/services/core/java/com/android/server/storage/AppFuseBridge.java
new file mode 100644
index 0000000..23be9a3
--- /dev/null
+++ b/services/core/java/com/android/server/storage/AppFuseBridge.java
@@ -0,0 +1,95 @@
+/*
+ * 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.storage;
+
+import android.annotation.CallSuper;
+import android.annotation.WorkerThread;
+import android.os.Handler;
+import android.os.ParcelFileDescriptor;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.util.Log;
+import com.android.internal.os.AppFuseMount;
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+
+public class AppFuseBridge implements Runnable {
+ private static final String TAG = AppFuseBridge.class.getSimpleName();
+
+ private final FileDescriptor mDeviceFd;
+ private final FileDescriptor mProxyFd;
+ private final CountDownLatch mMountLatch = new CountDownLatch(1);
+
+ /**
+ * @param deviceFd FD of /dev/fuse. Ownership of fd is taken by AppFuseBridge.
+ * @param proxyFd FD of socket pair. Ownership of fd is taken by AppFuseBridge.
+ */
+ private AppFuseBridge(FileDescriptor deviceFd, FileDescriptor proxyFd) {
+ mDeviceFd = deviceFd;
+ mProxyFd = proxyFd;
+ }
+
+ public static AppFuseMount startMessageLoop(
+ int uid,
+ String name,
+ FileDescriptor deviceFd,
+ Handler handler,
+ ParcelFileDescriptor.OnCloseListener listener)
+ throws IOException, ErrnoException, InterruptedException {
+ final FileDescriptor localFd = new FileDescriptor();
+ final FileDescriptor remoteFd = new FileDescriptor();
+ // Needs to specify OsConstants.SOCK_SEQPACKET to keep message boundaries.
+ Os.socketpair(OsConstants.AF_UNIX, OsConstants.SOCK_SEQPACKET, 0, remoteFd, localFd);
+
+ // Caller must invoke #start() after instantiate AppFuseBridge.
+ // Otherwise FDs will be leaked.
+ final AppFuseBridge bridge = new AppFuseBridge(deviceFd, localFd);
+ final Thread thread = new Thread(bridge, TAG);
+ thread.start();
+ try {
+ bridge.mMountLatch.await();
+ } catch (InterruptedException error) {
+ throw error;
+ }
+ return new AppFuseMount(
+ new File("/mnt/appfuse/" + uid + "_" + name),
+ ParcelFileDescriptor.fromFd(remoteFd, handler, listener));
+ }
+
+ @Override
+ public void run() {
+ // deviceFd and proxyFd must be closed in native_start_loop.
+ final int deviceFd = mDeviceFd.getInt$();
+ final int proxyFd = mProxyFd.getInt$();
+ mDeviceFd.setInt$(-1);
+ mProxyFd.setInt$(-1);
+ native_start_loop(deviceFd, proxyFd);
+ }
+
+ // Used by com_android_server_storage_AppFuse.cpp.
+ private void onMount() {
+ mMountLatch.countDown();
+ }
+
+ private native boolean native_start_loop(int deviceFd, int proxyFd);
+}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 622eece..05e6f96 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 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 @@
}
@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 5bfece4..cf5cecda 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 @@
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 @@
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 ec4cdf2..6a625f4 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.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 @@
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 @@
}
@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 1ccf722..c711b39 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 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 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 @@
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 @@
// 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 @@
}
@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 @@
}
/**
+ * 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 @@
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/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index f038135..299fa05 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -40,9 +40,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.HashMap;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.app.AppOpsManager.OP_NONE;
@@ -55,17 +53,13 @@
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
-import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
+
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEEP_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_POWER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
@@ -78,7 +72,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
-import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_NONE;
@@ -760,9 +753,6 @@
WindowToken token = exitingTokens.get(i);
if (!token.hasVisible) {
exitingTokens.remove(i);
- if (token.windowType == TYPE_WALLPAPER) {
- displayContent.mWallpaperController.removeWallpaperToken(token);
- }
}
}
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index a0270c6..203ba72 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 @@
// 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 @@
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 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/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 250d381..178fbe7 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -55,7 +55,7 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "WallpaperController" : TAG_WM;
final private WindowManagerService mService;
- private final ArrayList<WindowToken> mWallpaperTokens = new ArrayList<>();
+ private final ArrayList<WallpaperWindowToken> mWallpaperTokens = new ArrayList<>();
// If non-null, this is the currently visible window that is associated
// with the wallpaper.
@@ -135,7 +135,7 @@
*/
void startWallpaperAnimation(Animation a) {
for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
- final WindowToken token = mWallpaperTokens.get(curTokenNdx);
+ final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
token.startAnimation(a);
}
}
@@ -163,7 +163,7 @@
final boolean visible = isWallpaperVisible(mWallpaperTarget);
for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
- final WindowToken token = mWallpaperTokens.get(curTokenNdx);
+ final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
token.updateWallpaperVisibility(visible);
}
}
@@ -189,7 +189,7 @@
final boolean wasDeferred = (mDeferredHideWallpaper == winGoingAway);
for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
- final WindowToken token = mWallpaperTokens.get(i);
+ final WallpaperWindowToken token = mWallpaperTokens.get(i);
token.hideWallpaperToken(wasDeferred, "hideWallpapers");
if (DEBUG_WALLPAPER_LIGHT && !token.hidden) Slog.d(TAG, "Hiding wallpaper " + token
+ " from " + winGoingAway + " target=" + mWallpaperTarget + " lower="
@@ -304,7 +304,7 @@
|| window == mUpperWallpaperTarget) {
boolean doWait = sync;
for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
- final WindowToken token = mWallpaperTokens.get(curTokenNdx);
+ final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
token.sendWindowWallpaperCommand(action, x, y, z, extras, sync);
}
@@ -652,7 +652,7 @@
// Start stepping backwards from here, ensuring that our wallpaper windows are correctly placed.
boolean changed = false;
for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
- final WindowToken token = mWallpaperTokens.get(curTokenNdx);
+ final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
changed |= token.updateWallpaperWindowsPlacement(windows, wallpaperTarget,
wallpaperTargetIndex, visible, dw, dh, mWallpaperAnimLayerAdjustment);
}
@@ -725,7 +725,7 @@
boolean wallpaperReady = true;
for (int curTokenIndex = mWallpaperTokens.size() - 1;
curTokenIndex >= 0 && wallpaperReady; curTokenIndex--) {
- final WindowToken token = mWallpaperTokens.get(curTokenIndex);
+ final WallpaperWindowToken token = mWallpaperTokens.get(curTokenIndex);
if (token.hasVisibleNotDrawnWallpaper()) {
// We've told this wallpaper to be visible, but it is not drawn yet
wallpaperReady = false;
@@ -777,11 +777,11 @@
}
}
- void addWallpaperToken(WindowToken token) {
+ void addWallpaperToken(WallpaperWindowToken token) {
mWallpaperTokens.add(token);
}
- void removeWallpaperToken(WindowToken token) {
+ void removeWallpaperToken(WallpaperWindowToken token) {
mWallpaperTokens.remove(token);
}
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
new file mode 100644
index 0000000..fdefcfe
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -0,0 +1,228 @@
+/*
+ * 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 static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.DisplayInfo;
+import android.view.animation.Animation;
+
+/**
+ * A token that represents a set of wallpaper windows.
+ */
+class WallpaperWindowToken extends WindowToken {
+
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "WallpaperWindowToken" : TAG_WM;
+
+ WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit,
+ DisplayContent dc) {
+ super(service, token, TYPE_WALLPAPER, explicit, dc);
+ dc.mWallpaperController.addWallpaperToken(this);
+ }
+
+ @Override
+ void setExiting() {
+ super.setExiting();
+ mDisplayContent.mWallpaperController.removeWallpaperToken(this);
+ }
+
+ void hideWallpaperToken(boolean wasDeferred, String reason) {
+ for (int j = mChildren.size() - 1; j >= 0; j--) {
+ final WindowState wallpaper = mChildren.get(j);
+ wallpaper.hideWallpaperWindow(wasDeferred, reason);
+ }
+ hidden = true;
+ }
+
+ void sendWindowWallpaperCommand(
+ String action, int x, int y, int z, Bundle extras, boolean sync) {
+ for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
+ final WindowState wallpaper = mChildren.get(wallpaperNdx);
+ try {
+ wallpaper.mClient.dispatchWallpaperCommand(action, x, y, z, extras, sync);
+ // We only want to be synchronous with one wallpaper.
+ sync = false;
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ void updateWallpaperOffset(int dw, int dh, boolean sync) {
+ final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
+ for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
+ final WindowState wallpaper = mChildren.get(wallpaperNdx);
+ if (wallpaperController.updateWallpaperOffset(wallpaper, dw, dh, sync)) {
+ final WindowStateAnimator winAnimator = wallpaper.mWinAnimator;
+ winAnimator.computeShownFrameLocked();
+ // No need to lay out the windows - we can just set the wallpaper position directly.
+ winAnimator.setWallpaperOffset(wallpaper.mShownPosition);
+ // We only want to be synchronous with one wallpaper.
+ sync = false;
+ }
+ }
+ }
+
+ void updateWallpaperVisibility(boolean visible) {
+ final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
+ final int dw = displayInfo.logicalWidth;
+ final int dh = displayInfo.logicalHeight;
+
+ if (hidden == visible) {
+ hidden = !visible;
+ // Need to do a layout to ensure the wallpaper now has the correct size.
+ mDisplayContent.setLayoutNeeded();
+ }
+
+ final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
+ for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
+ final WindowState wallpaper = mChildren.get(wallpaperNdx);
+ if (visible) {
+ wallpaperController.updateWallpaperOffset(wallpaper, dw, dh, false);
+ }
+
+ wallpaper.dispatchWallpaperVisibility(visible);
+ }
+ }
+
+ /**
+ * Starts {@param anim} on all children.
+ */
+ void startAnimation(Animation anim) {
+ for (int ndx = mChildren.size() - 1; ndx >= 0; ndx--) {
+ final WindowState windowState = mChildren.get(ndx);
+ windowState.mWinAnimator.setAnimation(anim);
+ }
+ }
+
+ boolean updateWallpaperWindowsPlacement(ReadOnlyWindowList windowList,
+ WindowState wallpaperTarget, int wallpaperTargetIndex, boolean visible, int dw, int dh,
+ int wallpaperAnimLayerAdj) {
+
+ boolean changed = false;
+ if (hidden == visible) {
+ if (DEBUG_WALLPAPER_LIGHT) Slog.d(TAG,
+ "Wallpaper token " + token + " hidden=" + !visible);
+ hidden = !visible;
+ // Need to do a layout to ensure the wallpaper now has the correct size.
+ mDisplayContent.setLayoutNeeded();
+ }
+
+ final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
+ for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
+ final WindowState wallpaper = mChildren.get(wallpaperNdx);
+
+ if (visible) {
+ wallpaperController.updateWallpaperOffset(wallpaper, dw, dh, false);
+ }
+
+ // First, make sure the client has the current visibility state.
+ wallpaper.dispatchWallpaperVisibility(visible);
+ wallpaper.adjustAnimLayer(wallpaperAnimLayerAdj);
+
+ if (DEBUG_LAYERS || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "adjustWallpaper win "
+ + wallpaper + " anim layer: " + wallpaper.mWinAnimator.mAnimLayer);
+
+ // First, if this window is at the current index, then all is well.
+ if (wallpaper == wallpaperTarget) {
+ wallpaperTargetIndex--;
+ wallpaperTarget = wallpaperTargetIndex > 0
+ ? windowList.get(wallpaperTargetIndex - 1) : null;
+ continue;
+ }
+
+ // The window didn't match... the current wallpaper window,
+ // wherever it is, is in the wrong place, so make sure it is not in the list.
+ int oldIndex = windowList.indexOf(wallpaper);
+ if (oldIndex >= 0) {
+ if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG,
+ "Wallpaper removing at " + oldIndex + ": " + wallpaper);
+ mDisplayContent.removeFromWindowList(wallpaper);
+ if (oldIndex < wallpaperTargetIndex) {
+ wallpaperTargetIndex--;
+ }
+ }
+
+ // Now stick it in. For apps over wallpaper keep the wallpaper at the bottommost
+ // layer. For keyguard over wallpaper put the wallpaper under the lowest window that
+ // is currently on screen, i.e. not hidden by policy.
+ int insertionIndex = 0;
+ if (visible && wallpaperTarget != null) {
+ final int privateFlags = wallpaperTarget.mAttrs.privateFlags;
+ if ((privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
+ insertionIndex = Math.min(windowList.indexOf(wallpaperTarget),
+ findLowestWindowOnScreen(windowList));
+ }
+ }
+ if (DEBUG_WALLPAPER_LIGHT || DEBUG_WINDOW_MOVEMENT
+ || (DEBUG_ADD_REMOVE && oldIndex != insertionIndex)) Slog.v(TAG,
+ "Moving wallpaper " + wallpaper + " from " + oldIndex + " to " + insertionIndex);
+
+ mDisplayContent.addToWindowList(wallpaper, insertionIndex);
+ changed = true;
+ }
+
+ return changed;
+ }
+
+ /**
+ * @return The index in {@param windows} of the lowest window that is currently on screen and
+ * not hidden by the policy.
+ */
+ private int findLowestWindowOnScreen(ReadOnlyWindowList windowList) {
+ final int size = windowList.size();
+ for (int index = 0; index < size; index++) {
+ final WindowState win = windowList.get(index);
+ if (win.isOnScreen()) {
+ return index;
+ }
+ }
+ return Integer.MAX_VALUE;
+ }
+
+ boolean hasVisibleNotDrawnWallpaper() {
+ for (int j = mChildren.size() - 1; j >= 0; --j) {
+ final WindowState wallpaper = mChildren.get(j);
+ if (wallpaper.hasVisibleNotDrawnWallpaper()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ if (stringName == null) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("WallpaperWindowToken{");
+ sb.append(Integer.toHexString(System.identityHashCode(this)));
+ sb.append(" token="); sb.append(token); sb.append('}');
+ stringName = sb.toString();
+ }
+ return stringName;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index e30ebcb..62ad217 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 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 @@
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 @@
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 a533d84..5c9dc10 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.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;
@@ -2415,9 +2414,10 @@
+ " displayId=" + displayId);
return;
}
- token = new WindowToken(this, binder, type, true, dc);
if (type == TYPE_WALLPAPER) {
- dc.mWallpaperController.addWallpaperToken(token);
+ new WallpaperWindowToken(this, binder, true, dc);
+ } else {
+ new WindowToken(this, binder, type, true, dc);
}
}
}
@@ -2446,9 +2446,6 @@
}
token.setExiting();
- if (token.windowType == TYPE_WALLPAPER) {
- dc.mWallpaperController.removeWallpaperToken(token);
- }
mInputMonitor.updateInputWindowsLw(true /*force*/);
}
@@ -3404,7 +3401,7 @@
public Rect getPictureInPictureDefaultBounds(int displayId) {
synchronized (mWindowMap) {
if (!mSupportsPictureInPicture) {
- return new Rect();
+ return null;
}
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
@@ -3416,7 +3413,7 @@
public Rect getPictureInPictureMovementBounds(int displayId) {
synchronized (mWindowMap) {
if (!mSupportsPictureInPicture) {
- return new Rect();
+ return null;
}
final Rect stackBounds = new Rect();
@@ -3430,6 +3427,47 @@
}
}
+ 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.
@@ -3578,6 +3616,11 @@
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.
@@ -6116,6 +6159,7 @@
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.
@@ -6754,6 +6798,11 @@
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");
@@ -8203,6 +8252,8 @@
StringBuilder output = new StringBuilder();
mRoot.dumpChildrenNames(output, " ");
pw.println(output.toString());
+ pw.println(" ");
+ mRoot.forAllWindows(pw::println, true /* traverseTopToBottom */);
}
return;
} else {
@@ -8456,6 +8507,7 @@
}
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 972c359..1a56518 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.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 @@
* 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 @@
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 b821f09..402bcfb 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -82,7 +82,7 @@
boolean sendingToBottom;
// The display this token is on.
- private DisplayContent mDisplayContent;
+ protected DisplayContent mDisplayContent;
/**
* Compares two child window of this token and returns -1 if the first is lesser than the
@@ -196,9 +196,8 @@
*/
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) {
@@ -280,169 +279,6 @@
return false;
}
- void hideWallpaperToken(boolean wasDeferred, String reason) {
- for (int j = mChildren.size() - 1; j >= 0; j--) {
- final WindowState wallpaper = mChildren.get(j);
- wallpaper.hideWallpaperWindow(wasDeferred, reason);
- }
- hidden = true;
- }
-
- void sendWindowWallpaperCommand(
- String action, int x, int y, int z, Bundle extras, boolean sync) {
- for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
- final WindowState wallpaper = mChildren.get(wallpaperNdx);
- try {
- wallpaper.mClient.dispatchWallpaperCommand(action, x, y, z, extras, sync);
- // We only want to be synchronous with one wallpaper.
- sync = false;
- } catch (RemoteException e) {
- }
- }
- }
-
- void updateWallpaperOffset(int dw, int dh, boolean sync) {
- final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
- for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
- final WindowState wallpaper = mChildren.get(wallpaperNdx);
- if (wallpaperController.updateWallpaperOffset(wallpaper, dw, dh, sync)) {
- final WindowStateAnimator winAnimator = wallpaper.mWinAnimator;
- winAnimator.computeShownFrameLocked();
- // No need to lay out the windows - we can just set the wallpaper position directly.
- winAnimator.setWallpaperOffset(wallpaper.mShownPosition);
- // We only want to be synchronous with one wallpaper.
- sync = false;
- }
- }
- }
-
- void updateWallpaperVisibility(boolean visible) {
- final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
- final int dw = displayInfo.logicalWidth;
- final int dh = displayInfo.logicalHeight;
-
- if (hidden == visible) {
- hidden = !visible;
- // Need to do a layout to ensure the wallpaper now has the correct size.
- mDisplayContent.setLayoutNeeded();
- }
-
- final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
- for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
- final WindowState wallpaper = mChildren.get(wallpaperNdx);
- if (visible) {
- wallpaperController.updateWallpaperOffset(wallpaper, dw, dh, false);
- }
-
- wallpaper.dispatchWallpaperVisibility(visible);
- }
- }
-
- /**
- * Starts {@param anim} on all children.
- */
- void startAnimation(Animation anim) {
- for (int ndx = mChildren.size() - 1; ndx >= 0; ndx--) {
- final WindowState windowState = mChildren.get(ndx);
- windowState.mWinAnimator.setAnimation(anim);
- }
- }
-
- boolean updateWallpaperWindowsPlacement(ReadOnlyWindowList windowList,
- WindowState wallpaperTarget, int wallpaperTargetIndex, boolean visible, int dw, int dh,
- int wallpaperAnimLayerAdj) {
-
- boolean changed = false;
- if (hidden == visible) {
- if (DEBUG_WALLPAPER_LIGHT) Slog.d(TAG,
- "Wallpaper token " + token + " hidden=" + !visible);
- hidden = !visible;
- // Need to do a layout to ensure the wallpaper now has the correct size.
- mDisplayContent.setLayoutNeeded();
- }
-
- final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
- for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
- final WindowState wallpaper = mChildren.get(wallpaperNdx);
-
- if (visible) {
- wallpaperController.updateWallpaperOffset(wallpaper, dw, dh, false);
- }
-
- // First, make sure the client has the current visibility state.
- wallpaper.dispatchWallpaperVisibility(visible);
- wallpaper.adjustAnimLayer(wallpaperAnimLayerAdj);
-
- if (DEBUG_LAYERS || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "adjustWallpaper win "
- + wallpaper + " anim layer: " + wallpaper.mWinAnimator.mAnimLayer);
-
- // First, if this window is at the current index, then all is well.
- if (wallpaper == wallpaperTarget) {
- wallpaperTargetIndex--;
- wallpaperTarget = wallpaperTargetIndex > 0
- ? windowList.get(wallpaperTargetIndex - 1) : null;
- continue;
- }
-
- // The window didn't match... the current wallpaper window,
- // wherever it is, is in the wrong place, so make sure it is not in the list.
- int oldIndex = windowList.indexOf(wallpaper);
- if (oldIndex >= 0) {
- if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG,
- "Wallpaper removing at " + oldIndex + ": " + wallpaper);
- mDisplayContent.removeFromWindowList(wallpaper);
- if (oldIndex < wallpaperTargetIndex) {
- wallpaperTargetIndex--;
- }
- }
-
- // Now stick it in. For apps over wallpaper keep the wallpaper at the bottommost
- // layer. For keyguard over wallpaper put the wallpaper under the lowest window that
- // is currently on screen, i.e. not hidden by policy.
- int insertionIndex = 0;
- if (visible && wallpaperTarget != null) {
- final int privateFlags = wallpaperTarget.mAttrs.privateFlags;
- if ((privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
- insertionIndex = Math.min(windowList.indexOf(wallpaperTarget),
- findLowestWindowOnScreen(windowList));
- }
- }
- if (DEBUG_WALLPAPER_LIGHT || DEBUG_WINDOW_MOVEMENT
- || (DEBUG_ADD_REMOVE && oldIndex != insertionIndex)) Slog.v(TAG,
- "Moving wallpaper " + wallpaper + " from " + oldIndex + " to " + insertionIndex);
-
- mDisplayContent.addToWindowList(wallpaper, insertionIndex);
- changed = true;
- }
-
- return changed;
- }
-
- /**
- * @return The index in {@param windows} of the lowest window that is currently on screen and
- * not hidden by the policy.
- */
- private int findLowestWindowOnScreen(ReadOnlyWindowList windowList) {
- final int size = windowList.size();
- for (int index = 0; index < size; index++) {
- final WindowState win = windowList.get(index);
- if (win.isOnScreen()) {
- return index;
- }
- }
- return Integer.MAX_VALUE;
- }
-
- boolean hasVisibleNotDrawnWallpaper() {
- for (int j = mChildren.size() - 1; j >= 0; --j) {
- final WindowState wallpaper = mChildren.get(j);
- if (wallpaper.hasVisibleNotDrawnWallpaper()) {
- return true;
- }
- }
- return false;
- }
-
int getHighestAnimLayer() {
int highest = -1;
for (int j = 0; j < mChildren.size(); j++) {
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index a4b5e6a..c03a460 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -19,6 +19,7 @@
$(LOCAL_REL_DIR)/com_android_server_location_FlpHardwareProvider.cpp \
$(LOCAL_REL_DIR)/com_android_server_power_PowerManagerService.cpp \
$(LOCAL_REL_DIR)/com_android_server_SerialService.cpp \
+ $(LOCAL_REL_DIR)/com_android_server_storage_AppFuseBridge.cpp \
$(LOCAL_REL_DIR)/com_android_server_SystemServer.cpp \
$(LOCAL_REL_DIR)/com_android_server_tv_TvUinputBridge.cpp \
$(LOCAL_REL_DIR)/com_android_server_tv_TvInputHal.cpp \
@@ -37,6 +38,7 @@
frameworks/base/libs/hwui \
frameworks/base/core/jni \
frameworks/native/services \
+ system/core/libappfuse/include \
system/security/keystore/include \
$(call include-path-for, libhardware)/hardware \
$(call include-path-for, libhardware_legacy)/hardware_legacy \
@@ -44,6 +46,7 @@
LOCAL_SHARED_LIBRARIES += \
libandroid_runtime \
libandroidfw \
+ libappfuse \
libbinder \
libcutils \
liblog \
diff --git a/services/core/jni/com_android_server_storage_AppFuseBridge.cpp b/services/core/jni/com_android_server_storage_AppFuseBridge.cpp
new file mode 100644
index 0000000..640fd0e
--- /dev/null
+++ b/services/core/jni/com_android_server_storage_AppFuseBridge.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 specic language governing permissions and
+ * limitations under the License.
+ */
+
+// Need to use LOGE_EX.
+#define LOG_TAG "AppFuseBridge"
+
+#include <android_runtime/Log.h>
+#include <android-base/logging.h>
+#include <core_jni_helpers.h>
+#include <libappfuse/FuseBridgeLoop.h>
+#include <nativehelper/JNIHelp.h>
+
+namespace android {
+namespace {
+
+constexpr const char* CLASS_NAME = "com/android/server/storage/AppFuseBridge";
+static jclass appFuseClass;
+static jmethodID appFuseOnMount;
+
+class Callback : public FuseBridgeLoop::Callback {
+ JNIEnv* mEnv;
+ jobject mSelf;
+
+public:
+ Callback(JNIEnv* env, jobject self) : mEnv(env), mSelf(self) {}
+ void OnMount() override {
+ mEnv->CallVoidMethod(mSelf, appFuseOnMount);
+ if (mEnv->ExceptionCheck()) {
+ LOGE_EX(mEnv, nullptr);
+ mEnv->ExceptionClear();
+ }
+ }
+};
+
+jboolean com_android_server_storage_AppFuseBridge_start_loop(
+ JNIEnv* env, jobject self, jint devJavaFd, jint proxyJavaFd) {
+ FuseBridgeLoop loop;
+ Callback callback(env, self);
+ return loop.Start(devJavaFd, proxyJavaFd, &callback);
+}
+
+const JNINativeMethod methods[] = {
+ {
+ "native_start_loop",
+ "(II)Z",
+ (void *) com_android_server_storage_AppFuseBridge_start_loop
+ }
+};
+
+} // namespace
+
+void register_android_server_storage_AppFuse(JNIEnv* env) {
+ CHECK(env != nullptr);
+
+ appFuseClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, CLASS_NAME));
+ appFuseOnMount = GetMethodIDOrDie(env, appFuseClass, "onMount", "()V");
+ RegisterMethodsOrDie(env, CLASS_NAME, methods, NELEM(methods));
+}
+} // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index d69c37f..c291ba0 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -28,6 +28,7 @@
int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
+int register_android_server_storage_AppFuse(JNIEnv* env);
int register_android_server_SerialService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
int register_android_server_UsbDeviceManager(JNIEnv* env);
@@ -83,6 +84,7 @@
register_android_server_PersistentDataBlockService(env);
register_android_server_Watchdog(env);
register_android_server_HardwarePropertiesManagerService(env);
+ register_android_server_storage_AppFuse(env);
return JNI_VERSION_1_4;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 0ea8d4e..7f8b9b8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -22,7 +22,7 @@
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
import static android.content.pm.PackageManager.GET_UNINSTALLED_PACKAGES;
-import static com.android.internal.logging.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index cc96f56..3db24fa 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -27,6 +27,7 @@
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 @@
"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 @@
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 @@
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/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 01d9304..39f14e5 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -631,6 +631,11 @@
return shouldLog;
}
+ // TODO: Migrate all Log.e(...) to logError(...).
+ private void logError(String fmt, Object... args) {
+ mLocalLog.log("ERROR " + String.format(fmt, args));
+ }
+
private void getNetworkInterface() {
try {
mNetworkInterface = NetworkInterface.getByName(mInterfaceName);
@@ -880,7 +885,7 @@
mNwService.setInterfaceConfig(mInterfaceName, ifcg);
if (VDBG) Log.d(mTag, "IPv4 configuration succeeded");
} catch (IllegalStateException | RemoteException e) {
- Log.e(mTag, "IPv4 configuration failed: ", e);
+ logError("IPv4 configuration failed: %s", e);
return false;
}
return true;
@@ -944,6 +949,12 @@
}
}
+ private void doImmediateProvisioningFailure(int failureType) {
+ if (DBG) { Log.e(mTag, "onProvisioningFailure(): " + failureType); }
+ recordMetric(failureType);
+ mCallback.onProvisioningFailure(new LinkProperties(mLinkProperties));
+ }
+
private boolean startIPv4() {
// If we have a StaticIpConfiguration attempt to apply it and
// handle the result accordingly.
@@ -951,9 +962,6 @@
if (setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) {
handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
} else {
- if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
- recordMetric(IpManagerEvent.PROVISIONING_FAIL);
- mCallback.onProvisioningFailure(new LinkProperties(mLinkProperties));
return false;
}
} else {
@@ -972,16 +980,40 @@
mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
mNwService.enableIpv6(mInterfaceName);
} catch (RemoteException re) {
- Log.e(mTag, "Unable to change interface settings: " + re);
+ logError("Unable to change interface settings: %s", re);
return false;
} catch (IllegalStateException ie) {
- Log.e(mTag, "Unable to change interface settings: " + ie);
+ logError("Unable to change interface settings: %s", ie);
return false;
}
return true;
}
+ private boolean startIpReachabilityMonitor() {
+ try {
+ mIpReachabilityMonitor = new IpReachabilityMonitor(
+ mContext,
+ mInterfaceName,
+ new IpReachabilityMonitor.Callback() {
+ @Override
+ public void notifyLost(InetAddress ip, String logMsg) {
+ mCallback.onReachabilityLost(logMsg);
+ }
+ },
+ mAvoidBadWifiTracker);
+ } catch (IllegalArgumentException iae) {
+ // Failed to start IpReachabilityMonitor. Log it and call
+ // onProvisioningFailure() immediately.
+ //
+ // See http://b/31038971.
+ logError("IpReachabilityMonitor failure: %s", iae);
+ mIpReachabilityMonitor = null;
+ }
+
+ return (mIpReachabilityMonitor != null);
+ }
+
private void stopAllIP() {
// We don't need to worry about routes, just addresses, because:
// - disableIpv6() will clear autoconf IPv6 routes as well, and
@@ -1165,29 +1197,23 @@
mCallback.setFallbackMulticastFilter(mMulticastFiltering);
}
- if (mConfiguration.mEnableIPv6) {
- // TODO: Consider transitionTo(mStoppingState) if this fails.
- startIPv6();
+ if (mConfiguration.mEnableIPv6 && !startIPv6()) {
+ doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6);
+ transitionTo(mStoppingState);
+ return;
}
- if (mConfiguration.mEnableIPv4) {
- if (!startIPv4()) {
- transitionTo(mStoppingState);
- return;
- }
+ if (mConfiguration.mEnableIPv4 && !startIPv4()) {
+ doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV4);
+ transitionTo(mStoppingState);
+ return;
}
- if (mConfiguration.mUsingIpReachabilityMonitor) {
- mIpReachabilityMonitor = new IpReachabilityMonitor(
- mContext,
- mInterfaceName,
- new IpReachabilityMonitor.Callback() {
- @Override
- public void notifyLost(InetAddress ip, String logMsg) {
- mCallback.onReachabilityLost(logMsg);
- }
- },
- mAvoidBadWifiTracker);
+ if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) {
+ doImmediateProvisioningFailure(
+ IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR);
+ transitionTo(mStoppingState);
+ return;
}
}
diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java
index 07b26e8..6b919df 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.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 @@
.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/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index 65c740f..7474a64 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -68,7 +68,7 @@
import com.android.internal.R;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.SomeArgs;
import com.android.server.print.RemotePrintService.PrintServiceCallbacks;
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 4570a4b..d20e351 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 @@
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 22b674b..6c3f447 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 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 @@
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 305b5e0..6bc9675 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 @@
.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 629146f..92c67b5 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 @@
.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 @@
.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 @@
.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 ffc45ea..7a3ee7f 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 @@
.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/LockSettingsShellCommandTest.java b/services/tests/servicestests/src/com/android/server/LockSettingsShellCommandTest.java
new file mode 100644
index 0000000..d6ee367
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/LockSettingsShellCommandTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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;
+
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+
+import static com.android.internal.widget.LockPatternUtils.stringToPattern;
+
+import static junit.framework.Assert.*;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import static java.io.FileDescriptor.*;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.internal.widget.LockPatternUtils;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.FileDescriptor;
+
+/**
+ * Test class for {@link LockSettingsShellCommand}.
+ *
+ * runtest frameworks-services -c com.android.server.LockSettingsShellCommandTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class LockSettingsShellCommandTest {
+
+ private LockSettingsShellCommand mCommand;
+
+ private @Mock LockPatternUtils mLockPatternUtils;
+ private int mUserId;
+ private final Binder mBinder = new Binder();
+ private final ShellCallback mShellCallback = new ShellCallback();
+ private final ResultReceiver mResultReceiver = new ResultReceiver(
+ new Handler(Looper.getMainLooper()));
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ final Context context = InstrumentationRegistry.getTargetContext();
+ mUserId = ActivityManager.getCurrentUser();
+ mCommand = new LockSettingsShellCommand(context, mLockPatternUtils);
+ }
+
+ @Test
+ public void testWrongPassword() throws Exception {
+ when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
+ when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
+ when(mLockPatternUtils.checkPassword("1234", mUserId)).thenReturn(false);
+ assertEquals(-1, mCommand.exec(mBinder, in, out, err,
+ new String[] { "set-pin", "--old", "1234" },
+ mShellCallback, mResultReceiver));
+ verify(mLockPatternUtils, never()).saveLockPassword(any(), any(), anyInt(), anyInt());
+ }
+
+ @Test
+ public void testChangePin() throws Exception {
+ when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
+ when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
+ when(mLockPatternUtils.checkPassword("1234", mUserId)).thenReturn(true);
+ assertEquals(0, mCommand.exec(new Binder(), in, out, err,
+ new String[] { "set-pin", "--old", "1234", "4321" },
+ mShellCallback, mResultReceiver));
+ verify(mLockPatternUtils).saveLockPassword("4321", "1234", PASSWORD_QUALITY_NUMERIC,
+ mUserId);
+ }
+
+ @Test
+ public void testChangePassword() throws Exception {
+ when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
+ when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
+ when(mLockPatternUtils.checkPassword("1234", mUserId)).thenReturn(true);
+ assertEquals(0, mCommand.exec(new Binder(), in, out, err,
+ new String[] { "set-password", "--old", "1234", "4321" },
+ mShellCallback, mResultReceiver));
+ verify(mLockPatternUtils).saveLockPassword("4321", "1234", PASSWORD_QUALITY_ALPHABETIC,
+ mUserId);
+ }
+
+ @Test
+ public void testChangePattern() throws Exception {
+ when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
+ when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
+ when(mLockPatternUtils.checkPattern(stringToPattern("1234"), mUserId)).thenReturn(true);
+ assertEquals(0, mCommand.exec(new Binder(), in, out, err,
+ new String[] { "set-pattern", "--old", "1234", "4321" },
+ mShellCallback, mResultReceiver));
+ verify(mLockPatternUtils).saveLockPattern(stringToPattern("4321"), "1234", mUserId);
+ }
+
+ @Test
+ public void testClear() throws Exception {
+ when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
+ when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
+ when(mLockPatternUtils.checkPattern(stringToPattern("1234"), mUserId)).thenReturn(true);
+ assertEquals(0, mCommand.exec(new Binder(), in, out, err,
+ new String[] { "clear", "--old", "1234" },
+ mShellCallback, mResultReceiver));
+ verify(mLockPatternUtils).clearLock(mUserId);
+ }
+}
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 d933cb3..772b2a2 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_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 0000000..0533efc
--- /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 5326a19..d12d672a 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.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 62625bdf..c99e22a 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -843,6 +843,7 @@
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 @@
}
/** {@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 @@
/** {@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 @@
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 8f9c758..2b9a508 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -25,6 +25,7 @@
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 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 @@
/**
* 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 @@
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 @@
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 @@
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 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 @@
* {@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 @@
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 69de89d..5d68aae 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -87,7 +87,8 @@
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 @@
* {@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 4a6fd7c..1900cb9 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -182,10 +182,10 @@
* @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 a4ef560..30ec5b3 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -125,13 +125,16 @@
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/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 473e394..0457d63 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -114,7 +114,10 @@
public static final int CAPABILITY_SIM_SUBSCRIPTION = 0x4;
/**
- * Flag indicating that this {@code PhoneAccount} is capable of placing video calls.
+ * Flag indicating that this {@code PhoneAccount} is currently able to place video calls.
+ * <p>
+ * See also {@link #CAPABILITY_SUPPORTS_VIDEO_CALLING} which indicates whether the
+ * {@code PhoneAccount} supports placing video calls.
* <p>
* See {@link #getCapabilities}
*/
@@ -179,6 +182,23 @@
public static final int CAPABILITY_EMERGENCY_VIDEO_CALLING = 0x200;
/**
+ * Flag indicating that this {@link PhoneAccount} supports video calling.
+ * This is not an indication that the {@link PhoneAccount} is currently able to make a video
+ * call, but rather that it has the ability to make video calls (but not necessarily at this
+ * time).
+ * <p>
+ * Whether a {@link PhoneAccount} can make a video call is ultimately controlled by
+ * {@link #CAPABILITY_VIDEO_CALLING}, which indicates whether the {@link PhoneAccount} is
+ * currently capable of making a video call. Consider a case where, for example, a
+ * {@link PhoneAccount} supports making video calls (e.g.
+ * {@link #CAPABILITY_SUPPORTS_VIDEO_CALLING}), but a current lack of network connectivity
+ * prevents video calls from being made (e.g. {@link #CAPABILITY_VIDEO_CALLING}).
+ * <p>
+ * See {@link #getCapabilities}
+ */
+ public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 0x400;
+
+ /**
* URI scheme for telephone number URIs.
*/
public static final String SCHEME_TEL = "tel";
@@ -762,6 +782,9 @@
*/
private String capabilitiesToString(int capabilities) {
StringBuilder sb = new StringBuilder();
+ if (hasCapabilities(CAPABILITY_SUPPORTS_VIDEO_CALLING)) {
+ sb.append("SuppVideo ");
+ }
if (hasCapabilities(CAPABILITY_VIDEO_CALLING)) {
sb.append("Video ");
}
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index 0e4f53e..77e0e54 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -408,6 +408,8 @@
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 @@
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 void setCamera(String cameraId) {
try {
- mVideoProviderBinder.setCamera(cameraId);
+ mVideoProviderBinder.setCamera(cameraId, mCallingPackage);
} catch (RemoteException e) {
}
}
@@ -628,7 +631,7 @@
* @hide
*/
RemoteConnection(String callId, IConnectionService connectionService,
- ParcelableConnection connection) {
+ ParcelableConnection connection, String callingPackage) {
mConnectionId = callId;
mConnectionService = connectionService;
mConnected = true;
@@ -640,7 +643,7 @@
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 0a8470a..d8a226a 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -283,9 +283,13 @@
@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 @@
@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 e54abee..d8ede5c 100644
--- a/telecomm/java/android/telecom/VideoCallImpl.java
+++ b/telecomm/java/android/telecom/VideoCallImpl.java
@@ -43,6 +43,7 @@
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 @@
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 @@
/** {@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 68e5fd4..a109e90 100644
--- a/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl
+++ b/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl
@@ -30,7 +30,7 @@
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 bb2b447..32f487b 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -233,7 +233,7 @@
* @hide
*/
/** @hide */
- protected int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ protected Integer mSubId;
private final Handler mHandler;
@@ -242,7 +242,7 @@
* 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 @@
* @hide
*/
public PhoneStateListener(Looper looper) {
- this(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, looper);
+ this(null, looper);
}
/**
@@ -260,7 +260,7 @@
* 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 @@
* 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 dcf2f06..20dd012 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -263,6 +263,22 @@
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 @@
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 @@
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 @@
}
/**
+ * 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/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index 6ff0c5a..6a064d2 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -25,7 +25,7 @@
import static com.android.server.connectivity.MetricsTestUtil.anIntArray;
import static com.android.server.connectivity.MetricsTestUtil.b;
import static com.android.server.connectivity.MetricsTestUtil.describeIpEvent;
-import static com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityLog;
+import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog;
import android.net.ConnectivityMetricsEvent;
import android.net.metrics.ApfProgramEvent;
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index c7982b1..78d16d9 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -34,7 +34,7 @@
import android.os.Parcelable;
import android.util.Base64;
-import com.android.server.connectivity.metrics.IpConnectivityLogClass;
+import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass;
import junit.framework.TestCase;
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 a30b362..9f7261d 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 @@
@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 @@
@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 @@
upstreamIface);
mLooper.dispatchAll();
}
-}
\ No newline at end of file
+}
diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp
index a06140c..f0b18e6 100644
--- a/tools/aapt2/compile/Compile.cpp
+++ b/tools/aapt2/compile/Compile.cpp
@@ -482,7 +482,9 @@
void BackUp(int count) override { buffer_->BackUp(count); }
- int64_t ByteCount() const override { return buffer_->size(); }
+ google::protobuf::int64 ByteCount() const override {
+ return buffer_->size();
+ }
bool HadError() const override { return false; }
diff --git a/tools/aapt2/compile/Png.cpp b/tools/aapt2/compile/Png.cpp
index f1bc53e..7ab05b5 100644
--- a/tools/aapt2/compile/Png.cpp
+++ b/tools/aapt2/compile/Png.cpp
@@ -232,6 +232,13 @@
}
}*/
+#ifdef MAX
+#undef MAX
+#endif
+#ifdef ABS
+#undef ABS
+#endif
+
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define ABS(a) ((a) < 0 ? -(a) : (a))
diff --git a/tools/aapt2/compile/Png.h b/tools/aapt2/compile/Png.h
index 01c9adb..aff1da3 100644
--- a/tools/aapt2/compile/Png.h
+++ b/tools/aapt2/compile/Png.h
@@ -62,8 +62,8 @@
void BackUp(int count) override;
bool Skip(int count) override;
- int64_t ByteCount() const override {
- return static_cast<int64_t>(window_start_);
+ google::protobuf::int64 ByteCount() const override {
+ return static_cast<google::protobuf::int64>(window_start_);
}
bool HadError() const override { return error_; }
diff --git a/tools/aapt2/proto/TableProtoSerializer.cpp b/tools/aapt2/proto/TableProtoSerializer.cpp
index 68db6b3..0d0e46d 100644
--- a/tools/aapt2/proto/TableProtoSerializer.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer.cpp
@@ -361,7 +361,7 @@
bool CompiledFileInputStream::ReadCompiledFile(pb::CompiledFile* out_val) {
EnsureAlignedRead();
- uint64_t pb_size = 0u;
+ google::protobuf::uint64 pb_size = 0u;
if (!in_.ReadLittleEndian64(&pb_size)) {
return false;
}
@@ -389,7 +389,7 @@
uint64_t* out_len) {
EnsureAlignedRead();
- uint64_t pb_size = 0u;
+ google::protobuf::uint64 pb_size = 0u;
if (!in_.ReadLittleEndian64(&pb_size)) {
return false;
}
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png
index 9bf302a..7b58539 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png
Binary files differ
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 88c9cbc..c8a5fec 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 e7c6cc9..55cfd6d 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 @@
/** 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 017525b..bc38284 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 @@
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 8d5efba..e48f7bd 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -22,6 +22,7 @@
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 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 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 @@
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 @@
txSuccess = txgood;
rxSuccess = rxgood;
txRetries = txretries;
+ mLastPacketCountUpdateTimeStamp = timeStamp;
} else {
txBad = 0;
txSuccess = 0;
@@ -200,6 +220,7 @@
txSuccessRate = 0;
rxSuccessRate = 0;
txRetriesRate = 0;
+ mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP;
}
}
@@ -243,6 +264,7 @@
mRssi = INVALID_RSSI;
mLinkSpeed = -1;
mFrequency = -1;
+ mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP;
}
/** @hide */
@@ -268,6 +290,7 @@
badRssiCount = 0;
linkStuckCount = 0;
score = 0;
+ mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP;
}
/**
@@ -295,6 +318,8 @@
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 18a580e..98a6ca5 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -26,6 +26,7 @@
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 @@
}
/**
+ * 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 0000000..96db5d0
--- /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 0000000..8c1eb08
--- /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 0000000..6d86dd5
--- /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 0000000..906bfb3
--- /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 0000000..3fa97d1
--- /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 0000000..975f8e5
--- /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 0000000..833c527
--- /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 0000000..d1f8384
--- /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 0000000..6095929
--- /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