Merge "Fix DPM.getPermissionGrantState()"
diff --git a/api/current.txt b/api/current.txt
index 829df43..3111484 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5197,7 +5197,8 @@
}
public static class Notification.Builder {
- ctor public Notification.Builder(android.content.Context);
+ ctor public Notification.Builder(android.content.Context, java.lang.String);
+ ctor public deprecated Notification.Builder(android.content.Context);
method public deprecated android.app.Notification.Builder addAction(int, java.lang.CharSequence, android.app.PendingIntent);
method public android.app.Notification.Builder addAction(android.app.Notification.Action);
method public android.app.Notification.Builder addExtras(android.os.Bundle);
@@ -6910,6 +6911,7 @@
method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProviders();
method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProvidersForProfile(android.os.UserHandle);
method public static android.appwidget.AppWidgetManager getInstance(android.content.Context);
+ method public boolean isRequestPinAppWidgetSupported();
method public void notifyAppWidgetViewDataChanged(int[], int);
method public void notifyAppWidgetViewDataChanged(int, int);
method public void partiallyUpdateAppWidget(int[], android.widget.RemoteViews);
@@ -9789,6 +9791,7 @@
field public int theme;
field public int uiOptions;
field public int uid;
+ field public java.lang.String volumeUuid;
}
public static class ApplicationInfo.DisplayNameComparator implements java.util.Comparator {
@@ -9917,7 +9920,8 @@
method public void startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
method public void startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle);
method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
- field public static final java.lang.String ACTION_CONFIRM_PIN_ITEM = "android.content.pm.action.CONFIRM_PIN_ITEM";
+ field public static final java.lang.String ACTION_CONFIRM_PIN_APPWIDGET = "android.content.pm.action.CONFIRM_PIN_APPWIDGET";
+ field public static final java.lang.String ACTION_CONFIRM_PIN_SHORTCUT = "android.content.pm.action.CONFIRM_PIN_SHORTCUT";
field public static final java.lang.String EXTRA_PIN_ITEM_REQUEST = "android.content.pm.extra.PIN_ITEM_REQUEST";
}
@@ -15242,6 +15246,7 @@
field public static final int VIRTUAL_DISPLAY_FLAG_PRESENTATION = 2; // 0x2
field public static final int VIRTUAL_DISPLAY_FLAG_PUBLIC = 1; // 0x1
field public static final int VIRTUAL_DISPLAY_FLAG_SECURE = 4; // 0x4
+ field public static final int VIRTUAL_DISPLAY_FLAG_SHOW_WITH_INSECURE_LOCKSCREEN = 32; // 0x20
}
public static abstract interface DisplayManager.DisplayListener {
@@ -23850,8 +23855,16 @@
}
public static final class TvContract.Programs implements android.media.tv.TvContract.BaseTvColumns {
+ field public static final java.lang.String ASPECT_RATIO_16_9 = "ASPECT_RATIO_16_9";
+ field public static final java.lang.String ASPECT_RATIO_1_1 = "ASPECT_RATIO_1_1";
+ field public static final java.lang.String ASPECT_RATIO_2_3 = "ASPECT_RATIO_2_3";
+ field public static final java.lang.String ASPECT_RATIO_3_2 = "ASPECT_RATIO_3_2";
+ field public static final java.lang.String AVAILABILITY_AVAILABLE = "AVAILABILITY_AVAILABLE";
+ field public static final java.lang.String AVAILABILITY_FREE_WITH_SUBSCRIPTION = "AVAILABILITY_FREE_WITH_SUBSCRIPTION";
+ field public static final java.lang.String AVAILABILITY_PAID_CONTENT = "AVAILABILITY_PAID_CONTENT";
field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
field public static final java.lang.String COLUMN_AUTHOR = "author";
+ field public static final java.lang.String COLUMN_AVAILABILITY = "availability";
field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
@@ -23868,7 +23881,12 @@
field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+ field public static final java.lang.String COLUMN_ITEM_COUNT = "item_count";
+ field public static final java.lang.String COLUMN_LIVE = "live";
+ field public static final java.lang.String COLUMN_LOGO = "logo";
field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+ field public static final java.lang.String COLUMN_OFFER_PRICE = "offer_price";
+ field public static final java.lang.String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio";
field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
field public static final java.lang.String COLUMN_PREVIEW_DURATION = "preview_duration";
field public static final java.lang.String COLUMN_PREVIEW_INTENT_URI = "preview_intent_uri";
@@ -23876,6 +23894,7 @@
field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri";
field public static final java.lang.String COLUMN_PREVIEW_WEIGHT = "preview_weight";
field public static final java.lang.String COLUMN_RECORDING_PROHIBITED = "recording_prohibited";
+ field public static final java.lang.String COLUMN_RELEASE_DATE = "release_date";
field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating";
field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
@@ -23883,12 +23902,16 @@
field public static final deprecated java.lang.String COLUMN_SEASON_NUMBER = "season_number";
field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title";
field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+ field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price";
field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+ field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio";
field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
field public static final java.lang.String COLUMN_TITLE = "title";
+ field public static final java.lang.String COLUMN_TYPE = "type";
field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+ field public static final java.lang.String COLUMN_WATCH_NEXT_TYPE = "watch_next_type";
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program";
field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/program";
field public static final android.net.Uri CONTENT_URI;
@@ -23901,7 +23924,22 @@
field public static final java.lang.String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS";
field public static final java.lang.String REVIEW_RATING_STYLE_PERCENTAGE = "REVIEW_RATING_STYLE_PERCENTAGE";
field public static final java.lang.String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS";
- field public static final java.lang.String REVIEW_RATING_STYLE_THUMPS_UP_DOWN = "REVIEW_RATING_STYLE_THUMPS_UP_DOWN";
+ field public static final java.lang.String REVIEW_RATING_STYLE_THUMBS_UP_DOWN = "REVIEW_RATING_STYLE_THUMBS_UP_DOWN";
+ field public static final java.lang.String TYPE_ALBUM = "TYPE_ALBUM";
+ field public static final java.lang.String TYPE_ARTIST = "TYPE_ARTIST";
+ field public static final java.lang.String TYPE_CHANNEL = "TYPE_CHANNEL";
+ field public static final java.lang.String TYPE_CLIP = "TYPE_CLIP";
+ field public static final java.lang.String TYPE_EVENT = "TYPE_EVENT";
+ field public static final java.lang.String TYPE_MOVIE = "TYPE_MOVIE";
+ field public static final java.lang.String TYPE_PLAYLIST = "TYPE_PLAYLIST";
+ field public static final java.lang.String TYPE_STATION = "TYPE_STATION";
+ field public static final java.lang.String TYPE_TRACK = "TYPE_TRACK";
+ field public static final java.lang.String TYPE_TV_EPISODE = "TYPE_TV_EPISODE";
+ field public static final java.lang.String TYPE_TV_SEASON = "TYPE_TV_SEASON";
+ field public static final java.lang.String TYPE_TV_SERIES = "TYPE_TV_SERIES";
+ field public static final java.lang.String WATCH_NEXT_TYPE_CONTINUE = "WATCH_NEXT_TYPE_CONTINUE";
+ field public static final java.lang.String WATCH_NEXT_TYPE_NEW = "WATCH_NEXT_TYPE_NEW";
+ field public static final java.lang.String WATCH_NEXT_TYPE_NEXT = "WATCH_NEXT_TYPE_NEXT";
}
public static final class TvContract.Programs.Genres {
@@ -25547,6 +25585,7 @@
method public java.security.cert.X509Certificate getCaCertificate();
method public java.security.cert.X509Certificate[] getCaCertificates();
method public java.security.cert.X509Certificate getClientCertificate();
+ method public java.security.cert.X509Certificate[] getClientCertificateChain();
method public java.lang.String getDomainSuffixMatch();
method public int getEapMethod();
method public java.lang.String getIdentity();
@@ -25560,6 +25599,7 @@
method public void setCaCertificate(java.security.cert.X509Certificate);
method public void setCaCertificates(java.security.cert.X509Certificate[]);
method public void setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate);
+ method public void setClientKeyEntryWithCertificateChain(java.security.PrivateKey, java.security.cert.X509Certificate[]);
method public void setDomainSuffixMatch(java.lang.String);
method public void setEapMethod(int);
method public void setIdentity(java.lang.String);
@@ -25635,6 +25675,7 @@
method public boolean isTdlsSupported();
method public boolean isWifiEnabled();
method public boolean pingSupplicant();
+ method public void queryPasspointIcon(long, java.lang.String);
method public boolean reassociate();
method public boolean reconnect();
method public boolean removeNetwork(int);
@@ -25645,6 +25686,10 @@
method public boolean startScan();
method public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback);
method public int updateNetwork(android.net.wifi.WifiConfiguration);
+ field public static final java.lang.String ACTION_PASSPOINT_DEAUTH_IMMINENT = "android.net.wifi.action.PASSPOINT_DEAUTH_IMMINENT";
+ field public static final java.lang.String ACTION_PASSPOINT_ICON = "android.net.wifi.action.PASSPOINT_ICON";
+ field public static final java.lang.String ACTION_PASSPOINT_OSU_PROVIDERS_LIST = "android.net.wifi.action.PASSPOINT_OSU_PROVIDERS_LIST";
+ field public static final java.lang.String ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION = "android.net.wifi.action.PASSPOINT_SUBSCRIPTION_REMEDIATION";
field public static final java.lang.String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
field public static final java.lang.String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE";
field public static final int ERROR_AUTHENTICATING = 1; // 0x1
@@ -25652,6 +25697,18 @@
field public static final java.lang.String EXTRA_NETWORK_INFO = "networkInfo";
field public static final java.lang.String EXTRA_NEW_RSSI = "newRssi";
field public static final java.lang.String EXTRA_NEW_STATE = "newState";
+ field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_BSSID = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_BSSID";
+ field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_ESS = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_ESS";
+ field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_REASON_URL = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_REASON_URL";
+ field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_REAUTH_DELAY = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_REAUTH_DELAY";
+ field public static final java.lang.String EXTRA_PASSPOINT_ICON_BSSID = "android.net.wifi.extra.PASSPOINT_ICON_BSSID";
+ field public static final java.lang.String EXTRA_PASSPOINT_ICON_DATA = "android.net.wifi.extra.PASSPOINT_ICON_DATA";
+ field public static final java.lang.String EXTRA_PASSPOINT_ICON_FILENAME = "android.net.wifi.extra.PASSPOINT_ICON_FILENAME";
+ field public static final java.lang.String EXTRA_PASSPOINT_OSU_PROVIDERS_LIST_BSSID = "android.net.wifi.extra.PASSPOINT_OSU_PROVIDERS_LIST_BSSID";
+ field public static final java.lang.String EXTRA_PASSPOINT_OSU_PROVIDERS_LIST_DATA = "android.net.wifi.extra.PASSPOINT_OSU_PROVIDERS_LIST_DATA";
+ field public static final java.lang.String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_BSSID = "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_BSSID";
+ field public static final java.lang.String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_METHOD = "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_METHOD";
+ field public static final java.lang.String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_URL = "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_URL";
field public static final java.lang.String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state";
field public static final java.lang.String EXTRA_RESULTS_UPDATED = "resultsUpdated";
field public static final java.lang.String EXTRA_SUPPLICANT_CONNECTED = "connected";
@@ -35717,7 +35774,7 @@
method public void onDisconnected();
method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback);
method public void onFillResponseAuthenticationRequest(android.os.Bundle, int);
- method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback);
+ method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
field public static final java.lang.String EXTRA_DATASET_EXTRAS = "android.service.autofill.extra.DATASET_EXTRAS";
field public static final java.lang.String EXTRA_RESPONSE_EXTRAS = "android.service.autofill.extra.RESPONSE_EXTRAS";
field public static final int FLAG_AUTHENTICATION_ERROR = 4; // 0x4
@@ -42519,6 +42576,7 @@
field public static final int FLAG_PRIVATE = 4; // 0x4
field public static final int FLAG_ROUND = 16; // 0x10
field public static final int FLAG_SECURE = 2; // 0x2
+ field public static final int FLAG_SHOW_WITH_INSECURE_LOCKSCREEN = 32; // 0x20
field public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1; // 0x1
field public static final int INVALID_DISPLAY = -1; // 0xffffffff
field public static final int STATE_DOZE = 3; // 0x3
@@ -44431,8 +44489,8 @@
field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
- field public static final int AUTO_FILL_FLAG_TYPE_FILL = 1; // 0x1
- field public static final int AUTO_FILL_FLAG_TYPE_SAVE = 2; // 0x2
+ field public static final int AUTO_FILL_FLAG_TYPE_FILL = 268435456; // 0x10000000
+ field public static final int AUTO_FILL_FLAG_TYPE_SAVE = 536870912; // 0x20000000
field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100
field public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = 64; // 0x40
field public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 128; // 0x80
diff --git a/api/system-current.txt b/api/system-current.txt
index 84efdf7..29179cf 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5360,7 +5360,8 @@
}
public static class Notification.Builder {
- ctor public Notification.Builder(android.content.Context);
+ ctor public Notification.Builder(android.content.Context, java.lang.String);
+ ctor public deprecated Notification.Builder(android.content.Context);
method public deprecated android.app.Notification.Builder addAction(int, java.lang.CharSequence, android.app.PendingIntent);
method public android.app.Notification.Builder addAction(android.app.Notification.Action);
method public android.app.Notification.Builder addExtras(android.os.Bundle);
@@ -7265,6 +7266,7 @@
method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProviders();
method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProvidersForProfile(android.os.UserHandle);
method public static android.appwidget.AppWidgetManager getInstance(android.content.Context);
+ method public boolean isRequestPinAppWidgetSupported();
method public void notifyAppWidgetViewDataChanged(int[], int);
method public void notifyAppWidgetViewDataChanged(int, int);
method public void partiallyUpdateAppWidget(int[], android.widget.RemoteViews);
@@ -10208,6 +10210,7 @@
field public int theme;
field public int uiOptions;
field public int uid;
+ field public java.lang.String volumeUuid;
}
public static class ApplicationInfo.DisplayNameComparator implements java.util.Comparator {
@@ -10380,7 +10383,8 @@
method public void startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
method public void startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle);
method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
- field public static final java.lang.String ACTION_CONFIRM_PIN_ITEM = "android.content.pm.action.CONFIRM_PIN_ITEM";
+ field public static final java.lang.String ACTION_CONFIRM_PIN_APPWIDGET = "android.content.pm.action.CONFIRM_PIN_APPWIDGET";
+ field public static final java.lang.String ACTION_CONFIRM_PIN_SHORTCUT = "android.content.pm.action.CONFIRM_PIN_SHORTCUT";
field public static final java.lang.String EXTRA_PIN_ITEM_REQUEST = "android.content.pm.extra.PIN_ITEM_REQUEST";
}
@@ -15819,6 +15823,7 @@
field public static final int VIRTUAL_DISPLAY_FLAG_PRESENTATION = 2; // 0x2
field public static final int VIRTUAL_DISPLAY_FLAG_PUBLIC = 1; // 0x1
field public static final int VIRTUAL_DISPLAY_FLAG_SECURE = 4; // 0x4
+ field public static final int VIRTUAL_DISPLAY_FLAG_SHOW_WITH_INSECURE_LOCKSCREEN = 32; // 0x20
}
public static abstract interface DisplayManager.DisplayListener {
@@ -25597,8 +25602,16 @@
}
public static final class TvContract.Programs implements android.media.tv.TvContract.BaseTvColumns {
+ field public static final java.lang.String ASPECT_RATIO_16_9 = "ASPECT_RATIO_16_9";
+ field public static final java.lang.String ASPECT_RATIO_1_1 = "ASPECT_RATIO_1_1";
+ field public static final java.lang.String ASPECT_RATIO_2_3 = "ASPECT_RATIO_2_3";
+ field public static final java.lang.String ASPECT_RATIO_3_2 = "ASPECT_RATIO_3_2";
+ field public static final java.lang.String AVAILABILITY_AVAILABLE = "AVAILABILITY_AVAILABLE";
+ field public static final java.lang.String AVAILABILITY_FREE_WITH_SUBSCRIPTION = "AVAILABILITY_FREE_WITH_SUBSCRIPTION";
+ field public static final java.lang.String AVAILABILITY_PAID_CONTENT = "AVAILABILITY_PAID_CONTENT";
field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
field public static final java.lang.String COLUMN_AUTHOR = "author";
+ field public static final java.lang.String COLUMN_AVAILABILITY = "availability";
field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
@@ -25615,7 +25628,12 @@
field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+ field public static final java.lang.String COLUMN_ITEM_COUNT = "item_count";
+ field public static final java.lang.String COLUMN_LIVE = "live";
+ field public static final java.lang.String COLUMN_LOGO = "logo";
field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+ field public static final java.lang.String COLUMN_OFFER_PRICE = "offer_price";
+ field public static final java.lang.String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio";
field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
field public static final java.lang.String COLUMN_PREVIEW_DURATION = "preview_duration";
field public static final java.lang.String COLUMN_PREVIEW_INTENT_URI = "preview_intent_uri";
@@ -25623,6 +25641,7 @@
field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri";
field public static final java.lang.String COLUMN_PREVIEW_WEIGHT = "preview_weight";
field public static final java.lang.String COLUMN_RECORDING_PROHIBITED = "recording_prohibited";
+ field public static final java.lang.String COLUMN_RELEASE_DATE = "release_date";
field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating";
field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
@@ -25630,13 +25649,17 @@
field public static final deprecated java.lang.String COLUMN_SEASON_NUMBER = "season_number";
field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title";
field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+ field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price";
field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+ field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio";
field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
field public static final java.lang.String COLUMN_TITLE = "title";
field public static final java.lang.String COLUMN_TRANSIENT = "transient";
+ field public static final java.lang.String COLUMN_TYPE = "type";
field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+ field public static final java.lang.String COLUMN_WATCH_NEXT_TYPE = "watch_next_type";
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program";
field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/program";
field public static final android.net.Uri CONTENT_URI;
@@ -25649,7 +25672,22 @@
field public static final java.lang.String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS";
field public static final java.lang.String REVIEW_RATING_STYLE_PERCENTAGE = "REVIEW_RATING_STYLE_PERCENTAGE";
field public static final java.lang.String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS";
- field public static final java.lang.String REVIEW_RATING_STYLE_THUMPS_UP_DOWN = "REVIEW_RATING_STYLE_THUMPS_UP_DOWN";
+ field public static final java.lang.String REVIEW_RATING_STYLE_THUMBS_UP_DOWN = "REVIEW_RATING_STYLE_THUMBS_UP_DOWN";
+ field public static final java.lang.String TYPE_ALBUM = "TYPE_ALBUM";
+ field public static final java.lang.String TYPE_ARTIST = "TYPE_ARTIST";
+ field public static final java.lang.String TYPE_CHANNEL = "TYPE_CHANNEL";
+ field public static final java.lang.String TYPE_CLIP = "TYPE_CLIP";
+ field public static final java.lang.String TYPE_EVENT = "TYPE_EVENT";
+ field public static final java.lang.String TYPE_MOVIE = "TYPE_MOVIE";
+ field public static final java.lang.String TYPE_PLAYLIST = "TYPE_PLAYLIST";
+ field public static final java.lang.String TYPE_STATION = "TYPE_STATION";
+ field public static final java.lang.String TYPE_TRACK = "TYPE_TRACK";
+ field public static final java.lang.String TYPE_TV_EPISODE = "TYPE_TV_EPISODE";
+ field public static final java.lang.String TYPE_TV_SEASON = "TYPE_TV_SEASON";
+ field public static final java.lang.String TYPE_TV_SERIES = "TYPE_TV_SERIES";
+ field public static final java.lang.String WATCH_NEXT_TYPE_CONTINUE = "WATCH_NEXT_TYPE_CONTINUE";
+ field public static final java.lang.String WATCH_NEXT_TYPE_NEW = "WATCH_NEXT_TYPE_NEW";
+ field public static final java.lang.String WATCH_NEXT_TYPE_NEXT = "WATCH_NEXT_TYPE_NEXT";
}
public static final class TvContract.Programs.Genres {
@@ -26108,6 +26146,7 @@
ctor public LogMaker(int);
ctor public LogMaker(java.lang.Object[]);
method public android.metrics.LogMaker addTaggedData(int, java.lang.Object);
+ method public android.metrics.LogMaker clearTaggedData(int);
method public void deserialize(java.lang.Object[]);
method public int getCategory();
method public long getCounterBucket();
@@ -28056,6 +28095,7 @@
method public java.security.cert.X509Certificate getCaCertificate();
method public java.security.cert.X509Certificate[] getCaCertificates();
method public java.security.cert.X509Certificate getClientCertificate();
+ method public java.security.cert.X509Certificate[] getClientCertificateChain();
method public java.lang.String getDomainSuffixMatch();
method public int getEapMethod();
method public java.lang.String getIdentity();
@@ -28069,6 +28109,7 @@
method public void setCaCertificate(java.security.cert.X509Certificate);
method public void setCaCertificates(java.security.cert.X509Certificate[]);
method public void setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate);
+ method public void setClientKeyEntryWithCertificateChain(java.security.PrivateKey, java.security.cert.X509Certificate[]);
method public void setDomainSuffixMatch(java.lang.String);
method public void setEapMethod(int);
method public void setIdentity(java.lang.String);
@@ -28155,6 +28196,7 @@
method public boolean isWifiEnabled();
method public boolean isWifiScannerSupported();
method public boolean pingSupplicant();
+ method public void queryPasspointIcon(long, java.lang.String);
method public boolean reassociate();
method public boolean reconnect();
method public boolean removeNetwork(int);
@@ -28169,6 +28211,10 @@
method public boolean startScan(android.os.WorkSource);
method public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback);
method public int updateNetwork(android.net.wifi.WifiConfiguration);
+ field public static final java.lang.String ACTION_PASSPOINT_DEAUTH_IMMINENT = "android.net.wifi.action.PASSPOINT_DEAUTH_IMMINENT";
+ field public static final java.lang.String ACTION_PASSPOINT_ICON = "android.net.wifi.action.PASSPOINT_ICON";
+ field public static final java.lang.String ACTION_PASSPOINT_OSU_PROVIDERS_LIST = "android.net.wifi.action.PASSPOINT_OSU_PROVIDERS_LIST";
+ field public static final java.lang.String ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION = "android.net.wifi.action.PASSPOINT_SUBSCRIPTION_REMEDIATION";
field public static final java.lang.String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
field public static final java.lang.String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE";
field public static final int CHANGE_REASON_ADDED = 0; // 0x0
@@ -28182,6 +28228,18 @@
field public static final java.lang.String EXTRA_NETWORK_INFO = "networkInfo";
field public static final java.lang.String EXTRA_NEW_RSSI = "newRssi";
field public static final java.lang.String EXTRA_NEW_STATE = "newState";
+ field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_BSSID = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_BSSID";
+ field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_ESS = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_ESS";
+ field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_REASON_URL = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_REASON_URL";
+ field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_REAUTH_DELAY = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_REAUTH_DELAY";
+ field public static final java.lang.String EXTRA_PASSPOINT_ICON_BSSID = "android.net.wifi.extra.PASSPOINT_ICON_BSSID";
+ field public static final java.lang.String EXTRA_PASSPOINT_ICON_DATA = "android.net.wifi.extra.PASSPOINT_ICON_DATA";
+ field public static final java.lang.String EXTRA_PASSPOINT_ICON_FILENAME = "android.net.wifi.extra.PASSPOINT_ICON_FILENAME";
+ field public static final java.lang.String EXTRA_PASSPOINT_OSU_PROVIDERS_LIST_BSSID = "android.net.wifi.extra.PASSPOINT_OSU_PROVIDERS_LIST_BSSID";
+ field public static final java.lang.String EXTRA_PASSPOINT_OSU_PROVIDERS_LIST_DATA = "android.net.wifi.extra.PASSPOINT_OSU_PROVIDERS_LIST_DATA";
+ field public static final java.lang.String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_BSSID = "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_BSSID";
+ field public static final java.lang.String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_METHOD = "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_METHOD";
+ field public static final java.lang.String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_URL = "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_URL";
field public static final java.lang.String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
field public static final java.lang.String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state";
field public static final java.lang.String EXTRA_RESULTS_UPDATED = "resultsUpdated";
@@ -36592,6 +36650,7 @@
field public static final java.lang.String ACTION_APPLICATION_DETAILS_SETTINGS = "android.settings.APPLICATION_DETAILS_SETTINGS";
field public static final java.lang.String ACTION_APPLICATION_DEVELOPMENT_SETTINGS = "android.settings.APPLICATION_DEVELOPMENT_SETTINGS";
field public static final java.lang.String ACTION_APPLICATION_SETTINGS = "android.settings.APPLICATION_SETTINGS";
+ field public static final java.lang.String ACTION_APP_NOTIFICATION_SETTINGS = "android.settings.APP_NOTIFICATION_SETTINGS";
field public static final java.lang.String ACTION_BATTERY_SAVER_SETTINGS = "android.settings.BATTERY_SAVER_SETTINGS";
field public static final java.lang.String ACTION_BLUETOOTH_SETTINGS = "android.settings.BLUETOOTH_SETTINGS";
field public static final java.lang.String ACTION_CAPTIONING_SETTINGS = "android.settings.CAPTIONING_SETTINGS";
@@ -38741,7 +38800,7 @@
method public void onDisconnected();
method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback);
method public void onFillResponseAuthenticationRequest(android.os.Bundle, int);
- method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback);
+ method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
field public static final java.lang.String EXTRA_DATASET_EXTRAS = "android.service.autofill.extra.DATASET_EXTRAS";
field public static final java.lang.String EXTRA_RESPONSE_EXTRAS = "android.service.autofill.extra.RESPONSE_EXTRAS";
field public static final int FLAG_AUTHENTICATION_ERROR = 4; // 0x4
@@ -45918,6 +45977,7 @@
field public static final int FLAG_PRIVATE = 4; // 0x4
field public static final int FLAG_ROUND = 16; // 0x10
field public static final int FLAG_SECURE = 2; // 0x2
+ field public static final int FLAG_SHOW_WITH_INSECURE_LOCKSCREEN = 32; // 0x20
field public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1; // 0x1
field public static final int INVALID_DISPLAY = -1; // 0xffffffff
field public static final int STATE_DOZE = 3; // 0x3
@@ -47830,8 +47890,8 @@
field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
- field public static final int AUTO_FILL_FLAG_TYPE_FILL = 1; // 0x1
- field public static final int AUTO_FILL_FLAG_TYPE_SAVE = 2; // 0x2
+ field public static final int AUTO_FILL_FLAG_TYPE_FILL = 268435456; // 0x10000000
+ field public static final int AUTO_FILL_FLAG_TYPE_SAVE = 536870912; // 0x20000000
field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100
field public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = 64; // 0x40
field public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 128; // 0x80
diff --git a/api/test-current.txt b/api/test-current.txt
index 28b9af1..334f4e6 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5207,7 +5207,8 @@
}
public static class Notification.Builder {
- ctor public Notification.Builder(android.content.Context);
+ ctor public Notification.Builder(android.content.Context, java.lang.String);
+ ctor public deprecated Notification.Builder(android.content.Context);
method public deprecated android.app.Notification.Builder addAction(int, java.lang.CharSequence, android.app.PendingIntent);
method public android.app.Notification.Builder addAction(android.app.Notification.Action);
method public android.app.Notification.Builder addExtras(android.os.Bundle);
@@ -6933,6 +6934,7 @@
method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProviders();
method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProvidersForProfile(android.os.UserHandle);
method public static android.appwidget.AppWidgetManager getInstance(android.content.Context);
+ method public boolean isRequestPinAppWidgetSupported();
method public void notifyAppWidgetViewDataChanged(int[], int);
method public void notifyAppWidgetViewDataChanged(int, int);
method public void partiallyUpdateAppWidget(int[], android.widget.RemoteViews);
@@ -9817,6 +9819,7 @@
field public int theme;
field public int uiOptions;
field public int uid;
+ field public java.lang.String volumeUuid;
}
public static class ApplicationInfo.DisplayNameComparator implements java.util.Comparator {
@@ -9946,7 +9949,8 @@
method public void startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
method public void startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle);
method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
- field public static final java.lang.String ACTION_CONFIRM_PIN_ITEM = "android.content.pm.action.CONFIRM_PIN_ITEM";
+ field public static final java.lang.String ACTION_CONFIRM_PIN_APPWIDGET = "android.content.pm.action.CONFIRM_PIN_APPWIDGET";
+ field public static final java.lang.String ACTION_CONFIRM_PIN_SHORTCUT = "android.content.pm.action.CONFIRM_PIN_SHORTCUT";
field public static final java.lang.String EXTRA_PIN_ITEM_REQUEST = "android.content.pm.extra.PIN_ITEM_REQUEST";
}
@@ -15275,6 +15279,7 @@
field public static final int VIRTUAL_DISPLAY_FLAG_PRESENTATION = 2; // 0x2
field public static final int VIRTUAL_DISPLAY_FLAG_PUBLIC = 1; // 0x1
field public static final int VIRTUAL_DISPLAY_FLAG_SECURE = 4; // 0x4
+ field public static final int VIRTUAL_DISPLAY_FLAG_SHOW_WITH_INSECURE_LOCKSCREEN = 32; // 0x20
}
public static abstract interface DisplayManager.DisplayListener {
@@ -23941,8 +23946,16 @@
}
public static final class TvContract.Programs implements android.media.tv.TvContract.BaseTvColumns {
+ field public static final java.lang.String ASPECT_RATIO_16_9 = "ASPECT_RATIO_16_9";
+ field public static final java.lang.String ASPECT_RATIO_1_1 = "ASPECT_RATIO_1_1";
+ field public static final java.lang.String ASPECT_RATIO_2_3 = "ASPECT_RATIO_2_3";
+ field public static final java.lang.String ASPECT_RATIO_3_2 = "ASPECT_RATIO_3_2";
+ field public static final java.lang.String AVAILABILITY_AVAILABLE = "AVAILABILITY_AVAILABLE";
+ field public static final java.lang.String AVAILABILITY_FREE_WITH_SUBSCRIPTION = "AVAILABILITY_FREE_WITH_SUBSCRIPTION";
+ field public static final java.lang.String AVAILABILITY_PAID_CONTENT = "AVAILABILITY_PAID_CONTENT";
field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
field public static final java.lang.String COLUMN_AUTHOR = "author";
+ field public static final java.lang.String COLUMN_AVAILABILITY = "availability";
field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
@@ -23959,7 +23972,12 @@
field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+ field public static final java.lang.String COLUMN_ITEM_COUNT = "item_count";
+ field public static final java.lang.String COLUMN_LIVE = "live";
+ field public static final java.lang.String COLUMN_LOGO = "logo";
field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+ field public static final java.lang.String COLUMN_OFFER_PRICE = "offer_price";
+ field public static final java.lang.String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio";
field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
field public static final java.lang.String COLUMN_PREVIEW_DURATION = "preview_duration";
field public static final java.lang.String COLUMN_PREVIEW_INTENT_URI = "preview_intent_uri";
@@ -23967,6 +23985,7 @@
field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri";
field public static final java.lang.String COLUMN_PREVIEW_WEIGHT = "preview_weight";
field public static final java.lang.String COLUMN_RECORDING_PROHIBITED = "recording_prohibited";
+ field public static final java.lang.String COLUMN_RELEASE_DATE = "release_date";
field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating";
field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
@@ -23974,12 +23993,16 @@
field public static final deprecated java.lang.String COLUMN_SEASON_NUMBER = "season_number";
field public static final java.lang.String COLUMN_SEASON_TITLE = "season_title";
field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+ field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price";
field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+ field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio";
field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
field public static final java.lang.String COLUMN_TITLE = "title";
+ field public static final java.lang.String COLUMN_TYPE = "type";
field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+ field public static final java.lang.String COLUMN_WATCH_NEXT_TYPE = "watch_next_type";
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program";
field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/program";
field public static final android.net.Uri CONTENT_URI;
@@ -23992,7 +24015,22 @@
field public static final java.lang.String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS";
field public static final java.lang.String REVIEW_RATING_STYLE_PERCENTAGE = "REVIEW_RATING_STYLE_PERCENTAGE";
field public static final java.lang.String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS";
- field public static final java.lang.String REVIEW_RATING_STYLE_THUMPS_UP_DOWN = "REVIEW_RATING_STYLE_THUMPS_UP_DOWN";
+ field public static final java.lang.String REVIEW_RATING_STYLE_THUMBS_UP_DOWN = "REVIEW_RATING_STYLE_THUMBS_UP_DOWN";
+ field public static final java.lang.String TYPE_ALBUM = "TYPE_ALBUM";
+ field public static final java.lang.String TYPE_ARTIST = "TYPE_ARTIST";
+ field public static final java.lang.String TYPE_CHANNEL = "TYPE_CHANNEL";
+ field public static final java.lang.String TYPE_CLIP = "TYPE_CLIP";
+ field public static final java.lang.String TYPE_EVENT = "TYPE_EVENT";
+ field public static final java.lang.String TYPE_MOVIE = "TYPE_MOVIE";
+ field public static final java.lang.String TYPE_PLAYLIST = "TYPE_PLAYLIST";
+ field public static final java.lang.String TYPE_STATION = "TYPE_STATION";
+ field public static final java.lang.String TYPE_TRACK = "TYPE_TRACK";
+ field public static final java.lang.String TYPE_TV_EPISODE = "TYPE_TV_EPISODE";
+ field public static final java.lang.String TYPE_TV_SEASON = "TYPE_TV_SEASON";
+ field public static final java.lang.String TYPE_TV_SERIES = "TYPE_TV_SERIES";
+ field public static final java.lang.String WATCH_NEXT_TYPE_CONTINUE = "WATCH_NEXT_TYPE_CONTINUE";
+ field public static final java.lang.String WATCH_NEXT_TYPE_NEW = "WATCH_NEXT_TYPE_NEW";
+ field public static final java.lang.String WATCH_NEXT_TYPE_NEXT = "WATCH_NEXT_TYPE_NEXT";
}
public static final class TvContract.Programs.Genres {
@@ -25638,6 +25676,7 @@
method public java.security.cert.X509Certificate getCaCertificate();
method public java.security.cert.X509Certificate[] getCaCertificates();
method public java.security.cert.X509Certificate getClientCertificate();
+ method public java.security.cert.X509Certificate[] getClientCertificateChain();
method public java.lang.String getDomainSuffixMatch();
method public int getEapMethod();
method public java.lang.String getIdentity();
@@ -25651,6 +25690,7 @@
method public void setCaCertificate(java.security.cert.X509Certificate);
method public void setCaCertificates(java.security.cert.X509Certificate[]);
method public void setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate);
+ method public void setClientKeyEntryWithCertificateChain(java.security.PrivateKey, java.security.cert.X509Certificate[]);
method public void setDomainSuffixMatch(java.lang.String);
method public void setEapMethod(int);
method public void setIdentity(java.lang.String);
@@ -25726,6 +25766,7 @@
method public boolean isTdlsSupported();
method public boolean isWifiEnabled();
method public boolean pingSupplicant();
+ method public void queryPasspointIcon(long, java.lang.String);
method public boolean reassociate();
method public boolean reconnect();
method public boolean removeNetwork(int);
@@ -25736,6 +25777,10 @@
method public boolean startScan();
method public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback);
method public int updateNetwork(android.net.wifi.WifiConfiguration);
+ field public static final java.lang.String ACTION_PASSPOINT_DEAUTH_IMMINENT = "android.net.wifi.action.PASSPOINT_DEAUTH_IMMINENT";
+ field public static final java.lang.String ACTION_PASSPOINT_ICON = "android.net.wifi.action.PASSPOINT_ICON";
+ field public static final java.lang.String ACTION_PASSPOINT_OSU_PROVIDERS_LIST = "android.net.wifi.action.PASSPOINT_OSU_PROVIDERS_LIST";
+ field public static final java.lang.String ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION = "android.net.wifi.action.PASSPOINT_SUBSCRIPTION_REMEDIATION";
field public static final java.lang.String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
field public static final java.lang.String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE";
field public static final int ERROR_AUTHENTICATING = 1; // 0x1
@@ -25743,6 +25788,18 @@
field public static final java.lang.String EXTRA_NETWORK_INFO = "networkInfo";
field public static final java.lang.String EXTRA_NEW_RSSI = "newRssi";
field public static final java.lang.String EXTRA_NEW_STATE = "newState";
+ field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_BSSID = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_BSSID";
+ field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_ESS = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_ESS";
+ field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_REASON_URL = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_REASON_URL";
+ field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_REAUTH_DELAY = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_REAUTH_DELAY";
+ field public static final java.lang.String EXTRA_PASSPOINT_ICON_BSSID = "android.net.wifi.extra.PASSPOINT_ICON_BSSID";
+ field public static final java.lang.String EXTRA_PASSPOINT_ICON_DATA = "android.net.wifi.extra.PASSPOINT_ICON_DATA";
+ field public static final java.lang.String EXTRA_PASSPOINT_ICON_FILENAME = "android.net.wifi.extra.PASSPOINT_ICON_FILENAME";
+ field public static final java.lang.String EXTRA_PASSPOINT_OSU_PROVIDERS_LIST_BSSID = "android.net.wifi.extra.PASSPOINT_OSU_PROVIDERS_LIST_BSSID";
+ field public static final java.lang.String EXTRA_PASSPOINT_OSU_PROVIDERS_LIST_DATA = "android.net.wifi.extra.PASSPOINT_OSU_PROVIDERS_LIST_DATA";
+ field public static final java.lang.String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_BSSID = "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_BSSID";
+ field public static final java.lang.String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_METHOD = "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_METHOD";
+ field public static final java.lang.String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_URL = "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_URL";
field public static final java.lang.String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state";
field public static final java.lang.String EXTRA_RESULTS_UPDATED = "resultsUpdated";
field public static final java.lang.String EXTRA_SUPPLICANT_CONNECTED = "connected";
@@ -35851,7 +35908,7 @@
method public void onDisconnected();
method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback);
method public void onFillResponseAuthenticationRequest(android.os.Bundle, int);
- method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback);
+ method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
field public static final java.lang.String EXTRA_DATASET_EXTRAS = "android.service.autofill.extra.DATASET_EXTRAS";
field public static final java.lang.String EXTRA_RESPONSE_EXTRAS = "android.service.autofill.extra.RESPONSE_EXTRAS";
field public static final int FLAG_AUTHENTICATION_ERROR = 4; // 0x4
@@ -42821,6 +42878,7 @@
field public static final int FLAG_PRIVATE = 4; // 0x4
field public static final int FLAG_ROUND = 16; // 0x10
field public static final int FLAG_SECURE = 2; // 0x2
+ field public static final int FLAG_SHOW_WITH_INSECURE_LOCKSCREEN = 32; // 0x20
field public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1; // 0x1
field public static final int INVALID_DISPLAY = -1; // 0xffffffff
field public static final int STATE_DOZE = 3; // 0x3
@@ -44736,8 +44794,8 @@
field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
- field public static final int AUTO_FILL_FLAG_TYPE_FILL = 1; // 0x1
- field public static final int AUTO_FILL_FLAG_TYPE_SAVE = 2; // 0x2
+ field public static final int AUTO_FILL_FLAG_TYPE_FILL = 268435456; // 0x10000000
+ field public static final int AUTO_FILL_FLAG_TYPE_SAVE = 536870912; // 0x20000000
field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100
field public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = 64; // 0x40
field public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 128; // 0x80
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 634dc1fd..08ad976 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -469,8 +469,9 @@
*/
@Override
public boolean doAnimationFrame(long frameTime) {
- // TODO: Need to find a better signal than this
- return getDuration() + getStartDelay() >= frameTime;
+ // TODO: Need to find a better signal than this. There's a bug in SystemUI that's preventing
+ // returning !isStarted() from working.
+ return false;
}
/**
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index bca736a25..8aba405 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -174,6 +174,19 @@
*/
private long mPauseTime = -1;
+ // This is to work around a bug in b/34736819. This needs to be removed once app team
+ // fixes their side.
+ private AnimatorListenerAdapter mDummyListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mNodeMap.get(animation) == null) {
+ throw new AndroidRuntimeException("Error: animation ended is not in the node map");
+ }
+ mNodeMap.get(animation).mEnded = true;
+
+ }
+ };
+
public AnimatorSet() {
super();
mNodeMap.put(mDelayAnim, mRootNode);
@@ -1018,6 +1031,8 @@
}
private void startAnimation() {
+ addDummyListener();
+
// Register animation callback
addAnimationCallback(mStartDelay);
@@ -1062,6 +1077,20 @@
}
}
+ // This is to work around the issue in b/34736819, as the old behavior in AnimatorSet had
+ // masked a real bug in play movies. TODO: remove this and below once the root cause is fixed.
+ private void addDummyListener() {
+ for (int i = 1; i < mNodes.size(); i++) {
+ mNodes.get(i).mAnimation.addListener(mDummyListener);
+ }
+ }
+
+ private void removeDummyListener() {
+ for (int i = 1; i < mNodes.size(); i++) {
+ mNodes.get(i).mAnimation.removeListener(mDummyListener);
+ }
+ }
+
private int findLatestEventIdForTime(long currentPlayTime) {
int size = mEvents.size();
int latestId = mLastEventId;
@@ -1107,6 +1136,7 @@
tmpListeners.get(i).onAnimationEnd(this, mReversing);
}
}
+ removeDummyListener();
mSelfPulse = true;
mReversing = false;
}
@@ -1151,6 +1181,17 @@
anim.mNodeMap = new ArrayMap<Animator, Node>();
anim.mNodes = new ArrayList<Node>(nodeCount);
anim.mEvents = new ArrayList<AnimationEvent>();
+ anim.mDummyListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (anim.mNodeMap.get(animation) == null) {
+ throw new AndroidRuntimeException("Error: animation ended is not in the node"
+ + " map");
+ }
+ anim.mNodeMap.get(animation).mEnded = true;
+
+ }
+ };
anim.mReversing = false;
anim.mDependencyDirty = true;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 580bb50..1d84ff5 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -117,9 +117,8 @@
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
import android.view.autofill.AutoFillId;
-import android.view.autofill.Dataset;
-import android.view.autofill.DatasetField;
-import android.view.autofill.VirtualViewDelegate;
+import android.view.autofill.AutoFillManager;
+import android.view.autofill.AutoFillSession;
import android.widget.AdapterView;
import android.widget.Toast;
import android.widget.Toolbar;
@@ -848,10 +847,7 @@
private boolean mHasCurrentPermissionsRequest;
@GuardedBy("this")
- private WeakReference<IAutoFillAppCallback> mAutoFillCallback;
-
- @GuardedBy("this")
- private VirtualViewDelegate.Callback mAutoFillDelegateCallback;
+ private AutoFillSession mAutoFillSession;
private static native String getDlWarning();
@@ -1704,76 +1700,17 @@
}
/**
- * Lazily sets the {@link #mAutoFillDelegateCallback}.
- */
- private void setAutoFillDelegateCallback() {
- synchronized (this) {
- if (mAutoFillDelegateCallback == null) {
- mAutoFillDelegateCallback = new VirtualViewDelegate.Callback() {
- // TODO(b/33197203): implement
- };
- }
- }
- }
-
- /**
* Lazily gets the {@link IAutoFillAppCallback} for this activitity.
*
* <p>This callback is used by the {@link AutoFillService} app to auto-fill the activity fields.
*/
- WeakReference<IAutoFillAppCallback> getAutoFillCallback() {
+ IAutoFillAppCallback getAutoFillCallback() {
synchronized (this) {
- if (mAutoFillCallback == null) {
- final IAutoFillAppCallback cb = new IAutoFillAppCallback.Stub() {
- @Override
- public void autoFill(Dataset dataset) throws RemoteException {
- // TODO(b/33197203): must keep the dataset so subsequent calls pass the same
- // dataset.extras to service
- runOnUiThread(() -> {
- final View root = getWindow().getDecorView().getRootView();
- for (DatasetField field : dataset.getFields()) {
- final AutoFillId id = field.getId();
- if (id == null) {
- Log.w(TAG, "autoFill(): null id on " + field);
- continue;
- }
- final int viewId = id.getViewId();
- final View view = root.findViewByAccessibilityIdTraversal(viewId);
- if (view == null) {
- Log.w(TAG, "autoFill(): no View with id " + viewId);
- continue;
- }
-
- // TODO(b/33197203): handle protected value (like credit card)
- if (id.isVirtual()) {
- // Delegate virtual fields to provider.
- setAutoFillDelegateCallback();
- final VirtualViewDelegate mgr = view
- .getAutoFillVirtualViewDelegate(
- mAutoFillDelegateCallback);
- if (mgr == null) {
- Log.w(TAG, "autoFill(): cannot fill virtual " + id
- + "; no auto-fill provider for view "
- + view.getClass());
- continue;
- }
- if (DEBUG_AUTO_FILL) {
- Log.d(TAG, "autoFill(): delegating " + id
- + " to virtual manager " + mgr);
- }
- mgr.autoFill(id.getVirtualChildId(), field.getValue());
- } else {
- // Handle non-virtual fields itself.
- view.autoFill(field.getValue());
- }
- }
- });
- }
- };
- mAutoFillCallback = new WeakReference<IAutoFillAppCallback>(cb);
+ if (mAutoFillSession == null) {
+ mAutoFillSession = new AutoFillSession(this);
}
+ return mAutoFillSession.getCallback();
}
- return mAutoFillCallback;
}
/**
@@ -6067,9 +6004,9 @@
getWindow().peekDecorView().getViewRootImpl().dump(prefix, fd, writer, args);
}
- if (mAutoFillCallback != null) {
- writer.print(prefix); writer.print("mAutoFillCallback: " );
- writer.println(mAutoFillCallback.get());
+ if (mAutoFillSession!= null) {
+ writer.print(prefix); writer.print("mAutoFillSession: " );
+ writer.println(mAutoFillSession);
}
mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix);
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 89510d9..e848080 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -154,6 +154,12 @@
public abstract List<IBinder> getTopVisibleActivities();
/**
+ * Returns the top, focused activity of the currently visible stack, but only if it belongs to
+ * the given UID.
+ */
+ public abstract IBinder getTopVisibleActivity(int uid);
+
+ /**
* Callback for window manager to let activity manager know that docked stack changes its
* minimized state.
*/
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 34eaa0b..cf20b68 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -541,6 +541,7 @@
ParcelFileDescriptor profileFd;
int samplingInterval;
boolean autoStopProfiler;
+ boolean streamingOutput;
boolean profiling;
boolean handlingProfiling;
public void setProfiler(ProfilerInfo profilerInfo) {
@@ -566,6 +567,7 @@
profileFd = fd;
samplingInterval = profilerInfo.samplingInterval;
autoStopProfiler = profilerInfo.autoStopProfiler;
+ streamingOutput = profilerInfo.streamingOutput;
}
public void startProfiling() {
if (profileFd == null || profiling) {
@@ -574,7 +576,8 @@
try {
int bufferSize = SystemProperties.getInt("debug.traceview-buffer-size-mb", 8);
VMDebug.startMethodTracing(profileFile, profileFd.getFileDescriptor(),
- bufferSize * 1024 * 1024, 0, samplingInterval != 0, samplingInterval);
+ bufferSize * 1024 * 1024, 0, samplingInterval != 0, samplingInterval,
+ streamingOutput);
profiling = true;
} catch (RuntimeException e) {
Slog.w(TAG, "Profiling failed on path " + profileFile);
@@ -2964,7 +2967,7 @@
if (!forAutoFill) {
r.activity.onProvideAssistContent(content);
} else if (addAutoFillCallback) {
- IAutoFillAppCallback cb = r.activity.getAutoFillCallback().get();
+ IAutoFillAppCallback cb = r.activity.getAutoFillCallback();
if (cb != null) {
data.putBinder(AutoFillService.KEY_CALLBACK, cb.asBinder());
} else {
@@ -5029,7 +5032,9 @@
* @hide
*/
public void stopProfiling() {
- mProfiler.stopProfiling();
+ if (mProfiler != null) {
+ mProfiler.stopProfiling();
+ }
}
static final void handleDumpHeap(boolean managed, DumpHeapData dhd) {
@@ -5275,6 +5280,7 @@
mProfiler.profileFd = data.initProfilerInfo.profileFd;
mProfiler.samplingInterval = data.initProfilerInfo.samplingInterval;
mProfiler.autoStopProfiler = data.initProfilerInfo.autoStopProfiler;
+ mProfiler.streamingOutput = data.initProfilerInfo.streamingOutput;
}
// send up app name; do this *before* waiting for debugger
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 73b96f1..612998d 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -762,6 +762,24 @@
* are going to call back with {@link #onActivityResult(int, int, Intent)}.
*/
public void setTargetFragment(Fragment fragment, int requestCode) {
+ // Don't allow a caller to set a target fragment in another FragmentManager,
+ // but there's a snag: people do set target fragments before fragments get added.
+ // We'll have the FragmentManager check that for validity when we move
+ // the fragments to a valid state.
+ final FragmentManager mine = getFragmentManager();
+ final FragmentManager theirs = fragment.getFragmentManager();
+ if (mine != null && theirs != null && mine != theirs) {
+ throw new IllegalArgumentException("Fragment " + fragment
+ + " must share the same FragmentManager to be set as a target fragment");
+ }
+
+ // Don't let someone create a cycle.
+ for (Fragment check = fragment; check != null; check = check.getTargetFragment()) {
+ if (check == this) {
+ throw new IllegalArgumentException("Setting " + fragment + " as the target of "
+ + this + " would create a target cycle");
+ }
+ }
mTarget = fragment;
mTargetRequestCode = requestCode;
}
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 44f1322..32cf1c3 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -1110,10 +1110,25 @@
}
}
}
+
f.mHost = mHost;
f.mParentFragment = mParent;
f.mFragmentManager = mParent != null
? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
+
+ // If we have a target fragment, push it along to at least CREATED
+ // so that this one can rely on it as an initialized dependency.
+ if (f.mTarget != null) {
+ if (!mActive.contains(f.mTarget)) {
+ throw new IllegalStateException("Fragment " + f
+ + " declared target fragment " + f.mTarget
+ + " that does not belong to this FragmentManager!");
+ }
+ if (f.mTarget.mState < Fragment.CREATED) {
+ moveToState(f.mTarget, Fragment.CREATED, 0, 0, true);
+ }
+ }
+
dispatchOnFragmentPreAttached(f, mHost.getContext(), false);
f.mCalled = false;
f.onAttach(mHost.getContext());
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 5a48793..0a2f804 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -539,7 +539,7 @@
*/
void swapDockedAndFullscreenStack();
void notifyLockedProfile(int userId);
- void startConfirmDeviceCredentialIntent(in Intent intent);
+ void startConfirmDeviceCredentialIntent(in Intent intent, in Bundle options);
void sendIdleJobTrigger();
int sendIntentSender(in IIntentSender target, int code, in Intent intent,
in String resolvedType, in IIntentReceiver finishedReceiver,
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 34d2039..7bdf4cc 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2486,9 +2486,23 @@
* A {@link Context} that will be used by the Builder to construct the
* RemoteViews. The Context will not be held past the lifetime of this Builder
* object.
+ * @param channelId
+ * The constructed Notification will be posted on this
+ * {@link NotificationChannel}. To use a NotificationChannel, it must first be
+ * created using {@link NotificationManager#createNotificationChannel}.
*/
+ public Builder(Context context, String channelId) {
+ this(context, (Notification) null);
+ mN.mChannelId = channelId;
+ }
+
+ /**
+ * @deprecated use {@link Notification.Builder#Notification.Builder(Context, String)}
+ * instead. All posted Notifications must specify a NotificationChannel Id.
+ */
+ @Deprecated
public Builder(Context context) {
- this(context, null);
+ this(context, (Notification) null);
}
/**
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 1f4fe67..5205959 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1131,7 +1131,7 @@
* can be used from within background operations like broadcast receivers
* or scheduled jobs.
*
- * @param service Description of the service to be stopped. The Intent must be either
+ * @param service Description of the service to be started. The Intent must be either
* fully explicit (supplying a component name) or specify a specific package
* name it is targeted to.
* @param id The identifier for this notification as per
diff --git a/core/java/android/app/ProfilerInfo.java b/core/java/android/app/ProfilerInfo.java
index cea7c3c..f3fe677 100644
--- a/core/java/android/app/ProfilerInfo.java
+++ b/core/java/android/app/ProfilerInfo.java
@@ -39,11 +39,16 @@
/* Automatically stop the profiler when the app goes idle. */
public final boolean autoStopProfiler;
- public ProfilerInfo(String filename, ParcelFileDescriptor fd, int interval, boolean autoStop) {
+ /* Indicates whether to stream the profiling info to the out file continuously. */
+ public final boolean streamingOutput;
+
+ public ProfilerInfo(String filename, ParcelFileDescriptor fd, int interval, boolean autoStop,
+ boolean streaming) {
profileFile = filename;
profileFd = fd;
samplingInterval = interval;
autoStopProfiler = autoStop;
+ streamingOutput = streaming;
}
public int describeContents() {
@@ -64,6 +69,7 @@
}
out.writeInt(samplingInterval);
out.writeInt(autoStopProfiler ? 1 : 0);
+ out.writeInt(streamingOutput ? 1 : 0);
}
public static final Parcelable.Creator<ProfilerInfo> CREATOR =
@@ -82,5 +88,6 @@
profileFd = in.readInt() != 0 ? ParcelFileDescriptor.CREATOR.createFromParcel(in) : null;
samplingInterval = in.readInt();
autoStopProfiler = in.readInt() != 0;
+ streamingOutput = in.readInt() != 0;
}
}
diff --git a/core/java/android/app/usage/StorageStatsManager.java b/core/java/android/app/usage/StorageStatsManager.java
index 695994b..9d30771 100644
--- a/core/java/android/app/usage/StorageStatsManager.java
+++ b/core/java/android/app/usage/StorageStatsManager.java
@@ -18,6 +18,7 @@
import android.annotation.WorkerThread;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -99,6 +100,8 @@
* @param volumeUuid the UUID of the storage volume you're interested in, or
* {@code null} to specify the default internal storage.
* @param uid the UID you're interested in.
+ * @see ApplicationInfo#volumeUuid
+ * @see ApplicationInfo#uid
*/
@WorkerThread
public StorageStats queryStatsForUid(String volumeUuid, int uid) {
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 9980e966..077331e 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -1072,6 +1072,18 @@
}
/**
+ * Return {@code TRUE} if the default launcher supports
+ * {@link #requestPinAppWidget(ComponentName, PendingIntent)}
+ */
+ public boolean isRequestPinAppWidgetSupported() {
+ try {
+ return mService.isRequestPinAppWidgetSupported();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Request to pin an app widget on the current launcher. It's up to the launcher to accept this
* request (optionally showing a user confirmation). If the request is accepted, the caller will
* get a confirmation with extra {@link #EXTRA_APPWIDGET_ID}.
@@ -1095,6 +1107,7 @@
*
* @see android.content.pm.ShortcutManager#isRequestPinShortcutSupported()
* @see android.content.pm.ShortcutManager#requestPinShortcut(ShortcutInfo, IntentSender)
+ * @see #isRequestPinAppWidgetSupported()
*
* @throws IllegalStateException The caller doesn't have a foreground activity or a foreground
* service or when the user is locked.
diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java
index 52cd2de..a37a0b3 100644
--- a/core/java/android/bluetooth/BluetoothCodecConfig.java
+++ b/core/java/android/bluetooth/BluetoothCodecConfig.java
@@ -51,9 +51,10 @@
// NOTE: The values should be same as those listed in the following file:
// hardware/libhardware/include/hardware/bt_av.h
public static final int SOURCE_CODEC_TYPE_SBC = 0;
- public static final int SOURCE_CODEC_TYPE_APTX = 1;
- public static final int SOURCE_CODEC_TYPE_APTX_HD = 2;
- public static final int SOURCE_CODEC_TYPE_LDAC = 3;
+ public static final int SOURCE_CODEC_TYPE_AAC = 1;
+ public static final int SOURCE_CODEC_TYPE_APTX = 2;
+ public static final int SOURCE_CODEC_TYPE_APTX_HD = 3;
+ public static final int SOURCE_CODEC_TYPE_LDAC = 4;
public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000;
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 3d9ba96..ec74617 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -596,8 +596,13 @@
*/
public int largestWidthLimitDp = 0;
- /** {@hide} */
+ /**
+ * UUID of the storage volume on which this application is being hosted. For
+ * apps hosted on the default internal storage at
+ * {@link Environment#getDataDirectory()}, the UUID value is {@code null}.
+ */
public String volumeUuid;
+
/** {@hide} */
public String scanSourceDir;
/** {@hide} */
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
index c90134a..03124be 100644
--- a/core/java/android/content/pm/IShortcutService.aidl
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -71,5 +71,5 @@
void applyRestore(in byte[] payload, int user);
- boolean isRequestPinShortcutSupported(int user);
+ boolean isRequestPinItemSupported(int user, int requestType);
}
\ No newline at end of file
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 57d2ba7..999b34f 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -93,22 +93,40 @@
* @see #EXTRA_PIN_ITEM_REQUEST
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_CONFIRM_PIN_ITEM =
- "android.content.pm.action.CONFIRM_PIN_ITEM";
+ public static final String ACTION_CONFIRM_PIN_SHORTCUT =
+ "android.content.pm.action.CONFIRM_PIN_SHORTCUT";
/**
- * An extra for {@link #ACTION_CONFIRM_PIN_ITEM} containing a
- * {@link ShortcutInfo} of the shortcut the publisher app asked to pin.
+ * Activity Action: For the default launcher to show the confirmation dialog to create
+ * a pinned app widget.
+ *
+ * <p>See the {@link android.appwidget.AppWidgetManager#requestPinAppWidget} javadoc for
+ * details.
+ *
+ * <p>
+ * Use {@link #getPinItemRequest(Intent)} to get a {@link PinItemRequest} object,
+ * and call {@link PinItemRequest#accept(Bundle)}
+ * if the user accepts. If the user doesn't accept, no further action is required.
+ *
+ * @see #EXTRA_PIN_ITEM_REQUEST
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_CONFIRM_PIN_APPWIDGET =
+ "android.content.pm.action.CONFIRM_PIN_APPWIDGET";
+
+ /**
+ * An extra for {@link #ACTION_CONFIRM_PIN_SHORTCUT} & {@link #ACTION_CONFIRM_PIN_APPWIDGET}
+ * containing a {@link PinItemRequest} of appropriate type asked to pin.
*
* <p>A helper function {@link #getPinItemRequest(Intent)} can be used
* instead of using this constant directly.
*
- * @see #ACTION_CONFIRM_PIN_ITEM
+ * @see #ACTION_CONFIRM_PIN_SHORTCUT
+ * @see #ACTION_CONFIRM_PIN_APPWIDGET
*/
public static final String EXTRA_PIN_ITEM_REQUEST =
"android.content.pm.extra.PIN_ITEM_REQUEST";
-
private Context mContext;
private ILauncherApps mService;
private PackageManager mPm;
@@ -1208,8 +1226,9 @@
}
/**
- * Represents a "pin shortcut" request made by an app, which is sent with
- * an {@link #ACTION_CONFIRM_PIN_ITEM} intent to the default launcher app.
+ * Represents a "pin shortcut" or a "pin appwidget" request made by an app, which is sent with
+ * an {@link #ACTION_CONFIRM_PIN_SHORTCUT} or {@link #ACTION_CONFIRM_PIN_APPWIDGET} intent
+ * respectively to the default launcher app.
*
* <p>Note the launcher may receive a request to pin a shortcut that is already pinned, because
* the user may actually want to have multiple icons of the same shortcut on the launcher.
@@ -1218,6 +1237,9 @@
* even if the launcher does not call it, the shortcut is already pinned. Also in this case,
* the {@code options} argument to {@link #accept(Bundle)} will be ignored.
*
+ * <p>For AppWidget pin requests launcher should send back the appwidget id as an extra for
+ * {@link #accept(Bundle)} as {@link android.appwidget.AppWidgetManager#EXTRA_APPWIDGET_ID}.
+ *
* @see #EXTRA_PIN_ITEM_REQUEST
* @see #getPinItemRequest(Intent)
*/
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 8223726..7032cc0 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1232,7 +1232,7 @@
XmlResourceParser parser = null;
try {
res = new Resources(assets, mMetrics, null);
- assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
@@ -1568,7 +1568,7 @@
private static AssetManager newConfiguredAssetManager() {
AssetManager assetManager = new AssetManager();
- assetManager.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ assetManager.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
return assetManager;
}
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 805054f..fb280a1 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -830,7 +830,8 @@
*/
public boolean isRequestPinShortcutSupported() {
try {
- return mService.isRequestPinShortcutSupported(injectMyUserId());
+ return mService.isRequestPinItemSupported(injectMyUserId(),
+ LauncherApps.PinItemRequest.REQUEST_TYPE_SHORTCUT);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index 4773c73..87a6d4a 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -74,4 +74,6 @@
public abstract boolean requestPinAppWidget(@NonNull String callingPackage,
@NonNull AppWidgetProviderInfo appWidget, @Nullable IntentSender resultIntent,
int userId);
+
+ public abstract boolean isRequestPinItemSupported(int callingUserId, int requestType);
}
diff --git a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
index 5a9966d..99eb470 100644
--- a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
+++ b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
@@ -65,7 +65,7 @@
AssetManager assets = new AssetManager();
try {
- assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
loadApkIntoAssetManager(assets, mBaseCodePath, mFlags);
diff --git a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
index 3ad45b6..4df90eb 100644
--- a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
+++ b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
@@ -83,7 +83,7 @@
throws PackageParser.PackageParserException {
final AssetManager assets = new AssetManager();
try {
- assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
for (String assetPath : assetPaths) {
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index db24ffe..6e3d343 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -759,7 +759,7 @@
int orientation, int touchscreen, int density, int keyboard,
int keyboardHidden, int navigation, int screenWidth, int screenHeight,
int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp,
- int screenLayout, int uiMode, int majorVersion);
+ int screenLayout, int uiMode, int colorMode, int majorVersion);
/**
* Retrieve the resource identifier for the given resource name.
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 05892e0..0ffc6c6 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -420,7 +420,7 @@
mConfiguration.smallestScreenWidthDp,
mConfiguration.screenWidthDp, mConfiguration.screenHeightDp,
mConfiguration.screenLayout, mConfiguration.uiMode,
- Build.VERSION.RESOURCES_SDK_INT);
+ mConfiguration.colorMode, Build.VERSION.RESOURCES_SDK_INT);
if (DEBUG_CONFIG) {
Slog.i(TAG, "**** Updating config of " + this + ": final config is "
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 12e1963..6788b67 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -108,13 +108,14 @@
* </p>
*
* <p>
- * A private virtual display belongs to the application that created it.
- * Only the a owner of a private virtual display is allowed to place windows upon it.
- * The private virtual display also does not participate in display mirroring: it will
- * neither receive mirrored content from another display nor allow its own content to
- * be mirrored elsewhere. More precisely, the only processes that are allowed to
- * enumerate or interact with the private display are those that have the same UID as the
- * application that originally created the private virtual display.
+ * A private virtual display belongs to the application that created it. Only the a owner of a
+ * private virtual display and the apps that are already on that display are allowed to place
+ * windows upon it. The private virtual display also does not participate in display mirroring:
+ * it will neither receive mirrored content from another display nor allow its own content to be
+ * mirrored elsewhere. More precisely, the only processes that are allowed to enumerate or
+ * interact with the private display are those that have the same UID as the application that
+ * originally created the private virtual display or as the activities that are already on that
+ * display.
* </p>
*
* @see #createVirtualDisplay
@@ -234,6 +235,21 @@
*/
public static final int VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR = 1 << 4;
+ /**
+ * Virtual display flag: Allows content to be displayed on private virtual displays when
+ * keyguard is shown but is insecure.
+ *
+ * <p>
+ * This flag can only be applied to private displays as defined by the
+ * {@link Display#FLAG_PRIVATE} display flag. It is mutually exclusive with
+ * {@link #VIRTUAL_DISPLAY_FLAG_PUBLIC}. If both flags are specified then this flag's behavior
+ * will not be applied.
+ * </p>
+ *
+ * @see #createVirtualDisplay
+ */
+ public static final int VIRTUAL_DISPLAY_FLAG_SHOW_WITH_INSECURE_LOCKSCREEN = 1 << 5;
+
/** @hide */
public DisplayManager(Context context) {
mContext = context;
diff --git a/core/java/android/metrics/LogMaker.java b/core/java/android/metrics/LogMaker.java
index 2bf841c..83f30be 100644
--- a/core/java/android/metrics/LogMaker.java
+++ b/core/java/android/metrics/LogMaker.java
@@ -102,10 +102,13 @@
/**
* @param tag From your MetricsEvent enum.
- * @param value One of Integer, Long, Float, String
- * @return
+ * @param value One of Integer, Long, Float, or String; or null to clear the tag.
+ * @return modified LogMaker
*/
public LogMaker addTaggedData(int tag, Object value) {
+ if (value == null) {
+ return clearTaggedData(tag);
+ }
if (!isValidValue(value)) {
throw new IllegalArgumentException(
"Value must be loggable type - int, long, float, String");
@@ -118,11 +121,21 @@
return this;
}
+ /**
+ * Remove a value from the LogMaker.
+ *
+ * @param tag From your MetricsEvent enum.
+ * @return modified LogMaker
+ */
+ public LogMaker clearTaggedData(int tag) {
+ entries.delete(tag);
+ return this;
+ }
+
+ /**
+ * @return true if this object may be added to a LogMaker as a value.
+ */
public boolean isValidValue(Object value) {
- if (value == null) {
- Log.i("LogBuilder", "Logging a null value.");
- return true;
- }
return value instanceof Integer ||
value instanceof String ||
value instanceof Long ||
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 80ecf97..817cb5b 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -883,8 +883,11 @@
SystemProperties.getInt("ro.debuggable", 0) == 1;
/** {@hide} */
- public static final boolean IS_ENG =
- "eng".equals(getString("ro.build.type"));
+ public static final boolean IS_ENG = "eng".equals(TYPE);
+ /** {@hide} */
+ public static final boolean IS_USERDEBUG = "userdebug".equals(TYPE);
+ /** {@hide} */
+ public static final boolean IS_USER = "user".equals(TYPE);
/**
* Whether this build is running inside a container.
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 175d883..210ddb6 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -1119,8 +1119,8 @@
* @hide
*/
public static void startMethodTracing(String traceName, FileDescriptor fd,
- int bufferSize, int flags) {
- VMDebug.startMethodTracing(traceName, fd, bufferSize, flags, false, 0);
+ int bufferSize, int flags, boolean streamOutput) {
+ VMDebug.startMethodTracing(traceName, fd, bufferSize, flags, false, 0, streamOutput);
}
/**
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 8d9ceb6..7cdb3ce 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -272,6 +272,11 @@
}
/** {@hide} */
+ public static File getDataMiscCeDirectory() {
+ return buildPath(getDataDirectory(), "misc_ce");
+ }
+
+ /** {@hide} */
public static File getDataMiscCeDirectory(int userId) {
return buildPath(getDataDirectory(), "misc_ce", String.valueOf(userId));
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index d6688e3..a41f45b 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -189,6 +189,13 @@
/** {@hide} */
public static final int LAST_APPLICATION_CACHE_GID = 29999;
+ /** {@hide} */
+ public static final int MEDIA_AUDIO_GID = 1055;
+ /** {@hide} */
+ public static final int MEDIA_VIDEO_GID = 1056;
+ /** {@hide} */
+ public static final int MEDIA_IMAGE_GID = 1057;
+
/**
* Standard priority of application threads.
* Use with {@link #setThreadPriority(int)} and
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index ae981b7..b9e4bad 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -40,6 +40,7 @@
import dalvik.system.BlockGuard;
import dalvik.system.CloseGuard;
import dalvik.system.VMDebug;
+import dalvik.system.VMRuntime;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -423,7 +424,21 @@
* disk operations but will likely expand in future releases.
*/
public Builder detectAll() {
- return enable(ALL_THREAD_DETECT_BITS);
+ detectDiskReads();
+ detectDiskWrites();
+ detectNetwork();
+
+ final int targetSdk = VMRuntime.getRuntime().getTargetSdkVersion();
+ if (targetSdk >= Build.VERSION_CODES.HONEYCOMB) {
+ detectCustomSlowCalls();
+ }
+ if (targetSdk >= Build.VERSION_CODES.M) {
+ detectResourceMismatches();
+ }
+ if (targetSdk >= Build.VERSION_CODES.O) {
+ detectUnbufferedIo();
+ }
+ return this;
}
/**
@@ -722,18 +737,31 @@
* but will likely expand in future releases.
*/
public Builder detectAll() {
- int flags = DETECT_VM_ACTIVITY_LEAKS | DETECT_VM_CURSOR_LEAKS
- | DETECT_VM_CLOSABLE_LEAKS | DETECT_VM_REGISTRATION_LEAKS
- | DETECT_VM_FILE_URI_EXPOSURE | DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION
- | DETECT_VM_UNTAGGED_SOCKET;
+ detectLeakedSqlLiteObjects();
- // TODO: always add DETECT_VM_CLEARTEXT_NETWORK once we have facility
- // for apps to mark sockets that should be ignored
- if (SystemProperties.getBoolean(CLEARTEXT_PROPERTY, false)) {
- flags |= DETECT_VM_CLEARTEXT_NETWORK;
+ final int targetSdk = VMRuntime.getRuntime().getTargetSdkVersion();
+ if (targetSdk >= Build.VERSION_CODES.HONEYCOMB) {
+ detectActivityLeaks();
+ detectLeakedClosableObjects();
}
-
- return enable(flags);
+ if (targetSdk >= Build.VERSION_CODES.JELLY_BEAN) {
+ detectLeakedRegistrationObjects();
+ }
+ if (targetSdk >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ detectFileUriExposure();
+ }
+ if (targetSdk >= Build.VERSION_CODES.M) {
+ // TODO: always add DETECT_VM_CLEARTEXT_NETWORK once we have
+ // facility for apps to mark sockets that should be ignored
+ if (SystemProperties.getBoolean(CLEARTEXT_PROPERTY, false)) {
+ detectCleartextNetwork();
+ }
+ }
+ if (targetSdk >= Build.VERSION_CODES.O) {
+ detectContentUriWithoutPermission();
+ detectUntaggedSockets();
+ }
+ return this;
}
/**
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index c5c380c..388054d 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -66,6 +66,8 @@
private final IUserManager mService;
private final Context mContext;
+ private Boolean mIsManagedProfileCached;
+
/**
* @hide
* No user restriction.
@@ -970,8 +972,14 @@
*/
@SystemApi
public boolean isManagedProfile() {
+ // No need for synchronization. Once it becomes non-null, it'll be non-null forever.
+ // Worst case we might end up calling the AIDL method multiple times but that's fine.
+ if (mIsManagedProfileCached != null) {
+ return mIsManagedProfileCached;
+ }
try {
- return mService.isManagedProfile(UserHandle.myUserId());
+ mIsManagedProfileCached = mService.isManagedProfile(UserHandle.myUserId());
+ return mIsManagedProfileCached;
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -987,6 +995,9 @@
*/
@SystemApi
public boolean isManagedProfile(@UserIdInt int userId) {
+ if (userId == UserHandle.myUserId()) {
+ return isManagedProfile();
+ }
try {
return mService.isManagedProfile(userId);
} catch (RemoteException re) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d41b3c5..f1bffd3 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1274,10 +1274,14 @@
/**
* Activity Action: Show notification settings for a single app.
- *
- * Input: Optionally, {@link #EXTRA_CHANNEL_ID}, to highlight that channel.
+ * <p>
+ * Input: {@link #EXTRA_APP_PACKAGE}, the package containing the channel to display.
+ * Input: Optionally, {@link #EXTRA_CHANNEL_ID}, to highlight that channel.
+ * <p>
+ * Output: Nothing.
* @hide
*/
+ @SystemApi
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_APP_NOTIFICATION_SETTINGS
= "android.settings.APP_NOTIFICATION_SETTINGS";
@@ -7654,6 +7658,13 @@
public static final String LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS =
"location_background_throttle_interval_ms";
+ /**
+ * Packages that are whitelisted for background throttling (throttling will not be applied).
+ * @hide
+ */
+ public static final String LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST =
+ "location_background_throttle_package_whitelist";
+
/**
* Whether TV will switch to MHL port when a mobile device is plugged in.
* (0 = false, 1 = true)
@@ -8174,6 +8185,14 @@
public static final String WIFI_WAKEUP_ENABLED = "wifi_wakeup_enabled";
/**
+ * Value to specify whether network quality scores and badging should be shown in the UI.
+ *
+ * Type: int (0 for false, 1 for true)
+ * @hide
+ */
+ public static final String NETWORK_SCORING_UI_ENABLED = "network_scoring_ui_enabled";
+
+ /**
* Value to specify if network recommendations from
* {@link com.android.server.NetworkScoreService} are enabled.
*
@@ -9562,6 +9581,14 @@
public static final String CONTACTS_DATABASE_WAL_ENABLED = "contacts_database_wal_enabled";
/**
+ * Flag to enable the link to location permissions in location setting. Set to 0 to disable.
+ *
+ * @hide
+ */
+ public static final String LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED =
+ "location_settings_link_to_permissions_enabled";
+
+ /**
* Settings to backup. This is here so that it's in the same place as the settings
* keys and easy to update.
*
@@ -10112,6 +10139,12 @@
* @hide
*/
public static final String WARNING_TEMPERATURE = "warning_temperature";
+
+ /**
+ * Whether the diskstats logging task is enabled/disabled.
+ * @hide
+ */
+ public static final String ENABLE_DISKSTATS_LOGGING = "enable_diskstats_logging";
}
/**
diff --git a/core/java/android/service/autofill/AutoFillService.java b/core/java/android/service/autofill/AutoFillService.java
index b5cb8f8..1e4f90d 100644
--- a/core/java/android/service/autofill/AutoFillService.java
+++ b/core/java/android/service/autofill/AutoFillService.java
@@ -89,16 +89,14 @@
/**
* Key of the {@link Bundle} passed to methods such as
- * {@link #onSaveRequest(AssistStructure, Bundle, CancellationSignal, SaveCallback)}
- * containing the extras set by
+ * {@link #onSaveRequest(AssistStructure, Bundle, SaveCallback)} containing the extras set by
* {@link android.view.autofill.FillResponse.Builder#setExtras(Bundle)}.
*/
public static final String EXTRA_RESPONSE_EXTRAS = KEY_PREFIX + "RESPONSE_EXTRAS";
/**
* Key of the {@link Bundle} passed to methods such as
- * {@link #onSaveRequest(AssistStructure, Bundle, CancellationSignal, SaveCallback)}
- * containing the extras set by
+ * {@link #onSaveRequest(AssistStructure, Bundle, SaveCallback)} containing the extras set by
* {@link android.view.autofill.Dataset.Builder#setExtras(Bundle)}.
*/
public static final String EXTRA_DATASET_EXTRAS = KEY_PREFIX + "DATASET_EXTRAS";
@@ -134,9 +132,9 @@
@Override
public void autoFill(AssistStructure structure, IAutoFillServerCallback callback,
- Bundle extras, int flags) {
+ int flags) {
mHandlerCaller
- .obtainMessageIOOO(MSG_AUTO_FILL_ACTIVITY, flags, structure, extras, callback)
+ .obtainMessageIOO(MSG_AUTO_FILL_ACTIVITY, flags, structure, callback)
.sendToTarget();
}
@@ -179,9 +177,8 @@
final SomeArgs args = (SomeArgs) msg.obj;
final int flags = msg.arg1;
final AssistStructure structure = (AssistStructure) args.arg1;
- final Bundle extras = (Bundle) args.arg2;
- final IAutoFillServerCallback callback = (IAutoFillServerCallback) args.arg3;
- requestAutoFill(callback, structure, extras, flags);
+ final IAutoFillServerCallback callback = (IAutoFillServerCallback) args.arg2;
+ requestAutoFill(callback, structure, flags);
break;
} case MSG_AUTHENTICATE_FILL_RESPONSE: {
final int flags = msg.arg1;
@@ -254,8 +251,8 @@
* @param cancellationSignal signal for observing cancel requests.
* @param callback object used to notify the result of the request.
*/
- public abstract void onFillRequest(AssistStructure structure,
- Bundle data, CancellationSignal cancellationSignal, FillCallback callback);
+ public abstract void onFillRequest(AssistStructure structure, Bundle data,
+ CancellationSignal cancellationSignal, FillCallback callback);
/**
* Called when user requests service to save the fields of an {@link Activity}.
@@ -267,20 +264,19 @@
* @param structure {@link Activity}'s view structure.
* @param data bundle containing additional arguments set by the Android system (currently none)
* or data passed by the service in the {@link FillResponse} that originated this call.
- * @param cancellationSignal signal for observing cancel requests.
* @param callback object used to notify the result of the request.
*/
- public abstract void onSaveRequest(AssistStructure structure,
- Bundle data, CancellationSignal cancellationSignal, SaveCallback callback);
+ public abstract void onSaveRequest(AssistStructure structure, Bundle data,
+ SaveCallback callback);
/**
* Called as result of the user action for a {@link FillResponse} that required authentication.
*
* <p>When the {@link FillResponse} required authentication through
- * {@link android.view.autofill.FillResponse.Builder#requiresCustomAuthentication(Bundle, int)}, this
- * call indicates the user is requesting the service to authenticate him/her (and {@code flags}
- * contains {@link #FLAG_AUTHENTICATION_REQUESTED}), and {@code extras} contains the
- * {@link Bundle} passed to that method.
+ * {@link android.view.autofill.FillResponse.Builder#requiresCustomAuthentication(Bundle, int)},
+ * this call indicates the user is requesting the service to authenticate him/her (and
+ * {@code flags} contains {@link #FLAG_AUTHENTICATION_REQUESTED}), and {@code extras} contains
+ * the {@link Bundle} passed to that method.
*
* <p>When the {@link FillResponse} required authentication through
* {@link android.view.autofill.FillResponse.Builder#requiresFingerprintAuthentication(
@@ -336,27 +332,28 @@
}
private void requestAutoFill(IAutoFillServerCallback callback, AssistStructure structure,
- Bundle data, int flags) {
- switch (flags) {
- case AUTO_FILL_FLAG_TYPE_FILL:
- final FillCallback fillCallback = new FillCallback(callback);
- if (DEBUG_PENDING_CALLBACKS) {
- addPendingCallback(fillCallback);
- }
- // TODO(b/33197203): hook up the cancelationSignal
- onFillRequest(structure, data, new CancellationSignal(), fillCallback);
- break;
- case AUTO_FILL_FLAG_TYPE_SAVE:
- final SaveCallback saveCallback = new SaveCallback(callback);
- if (DEBUG_PENDING_CALLBACKS) {
- addPendingCallback(saveCallback);
- }
- // TODO(b/33197203): hook up the cancelationSignal
- onSaveRequest(structure, data, new CancellationSignal(), saveCallback);
- break;
- default:
- Log.w(TAG, "invalid flag on requestAutoFill(): " + flags);
+ int flags) {
+ if (DEBUG) Log.d(TAG, "requestAutoFill(): flags=" + flags);
+
+ if ((flags & AUTO_FILL_FLAG_TYPE_FILL) != 0) {
+ final FillCallback fillCallback = new FillCallback(callback);
+ if (DEBUG_PENDING_CALLBACKS) {
+ addPendingCallback(fillCallback);
+ }
+ // TODO(b/33197203): hook up the cancelationSignal
+ onFillRequest(structure, null, new CancellationSignal(), fillCallback);
+ return;
}
+ if ((flags & AUTO_FILL_FLAG_TYPE_SAVE) != 0) {
+ final SaveCallback saveCallback = new SaveCallback(callback);
+ if (DEBUG_PENDING_CALLBACKS) {
+ addPendingCallback(saveCallback);
+ }
+ onSaveRequest(structure, null, saveCallback);
+ return;
+ }
+
+ Log.w(TAG, "invalid flags on requestAutoFill(): " + flags);
}
private void addPendingCallback(CallbackHelper.Dumpable callback) {
diff --git a/core/java/android/service/autofill/IAutoFillAppCallback.aidl b/core/java/android/service/autofill/IAutoFillAppCallback.aidl
index cc83776..8c3898a 100644
--- a/core/java/android/service/autofill/IAutoFillAppCallback.aidl
+++ b/core/java/android/service/autofill/IAutoFillAppCallback.aidl
@@ -25,8 +25,7 @@
*
* @hide
*/
-// TODO(b/33197203): rename methods to make them more consistent with a callback, or rename class
-// itself
+// TODO(b/33197203): rename IAutoFillAppSession
oneway interface IAutoFillAppCallback {
/**
* Auto-fills the activity with the contents of a dataset.
diff --git a/core/java/android/service/autofill/IAutoFillManagerService.aidl b/core/java/android/service/autofill/IAutoFillManagerService.aidl
index ce42107..ace5411 100644
--- a/core/java/android/service/autofill/IAutoFillManagerService.aidl
+++ b/core/java/android/service/autofill/IAutoFillManagerService.aidl
@@ -27,8 +27,9 @@
*/
oneway interface IAutoFillManagerService {
- void showAutoFillInput(in AutoFillId id, in Rect boundaries);
+ // Called by AutoFillManager (app).
+ void requestAutoFill(in AutoFillId id, in Rect bounds, int flags);
- // TODO(b/33197203): remove it and refactor onShellCommand
- void requestAutoFill(IBinder activityToken, int userId, in Bundle extras, int flags);
+ // Called by ShellCommand only.
+ void requestAutoFillForUser(int userId, int flags);
}
diff --git a/core/java/android/service/autofill/IAutoFillServerCallback.aidl b/core/java/android/service/autofill/IAutoFillServerCallback.aidl
index 185c8f3..f7d5064 100644
--- a/core/java/android/service/autofill/IAutoFillServerCallback.aidl
+++ b/core/java/android/service/autofill/IAutoFillServerCallback.aidl
@@ -28,8 +28,7 @@
*
* @hide
*/
-// TODO(b/33197203): rename methods to make them more consistent with a callback, or rename class
-// itself
+// TODO(b/33197203): rename to IAutoFillServerSession
oneway interface IAutoFillServerCallback {
// TODO(b/33197203): document methods
void showResponse(in FillResponse response);
diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl
index fa9786a..3e8087b 100644
--- a/core/java/android/service/autofill/IAutoFillService.aidl
+++ b/core/java/android/service/autofill/IAutoFillService.aidl
@@ -27,8 +27,7 @@
// TODO(b/33197203): document class and methods
oneway interface IAutoFillService {
// TODO(b/33197203): rename method to make them more consistent
- void autoFill(in AssistStructure structure, in IAutoFillServerCallback callback,
- in Bundle extras, int flags);
+ void autoFill(in AssistStructure structure, in IAutoFillServerCallback callback, int flags);
void authenticateFillResponse(in Bundle extras, int flags);
void authenticateDataset(in Bundle extras, int flags);
void onConnected();
diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java
index d5022d8..e2fb588 100644
--- a/core/java/android/service/autofill/SaveCallback.java
+++ b/core/java/android/service/autofill/SaveCallback.java
@@ -58,7 +58,7 @@
/**
* Notifies the Android System that an
* {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle,
- * android.os.CancellationSignal, SaveCallback)} was successfully fulfilled by the service.
+ * SaveCallback)} was successfully fulfilled by the service.
*
* @param ids ids ({@link ViewNode#getAutoFillId()}) of the fields that were saved.
*
@@ -85,7 +85,7 @@
/**
* Notifies the Android System that an
* {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle,
- * android.os.CancellationSignal, SaveCallback)} could not be fulfilled by the service.
+ * SaveCallback)} could not be fulfilled by the service.
*
* @param message error message to be displayed to the user.
*
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 105cc47..ebb9cf9 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -163,7 +163,7 @@
/**
* Display flag: Indicates that the display is private. Only the application that
- * owns the display can create windows on it.
+ * owns the display and apps that are already on the display can create windows on it.
*
* @see #getFlags
*/
@@ -194,6 +194,19 @@
public static final int FLAG_ROUND = 1 << 4;
/**
+ * Display flag: Indicates that the display can show its content when non-secure keyguard is
+ * shown.
+ * <p>
+ * This flag identifies secondary displays that won't show keyguard if it can be dismissed
+ * without entering credentials. Display content will be shown even if other displays are
+ * locked.
+ * </p>
+ *
+ * @see #getFlags
+ */
+ public static final int FLAG_SHOW_WITH_INSECURE_LOCKSCREEN = 1 << 5;
+
+ /**
* Display flag: Indicates that the contents of the display should not be scaled
* to fit the physical screen dimensions. Used for development only to emulate
* devices with smaller physicals screens while preserving density.
@@ -777,8 +790,7 @@
public boolean isHdr() {
synchronized (this) {
updateDisplayInfoLocked();
- int[] types = mDisplayInfo.hdrCapabilities.getSupportedHdrTypes();
- return types != null && types.length > 0;
+ return mDisplayInfo.isHdr();
}
}
@@ -788,12 +800,7 @@
public boolean isWideColorGamut() {
synchronized (this) {
updateDisplayInfoLocked();
- for (int colorMode : mDisplayInfo.supportedColorModes) {
- if (colorMode == COLOR_MODE_DCI_P3 || colorMode > COLOR_MODE_SRGB) {
- return true;
- }
- }
- return false;
+ return mDisplayInfo.isWideColorGamut();
}
}
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 1aef6ec..f6b94af 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -519,6 +519,20 @@
logicalHeight : logicalWidth;
}
+ public boolean isHdr() {
+ int[] types = hdrCapabilities != null ? hdrCapabilities.getSupportedHdrTypes() : null;
+ return types != null && types.length > 0;
+ }
+
+ public boolean isWideColorGamut() {
+ for (int colorMode : supportedColorModes) {
+ if (colorMode == Display.COLOR_MODE_DCI_P3 || colorMode > Display.COLOR_MODE_SRGB) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Returns true if the specified UID has access to this display.
*/
diff --git a/core/java/android/view/IDockedStackListener.aidl b/core/java/android/view/IDockedStackListener.aidl
index 36a81db..4cf7cf3e 100644
--- a/core/java/android/view/IDockedStackListener.aidl
+++ b/core/java/android/view/IDockedStackListener.aidl
@@ -40,8 +40,11 @@
*
* @param minimized Whether the docked stack is currently minimized.
* @param animDuration The duration of the animation for changing the minimized state.
+ * @param isHomeStackResizable If the home stack is resizable, a portion of the docked stack
+ * will be shown with the divider
*/
- void onDockedStackMinimizedChanged(boolean minimized, long animDuration);
+ void onDockedStackMinimizedChanged(boolean minimized, long animDuration,
+ boolean isHomeStackResizable);
/**
* Called when window manager decides to adjust the divider for IME. Like the minimized state,
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index a12600a..5bb577f 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -34,7 +34,7 @@
private static final String TAG = "SurfaceControl";
private static native long nativeCreate(SurfaceSession session, String name,
- int w, int h, int format, int flags)
+ int w, int h, int format, int flags, long parentObject)
throws OutOfResourcesException;
private static native void nativeRelease(long nativeObject);
private static native void nativeDestroy(long nativeObject);
@@ -287,6 +287,12 @@
public SurfaceControl(SurfaceSession session,
String name, int w, int h, int format, int flags)
throws OutOfResourcesException {
+ this(session, name, w, h, format, flags, null);
+ }
+
+ public SurfaceControl(SurfaceSession session,
+ String name, int w, int h, int format, int flags, SurfaceControl parent)
+ throws OutOfResourcesException {
if (session == null) {
throw new IllegalArgumentException("session must not be null");
}
@@ -304,7 +310,7 @@
}
mName = name;
- mNativeObject = nativeCreate(session, name, w, h, format, flags);
+ mNativeObject = nativeCreate(session, name, w, h, format, flags, parent != null ? parent.mNativeObject : 0);
if (mNativeObject == 0) {
throw new OutOfResourcesException(
"Couldn't allocate SurfaceControl native object");
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ba9bb67..0657bef 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1765,12 +1765,6 @@
*/
int mAccessibilityViewId = NO_ID;
- /**
- * The stable ID of this view for auto-fill purposes.
- */
- private int mAutoFillId = NO_ID;
-
-
private int mAccessibilityCursorPosition = ACCESSIBILITY_CURSOR_POSITION_UNDEFINED;
SendViewStateChangedAccessibilityEvent mSendViewStateChangedAccessibilityEvent;
@@ -4045,9 +4039,9 @@
* input fields and tags (like {@code id}).
* </ul>
*/
- // TODO(b/33197203) (b/34078930): improve documentation: mention all cases, show examples, etc.
- // In particular, be more specific about webview restrictions
- public static final int AUTO_FILL_FLAG_TYPE_FILL = 0x1;
+ // TODO(b/33197203): cannot conflict with flags defined on AutoFillManager until they're removed
+ // (when save is refactored).
+ public static final int AUTO_FILL_FLAG_TYPE_FILL = 0x10000000;
/**
* Set when the user explicitly asked a {@link android.service.autofill.AutoFillService} to save
@@ -4057,7 +4051,9 @@
* (Personally Identifiable Information). For example, the text of password fields should be
* included since that's what's typically saved.
*/
- public static final int AUTO_FILL_FLAG_TYPE_SAVE = 0x2;
+ // TODO(b/33197203): cannot conflict with flags defined on AutoFillManager until they're removed
+ // (when save is refactored).
+ public static final int AUTO_FILL_FLAG_TYPE_SAVE = 0x20000000;
/**
* Set to true when drawing cache is enabled and cannot be created.
@@ -6940,8 +6936,7 @@
if (forAutoFill) {
// The auto-fill id needs to be unique, but its value doesn't matter, so it's better to
// reuse the accessibility id to save space.
- mAutoFillId = getAccessibilityViewId();
- structure.setAutoFillId(mAutoFillId);
+ structure.setAutoFillId(getAccessibilityViewId());
structure.setAutoFillType(getAutoFillType());
}
@@ -7568,20 +7563,6 @@
}
/**
- * Gets the unique identifier of this view for auto-fill purposes.
- *
- * <p>It's only set after {@link #onProvideAutoFillStructure(ViewStructure, int)} is called.
- *
- * @return The view autofill id or {@link #NO_ID} if
- * {@link #onProvideAutoFillStructure(ViewStructure, int)} was not called yet.
- *
- * @hide
- */
- public int getAutoFillViewId() {
- return mAutoFillId;
- }
-
- /**
* Gets the unique identifier of the window in which this View reseides.
*
* @return The window accessibility id.
diff --git a/core/java/android/view/autofill/AutoFillManager.java b/core/java/android/view/autofill/AutoFillManager.java
index cd9842f..cf56e0e 100644
--- a/core/java/android/view/autofill/AutoFillManager.java
+++ b/core/java/android/view/autofill/AutoFillManager.java
@@ -36,12 +36,16 @@
/**
* Flag used to show the auto-fill UI affordance for a view.
*/
- public static final int FLAG_UPDATE_UI_SHOW = 1 << 0;
+ // TODO(b/33197203): cannot conflict with flags defined on View until they're removed (when
+ // save is refactored).
+ public static final int FLAG_UPDATE_UI_SHOW = 0x1;
/**
* Flag used to hide the auto-fill UI affordance for a view.
*/
- public static final int FLAG_UPDATE_UI_HIDE = 1 << 1;
+ // TODO(b/33197203): cannot conflict with flags defined on View until they're removed (when
+ // save is refactored).
+ public static final int FLAG_UPDATE_UI_HIDE = 0x2;
private final IAutoFillManagerService mService;
@@ -64,11 +68,10 @@
* {@link #FLAG_UPDATE_UI_HIDE}.
*/
public void updateAutoFillInput(View view, int flags) {
- if (DEBUG) {
- Log.v(TAG, "updateAutoFillInput(" + view.getAutoFillViewId() + "): flags=" + flags);
- }
+ final Rect bounds = new Rect();
+ view.getBoundsOnScreen(bounds);
- updateAutoFillInput(view, false, View.NO_ID, null, flags);
+ requestAutoFill(new AutoFillId(view.getAccessibilityViewId()), bounds, flags);
}
/**
@@ -79,56 +82,22 @@
*
* @param parent parent view.
* @param childId id identifying the virtual child inside the parent view.
- * @param boundaries boundaries of the child (inside the parent; could be {@code null} when
+ * @param bounds absolute boundaries of the child in the window (could be {@code null} when
* flag is {@link #FLAG_UPDATE_UI_HIDE}.
* @param flags either {@link #FLAG_UPDATE_UI_SHOW} or
* {@link #FLAG_UPDATE_UI_HIDE}.
*/
- public void updateAutoFillInput(View parent, int childId, @Nullable Rect boundaries,
+ public void updateAutoFillInput(View parent, int childId, @Nullable Rect bounds,
int flags) {
+ requestAutoFill(new AutoFillId(parent.getAccessibilityViewId(), childId), bounds, flags);
+ }
+
+ private void requestAutoFill(AutoFillId id, Rect bounds, int flags) {
if (DEBUG) {
- Log.v(TAG, "updateAutoFillInput(" + parent.getAutoFillViewId() + ", " + childId
- + "): boundaries=" + boundaries + ", flags=" + flags);
+ Log.v(TAG, "requestAutoFill(): id=" + id + ", bounds=" + bounds + ", flags=" + flags);
}
- updateAutoFillInput(parent, true, childId, boundaries, flags);
- }
-
- private void updateAutoFillInput(View view, boolean virtual, int childId, Rect boundaries,
- int flags) {
- if ((flags & FLAG_UPDATE_UI_SHOW) != 0) {
- final int viewId = view.getAutoFillViewId();
- final AutoFillId id = virtual
- ? new AutoFillId(viewId, childId)
- : new AutoFillId(viewId);
- showAutoFillInput(id, boundaries);
- return;
- }
- // TODO(b/33197203): handle FLAG_UPDATE_UI_HIDE
- }
-
- private void showAutoFillInput(AutoFillId id, Rect boundaries) {
- final int autoFillViewId = id.getViewId();
- /*
- * TODO(b/33197203): currently SHOW_AUTO_FILL_BAR is only set once per activity (i.e, when
- * the view does not have an auto-fill id), but it should be called again for views that
- * were not part of the initial auto-fill dataset returned by the service. For example:
- *
- * 1.Activity has 4 fields, `first_name`, `last_name`, and `address`.
- * 2.User taps `first_name`.
- * 3.Service returns a dataset with ids for `first_name` and `last_name`.
- * 4.When user taps `first_name` (again) or `last_name`, flag should not have
- * SHOW_AUTO_FILL_BAR set, but when user taps `address`, it should (since that field was
- * not part of the initial dataset).
- *
- * Similarly, once the activity is auto-filled, the flag logic should be reset (so if the
- * user taps the view again, a new auto-fill request is made)
- */
- if (autoFillViewId != View.NO_ID) {
- return;
- }
-
try {
- mService.showAutoFillInput(id, boundaries);
+ mService.requestAutoFill(id, bounds, flags);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/view/autofill/AutoFillSession.java b/core/java/android/view/autofill/AutoFillSession.java
new file mode 100644
index 0000000..eec7a82
--- /dev/null
+++ b/core/java/android/view/autofill/AutoFillSession.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.autofill;
+
+import static android.view.autofill.Helper.DEBUG;
+
+import android.app.Activity;
+import android.os.RemoteException;
+import android.service.autofill.IAutoFillAppCallback;
+import android.util.Log;
+import android.view.View;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * An auto-fill session associated with an activity.
+ *
+ * @hide
+ */
+public final class AutoFillSession {
+
+ private static final String TAG = "AutoFillSession";
+
+ private final IAutoFillAppCallback mCallback = new IAutoFillAppCallback.Stub() {
+ @Override
+ public void autoFill(Dataset dataset) throws RemoteException {
+ final Activity activity = mActivity.get();
+ if (activity == null) {
+ if (DEBUG) Log.d(TAG, "autoFill(): activity already GCed");
+ return;
+ }
+ // TODO(b/33197203): must keep the dataset so subsequent calls pass the same
+ // dataset.extras to service
+ activity.runOnUiThread(() -> {
+ final View root = activity.getWindow().getDecorView().getRootView();
+ for (DatasetField field : dataset.getFields()) {
+ final AutoFillId id = field.getId();
+ if (id == null) {
+ Log.w(TAG, "autoFill(): null id on " + field);
+ continue;
+ }
+ final int viewId = id.getViewId();
+ final View view = root.findViewByAccessibilityIdTraversal(viewId);
+ if (view == null) {
+ Log.w(TAG, "autoFill(): no View with id " + viewId);
+ continue;
+ }
+
+ // TODO(b/33197203): handle protected value (like credit card)
+ if (id.isVirtual()) {
+ // Delegate virtual fields.
+ setAutoFillDelegateCallback();
+ final VirtualViewDelegate delegate = view
+ .getAutoFillVirtualViewDelegate(
+ mAutoFillDelegateCallback);
+ if (delegate == null) {
+ Log.w(TAG, "autoFill(): cannot fill virtual " + id
+ + "; no VirtualViewDelegate for view "
+ + view.getClass());
+ continue;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "autoFill(): delegating " + id
+ + " to VirtualViewDelegate " + delegate);
+ }
+ delegate.autoFill(id.getVirtualChildId(), field.getValue());
+ } else {
+ // Handle non-virtual fields itself.
+ view.autoFill(field.getValue());
+ }
+ }
+ });
+ }
+ };
+
+ private final WeakReference<Activity> mActivity;
+
+ @GuardedBy("this")
+ private VirtualViewDelegate.Callback mAutoFillDelegateCallback;
+
+ public AutoFillSession(Activity activity) {
+ mActivity = new WeakReference<>(activity);
+ }
+
+ public IAutoFillAppCallback getCallback() {
+ return mCallback;
+ }
+
+ /**
+ * Lazily sets the {@link #mAutoFillDelegateCallback}.
+ */
+ private void setAutoFillDelegateCallback() {
+ synchronized (this) {
+ if (mAutoFillDelegateCallback == null) {
+ mAutoFillDelegateCallback = new VirtualViewDelegate.Callback() {
+ // TODO(b/33197203): implement
+ };
+ }
+ }
+ }
+
+}
diff --git a/core/java/android/view/autofill/Dataset.java b/core/java/android/view/autofill/Dataset.java
index b11eecc..18a08f9 100644
--- a/core/java/android/view/autofill/Dataset.java
+++ b/core/java/android/view/autofill/Dataset.java
@@ -284,7 +284,7 @@
* Sets a {@link Bundle} that will be passed to subsequent calls to
* {@link android.service.autofill.AutoFillService} methods such as
* {@link android.service.autofill.AutoFillService#onSaveRequest(android.app.assist.AssistStructure,
- * Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback)}, using
+ * Bundle, android.service.autofill.SaveCallback)}, using
* {@link android.service.autofill.AutoFillService#EXTRA_DATASET_EXTRAS} as the key.
*
* <p>It can be used to keep service state in between calls.
diff --git a/core/java/android/view/autofill/FillResponse.java b/core/java/android/view/autofill/FillResponse.java
index 67eb85a..48dbb84 100644
--- a/core/java/android/view/autofill/FillResponse.java
+++ b/core/java/android/view/autofill/FillResponse.java
@@ -149,9 +149,9 @@
* Dataset.Builder#setExtras(Bundle)} methods to pass {@link Bundle}s with service-specific data use
* to identify this response on future calls (like {@link
* android.service.autofill.AutoFillService#onSaveRequest(android.app.assist.AssistStructure,
- * Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback)}) - such bundles
- * will be available as the {@link android.service.autofill.AutoFillService#EXTRA_RESPONSE_EXTRAS}
- * and {@link android.service.autofill.AutoFillService#EXTRA_DATASET_EXTRAS} extras in that method's
+ * Bundle, android.service.autofill.SaveCallback)}) - such bundles will be available as the
+ * {@link android.service.autofill.AutoFillService#EXTRA_RESPONSE_EXTRAS} and
+ * {@link android.service.autofill.AutoFillService#EXTRA_DATASET_EXTRAS} extras in that method's
* {@code extras} argument.
*/
public final class FillResponse implements Parcelable {
@@ -369,9 +369,8 @@
/**
* Adds ids of additional fields that the service would be interested to save (through
* {@link android.service.autofill.AutoFillService#onSaveRequest(
- * android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal,
- * android.service.autofill.SaveCallback)}) but were not indirectly set through {@link
- * #addDataset(Dataset)}.
+ * android.app.assist.AssistStructure, Bundle, android.service.autofill.SaveCallback)})
+ * but were not indirectly set through {@link #addDataset(Dataset)}.
*
* <p>See {@link FillResponse} for examples.
*/
@@ -386,8 +385,8 @@
* Sets a {@link Bundle} that will be passed to subsequent calls to {@link
* android.service.autofill.AutoFillService} methods such as {@link
* android.service.autofill.AutoFillService#onSaveRequest(
- * android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal,
- * android.service.autofill.SaveCallback)}, using {@link
+ * android.app.assist.AssistStructure, Bundle, android.service.autofill.SaveCallback)},
+ * using {@link
* android.service.autofill.AutoFillService#EXTRA_RESPONSE_EXTRAS} as the key.
*
* <p>It can be used when to keep service state in between calls.
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 072fe4a..c0bec69 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9030,8 +9030,7 @@
final AutoFillManager afm = mContext.getSystemService(AutoFillManager.class);
if (afm != null) {
if (DEBUG_AUTOFILL) {
- Log.v(LOG_TAG, "onFocusChanged(): id=" + getAutoFillViewId() + ", focused= "
- + focused);
+ Log.v(LOG_TAG, "onFocusChanged(false): id=" + getAccessibilityViewId());
}
afm.updateAutoFillInput(this, AutoFillManager.FLAG_UPDATE_UI_HIDE);
}
@@ -10615,7 +10614,7 @@
protected void viewClicked(InputMethodManager imm) {
final AutoFillManager afm = mContext.getSystemService(AutoFillManager.class);
if (afm != null) {
- if (DEBUG_AUTOFILL) Log.v(LOG_TAG, "viewClicked(): id=" + getAutoFillViewId());
+ if (DEBUG_AUTOFILL) Log.v(LOG_TAG, "viewClicked(): id=" + getAccessibilityViewId());
// TODO(b/33197203): integrate with onFocus and/or move to view?
afm.updateAutoFillInput(this, AutoFillManager.FLAG_UPDATE_UI_SHOW);
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index a1eac36..81db93d 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -69,5 +69,6 @@
boolean isBoundWidgetPackage(String packageName, int userId);
boolean requestPinAppWidget(String packageName, in ComponentName providerComponent,
in IntentSender resultIntent);
+ boolean isRequestPinAppWidgetSupported();
}
diff --git a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
index cf14471..11e7102 100644
--- a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
@@ -53,6 +53,11 @@
*/
private static final int SNAP_ONLY_1_1 = 2;
+ /**
+ * 1 snap target: minimized height, (1 - minimized height)
+ */
+ private static final int SNAP_MODE_MINIMIZED = 3;
+
private final float mMinFlingVelocityPxPerSecond;
private final float mMinDismissVelocityPxPerSecond;
private final int mDisplayWidth;
@@ -62,6 +67,7 @@
private final Rect mInsets = new Rect();
private final int mSnapMode;
private final int mMinimalSizeResizableTask;
+ private final int mTaskHeightInMinimizedMode;
private final float mFixedRatio;
private boolean mIsHorizontalDivision;
@@ -93,6 +99,11 @@
public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
boolean isHorizontalDivision, Rect insets) {
+ this(res, displayWidth, displayHeight, dividerSize, isHorizontalDivision, insets, false);
+ }
+
+ public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
+ boolean isHorizontalDivision, Rect insets, boolean isMinimizedMode) {
mMinFlingVelocityPxPerSecond =
MIN_FLING_VELOCITY_DP_PER_SECOND * res.getDisplayMetrics().density;
mMinDismissVelocityPxPerSecond =
@@ -102,12 +113,14 @@
mDisplayHeight = displayHeight;
mIsHorizontalDivision = isHorizontalDivision;
mInsets.set(insets);
- mSnapMode = res.getInteger(
- com.android.internal.R.integer.config_dockedStackDividerSnapMode);
+ mSnapMode = isMinimizedMode ? SNAP_MODE_MINIMIZED :
+ res.getInteger(com.android.internal.R.integer.config_dockedStackDividerSnapMode);
mFixedRatio = res.getFraction(
com.android.internal.R.fraction.docked_stack_divider_fixed_ratio, 1, 1);
mMinimalSizeResizableTask = res.getDimensionPixelSize(
com.android.internal.R.dimen.default_minimal_size_resizable_task);
+ mTaskHeightInMinimizedMode = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.task_height_of_minimized_mode);
calculateTargets(isHorizontalDivision);
mFirstSplitTarget = mTargets.get(1);
mLastSplitTarget = mTargets.get(mTargets.size() - 2);
@@ -246,6 +259,7 @@
int dividerMax = isHorizontalDivision
? mDisplayHeight
: mDisplayWidth;
+ int navBarSize = isHorizontalDivision ? mInsets.bottom : mInsets.right;
mTargets.add(new SnapTarget(-mDividerSize, -mDividerSize, SnapTarget.FLAG_DISMISS_START,
0.35f));
switch (mSnapMode) {
@@ -258,8 +272,10 @@
case SNAP_ONLY_1_1:
addMiddleTarget(isHorizontalDivision);
break;
+ case SNAP_MODE_MINIMIZED:
+ addMinimizedTarget(isHorizontalDivision);
+ break;
}
- int navBarSize = isHorizontalDivision ? mInsets.bottom : mInsets.right;
mTargets.add(new SnapTarget(dividerMax - navBarSize, dividerMax,
SnapTarget.FLAG_DISMISS_END, 0.35f));
}
@@ -315,6 +331,12 @@
mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE));
}
+ private void addMinimizedTarget(boolean isHorizontalDivision) {
+ int position = mTaskHeightInMinimizedMode;
+ position += isHorizontalDivision ? mInsets.top : mInsets.left;
+ mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE));
+ }
+
public SnapTarget getMiddleTarget() {
return mMiddleTarget;
}
diff --git a/core/java/com/android/internal/policy/PipSnapAlgorithm.java b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
index 6d13743..ec92aa9 100644
--- a/core/java/com/android/internal/policy/PipSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
@@ -39,20 +39,27 @@
private static final int SNAP_MODE_CORNERS_AND_SIDES = 1;
// Allows snapping to anywhere along the edge of the screen
private static final int SNAP_MODE_EDGE = 2;
+ // Allows snapping to four corners on a fling towards a corner or slow move near a corner
+ // snaps anywhere along the edge of screen otherwise
+ private static final int SNAP_MODE_CORNERS_AND_EDGES = 3;
// The friction multiplier to control how slippery the PIP is when flung
private static final float SCROLL_FRICTION_MULTIPLIER = 8f;
+ // Threshold to magnet to a corner
+ private static final float CORNER_MAGNET_THRESHOLD = 0.3f;
+
private final Context mContext;
private final ArrayList<Integer> mSnapGravities = new ArrayList<>();
- private final int mDefaultSnapMode = SNAP_MODE_CORNERS_ONLY;
+ private final int mDefaultSnapMode = SNAP_MODE_CORNERS_AND_EDGES;
private int mSnapMode = mDefaultSnapMode;
private Scroller mScroller;
private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
private final int mMinimizedVisibleSize;
+ private boolean mIsMinimized;
public PipSnapAlgorithm(Context context) {
mContext = context;
@@ -70,6 +77,13 @@
}
/**
+ * Sets the PIP's minimized state.
+ */
+ public void setMinimized(boolean isMinimized) {
+ mIsMinimized = isMinimized;
+ }
+
+ /**
* Enables snapping to the closest edge.
*/
public void setSnapToEdge(boolean snapToEdge) {
@@ -107,7 +121,24 @@
movementBounds.right + stackBounds.width(),
movementBounds.bottom + stackBounds.height());
final Rect newBounds = new Rect(stackBounds);
- if (mSnapMode == SNAP_MODE_EDGE) {
+ if (mSnapMode == SNAP_MODE_CORNERS_AND_EDGES) {
+ final Rect tmpBounds = new Rect();
+ final Point[] snapTargets = new Point[mSnapGravities.size()];
+ for (int i = 0; i < mSnapGravities.size(); i++) {
+ Gravity.apply(mSnapGravities.get(i), stackBounds.width(), stackBounds.height(),
+ pipBounds, 0, 0, tmpBounds);
+ snapTargets[i] = new Point(tmpBounds.left, tmpBounds.top);
+ }
+ Point snapTarget = findClosestPoint(stackBounds.left, stackBounds.top, snapTargets);
+ float distance = distanceToPoint(snapTarget, stackBounds.left, stackBounds.top);
+ final float thresh = stackBounds.width() * CORNER_MAGNET_THRESHOLD;
+ if (distance < thresh) {
+ newBounds.offsetTo(snapTarget.x, snapTarget.y);
+ } else {
+ // Otherwise we snap to the edge
+ snapRectToClosestEdge(stackBounds, movementBounds, newBounds);
+ }
+ } else if (mSnapMode == SNAP_MODE_EDGE) {
// Find the closest edge to the given stack bounds and snap to it
snapRectToClosestEdge(stackBounds, movementBounds, newBounds);
} else {
@@ -228,8 +259,7 @@
final int boundedTop = Math.max(movementBounds.top, Math.min(movementBounds.bottom,
stackBounds.top));
boundsOut.set(stackBounds);
- if (stackBounds.left < movementBounds.left ||
- stackBounds.left > movementBounds.right) {
+ if (mIsMinimized) {
boundsOut.offsetTo(boundedLeft, boundsOut.top);
return;
}
@@ -273,6 +303,7 @@
}
// Fall through
case SNAP_MODE_CORNERS_ONLY:
+ case SNAP_MODE_CORNERS_AND_EDGES:
mSnapGravities.add(Gravity.TOP | Gravity.LEFT);
mSnapGravities.add(Gravity.TOP | Gravity.RIGHT);
mSnapGravities.add(Gravity.BOTTOM | Gravity.LEFT);
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index d382f24..723dce6 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -681,7 +681,7 @@
jint smallestScreenWidthDp,
jint screenWidthDp, jint screenHeightDp,
jint screenLayout, jint uiMode,
- jint sdkVersion)
+ jint colorMode, jint sdkVersion)
{
AssetManager* am = assetManagerForJavaObject(env, clazz);
if (am == NULL) {
@@ -712,6 +712,7 @@
config.screenHeightDp = (uint16_t)screenHeightDp;
config.screenLayout = (uint8_t)screenLayout;
config.uiMode = (uint8_t)uiMode;
+ config.colorMode = (uint8_t)colorMode;
config.sdkVersion = (uint16_t)sdkVersion;
config.minorVersion = 0;
@@ -1691,7 +1692,7 @@
{ "getSizeConfigurations", "()[Landroid/content/res/Configuration;",
(void*) android_content_AssetManager_getSizeConfigurations },
// @FastNative
- { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIII)V",
+ { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIIII)V",
(void*) android_content_AssetManager_setConfiguration },
// @FastNative
{ "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index a3fef27..ab3e311 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -98,11 +98,12 @@
// ----------------------------------------------------------------------------
static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
- jstring nameStr, jint w, jint h, jint format, jint flags) {
+ jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject) {
ScopedUtfChars name(env, nameStr);
sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
+ SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject);
sp<SurfaceControl> surface = client->createSurface(
- String8(name.c_str()), w, h, format, flags);
+ String8(name.c_str()), w, h, format, flags, parent);
if (surface == NULL) {
jniThrowException(env, OutOfResourcesException, NULL);
return 0;
@@ -147,8 +148,8 @@
}
Rect sourceCrop = rectFromObj(env, sourceCropObj);
if (allLayers) {
- minLayer = 0;
- maxLayer = -1;
+ minLayer = INT32_MIN;
+ maxLayer = INT32_MAX;
}
sp<GraphicBuffer> buffer;
status_t res = ScreenshotClient::captureToBuffer(displayToken,
@@ -181,8 +182,8 @@
std::unique_ptr<ScreenshotClient> screenshot(new ScreenshotClient());
status_t res;
if (allLayers) {
- minLayer = 0;
- maxLayer = -1;
+ minLayer = INT32_MIN;
+ maxLayer = INT32_MAX;
}
res = screenshot->update(displayToken, sourceCrop, width, height,
@@ -254,8 +255,8 @@
Rect sourceCrop(left, top, right, bottom);
if (allLayers) {
- minLayer = 0;
- maxLayer = -1;
+ minLayer = INT32_MIN;
+ maxLayer = INT32_MAX;
}
ScreenshotClient::capture(displayToken,
consumer->getIGraphicBufferProducer(), sourceCrop,
@@ -741,7 +742,7 @@
// ----------------------------------------------------------------------------
static const JNINativeMethod sSurfaceControlMethods[] = {
- {"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIII)J",
+ {"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIIIJ)J",
(void*)nativeCreate },
{"nativeRelease", "(J)V",
(void*)nativeRelease },
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index e6358a3..4266f88 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -488,6 +488,9 @@
<!-- The default minimal size of a resizable task, in both dimensions. -->
<dimen name="default_minimal_size_resizable_task">220dp</dimen>
+ <!-- Height of a task when in minimized mode from the top when launcher is resizable. -->
+ <dimen name="task_height_of_minimized_mode">80dp</dimen>
+
<!-- Minimum "smallest width" of the display for cascading menus to be enabled. -->
<dimen name="cascading_menus_min_smallest_width">720dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a732998..0626df3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -504,7 +504,6 @@
<java-symbol type="string" name="NetworkPreferenceSwitchTitle" />
<java-symbol type="string" name="SetupCallDefault" />
<java-symbol type="string" name="accept" />
- <java-symbol type="string" name="accessibility_enabled" />
<java-symbol type="string" name="activity_chooser_view_see_all" />
<java-symbol type="string" name="activitychooserview_choose_application" />
<java-symbol type="string" name="activitychooserview_choose_application_error" />
@@ -601,7 +600,6 @@
<java-symbol type="string" name="contentServiceSync" />
<java-symbol type="string" name="contentServiceSyncNotificationTitle" />
<java-symbol type="string" name="contentServiceTooManyDeletesNotificationDesc" />
- <java-symbol type="string" name="continue_to_enable_accessibility" />
<java-symbol type="string" name="date_and_time" />
<java-symbol type="string" name="date_picker_decrement_day_button" />
<java-symbol type="string" name="date_picker_decrement_month_button" />
@@ -648,7 +646,6 @@
<java-symbol type="string" name="widget_default_class_name" />
<java-symbol type="string" name="emergency_calls_only" />
<java-symbol type="array" name="config_ephemeralResolverPackage" />
- <java-symbol type="string" name="enable_accessibility_canceled" />
<java-symbol type="string" name="eventTypeAnniversary" />
<java-symbol type="string" name="eventTypeBirthday" />
<java-symbol type="string" name="eventTypeCustom" />
@@ -1777,6 +1774,7 @@
<java-symbol type="id" name="replace_message" />
<java-symbol type="fraction" name="config_dimBehindFadeDuration" />
<java-symbol type="dimen" name="default_minimal_size_resizable_task" />
+ <java-symbol type="dimen" name="task_height_of_minimized_mode" />
<java-symbol type="fraction" name="config_screenAutoBrightnessDozeScaleFactor" />
<java-symbol type="fraction" name="config_autoBrightnessAdjustmentMaxGamma" />
<java-symbol type="integer" name="config_autoBrightnessAmbientLightHorizon"/>
diff --git a/core/tests/coretests/src/android/metrics/LogMakerTest.java b/core/tests/coretests/src/android/metrics/LogMakerTest.java
index 35d8d93..b0c394e 100644
--- a/core/tests/coretests/src/android/metrics/LogMakerTest.java
+++ b/core/tests/coretests/src/android/metrics/LogMakerTest.java
@@ -115,6 +115,13 @@
assertEquals(10, out[1]);
}
+ public void testClearData() {
+ LogMaker builder = new LogMaker(0);
+ builder.addTaggedData(1, "onetwothree");
+ builder.clearTaggedData(1);
+ assertEquals(null, builder.getTaggedData(1));
+ }
+
public void testGiantLogOmitted() {
LogMaker badBuilder = new LogMaker(0);
StringBuilder b = new StringBuilder();
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 8f7787b..138a5ef 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -7,7 +7,7 @@
# Enables fine-grained GLES error checking
# If set to true, every GLES call is wrapped & error checked
# Has moderate overhead
-HWUI_ENABLE_OPENGL_VALIDATION := false
+HWUI_ENABLE_OPENGL_VALIDATION := true
hwui_src_files := \
hwui/Bitmap.cpp \
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 44f31526..9a08fbe 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -1088,6 +1088,166 @@
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program";
/**
+ * The program type for movie.
+ *
+ * @see #COLUMN_TYPE
+ */
+ public static final String TYPE_MOVIE = "TYPE_MOVIE";
+
+ /**
+ * The program type for TV series.
+ *
+ * @see #COLUMN_TYPE
+ */
+ public static final String TYPE_TV_SERIES = "TYPE_TV_SERIES";
+
+ /**
+ * The program type for TV season.
+ *
+ * @see #COLUMN_TYPE
+ */
+ public static final String TYPE_TV_SEASON = "TYPE_TV_SEASON";
+
+ /**
+ * The program type for TV episode.
+ *
+ * @see #COLUMN_TYPE
+ */
+ public static final String TYPE_TV_EPISODE = "TYPE_TV_EPISODE";
+
+ /**
+ * The program type for clip.
+ *
+ * @see #COLUMN_TYPE
+ */
+ public static final String TYPE_CLIP = "TYPE_CLIP";
+
+ /**
+ * The program type for event.
+ *
+ * @see #COLUMN_TYPE
+ */
+ public static final String TYPE_EVENT = "TYPE_EVENT";
+
+ /**
+ * The program type for channel.
+ *
+ * @see #COLUMN_TYPE
+ */
+ public static final String TYPE_CHANNEL = "TYPE_CHANNEL";
+
+ /**
+ * The program type for track.
+ *
+ * @see #COLUMN_TYPE
+ */
+ public static final String TYPE_TRACK = "TYPE_TRACK";
+
+ /**
+ * The program type for album.
+ *
+ * @see #COLUMN_TYPE
+ */
+ public static final String TYPE_ALBUM = "TYPE_ALBUM";
+
+ /**
+ * The program type for artist.
+ *
+ * @see #COLUMN_TYPE
+ */
+ public static final String TYPE_ARTIST = "TYPE_ARTIST";
+
+ /**
+ * The program type for playlist.
+ *
+ * @see #COLUMN_TYPE
+ */
+ public static final String TYPE_PLAYLIST = "TYPE_PLAYLIST";
+
+ /**
+ * The program type for station.
+ *
+ * @see #COLUMN_TYPE
+ */
+ public static final String TYPE_STATION = "TYPE_STATION";
+
+ /**
+ * The watch next type for CONTINUE.
+ *
+ * @see #COLUMN_WATCH_NEXT_TYPE
+ */
+ public static final String WATCH_NEXT_TYPE_CONTINUE = "WATCH_NEXT_TYPE_CONTINUE";
+
+ /**
+ * The watch next type for NEXT.
+ *
+ * @see #COLUMN_WATCH_NEXT_TYPE
+ */
+ public static final String WATCH_NEXT_TYPE_NEXT = "WATCH_NEXT_TYPE_NEXT";
+
+ /**
+ * The watch next type for NEW.
+ *
+ * @see #COLUMN_WATCH_NEXT_TYPE
+ */
+ public static final String WATCH_NEXT_TYPE_NEW = "WATCH_NEXT_TYPE_NEW";
+
+ /**
+ * The aspect ratio for 16:9.
+ *
+ * @see #COLUMN_POSTER_ART_ASPECT_RATIO
+ * @see #COLUMN_THUMBNAIL_ASPECT_RATIO
+ */
+ public static final String ASPECT_RATIO_16_9 = "ASPECT_RATIO_16_9";
+
+ /**
+ * The aspect ratio for 3:2.
+ *
+ * @see #COLUMN_POSTER_ART_ASPECT_RATIO
+ * @see #COLUMN_THUMBNAIL_ASPECT_RATIO
+ */
+ public static final String ASPECT_RATIO_3_2 = "ASPECT_RATIO_3_2";
+
+ /**
+ * The aspect ratio for 1:1.
+ *
+ * @see #COLUMN_POSTER_ART_ASPECT_RATIO
+ * @see #COLUMN_THUMBNAIL_ASPECT_RATIO
+ */
+ public static final String ASPECT_RATIO_1_1 = "ASPECT_RATIO_1_1";
+
+ /**
+ * The aspect ratio for 2:3.
+ *
+ * @see #COLUMN_POSTER_ART_ASPECT_RATIO
+ * @see #COLUMN_THUMBNAIL_ASPECT_RATIO
+ */
+ public static final String ASPECT_RATIO_2_3 = "ASPECT_RATIO_2_3";
+
+ /**
+ * The availability for "available to this user".
+ *
+ * @see #COLUMN_AVAILABILITY
+ */
+ public static final String AVAILABILITY_AVAILABLE = "AVAILABILITY_AVAILABLE";
+
+ /**
+ * The availability for "free with subscription".
+ *
+ * @see #COLUMN_AVAILABILITY
+ */
+ public static final String AVAILABILITY_FREE_WITH_SUBSCRIPTION =
+ "AVAILABILITY_FREE_WITH_SUBSCRIPTION";
+
+ /**
+ * The availability for "paid content, either to-own or rental
+ * (user has not purchased/rented).
+ *
+ * @see #COLUMN_AVAILABILITY
+ */
+ public static final String AVAILABILITY_PAID_CONTENT = "AVAILABILITY_PAID_CONTENT";
+
+ /**
* The interaction type for "listens".
*
* @see #COLUMN_INTERACTION_TYPE
@@ -1148,8 +1308,8 @@
*
* @see #COLUMN_REVIEW_RATING_STYLE
*/
- public static final String REVIEW_RATING_STYLE_THUMPS_UP_DOWN =
- "REVIEW_RATING_STYLE_THUMPS_UP_DOWN";
+ public static final String REVIEW_RATING_STYLE_THUMBS_UP_DOWN =
+ "REVIEW_RATING_STYLE_THUMBS_UP_DOWN";
/**
* The review rating style for 0 to 100 point system.
@@ -1171,6 +1331,44 @@
public static final String COLUMN_CHANNEL_ID = "channel_id";
/**
+ * The type of this program content.
+ *
+ * <p>The value should match one of the followings:
+ * {@link #TYPE_MOVIE},
+ * {@link #TYPE_TV_SERIES},
+ * {@link #TYPE_TV_SEASON},
+ * {@link #TYPE_TV_EPISODE},
+ * {@link #TYPE_CLIP},
+ * {@link #TYPE_EVENT},
+ * {@link #TYPE_CHANNEL},
+ * {@link #TYPE_TRACK},
+ * {@link #TYPE_ALBUM},
+ * {@link #TYPE_ARTIST},
+ * {@link #TYPE_PLAYLIST}, and
+ * {@link #TYPE_STATION}.
+ *
+ * <p>This is a required field if the program is from a {@link Channels#TYPE_PREVIEW}
+ * channel.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_TYPE = "type";
+
+ /**
+ * The "watch next" type of this program content.
+ *
+ * <p>The value should match one of the followings:
+ * {@link #WATCH_NEXT_TYPE_CONTINUE},
+ * {@link #WATCH_NEXT_TYPE_NEXT}, and
+ * {@link #WATCH_NEXT_TYPE_NEW}.
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_WATCH_NEXT_TYPE = "watch_next_type";
+
+ /**
* The title of this TV program.
*
* <p>If this program is an episodic TV show, it is recommended that the title is the series
@@ -1401,6 +1599,19 @@
public static final String COLUMN_POSTER_ART_URI = "poster_art_uri";
/**
+ * The aspect ratio of the poster art for this TV program.
+ *
+ * <p>The value should match one of the followings:
+ * {@link #ASPECT_RATIO_16_9},
+ * {@link #ASPECT_RATIO_3_2},
+ * {@link #ASPECT_RATIO_1_1}, and
+ * {@link #ASPECT_RATIO_2_3}.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio";
+
+ /**
* The URI for the thumbnail of this TV program.
*
* <p>The system can generate a thumbnail from the poster art if this column is not
@@ -1423,6 +1634,104 @@
public static final String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
/**
+ * The aspect ratio of the thumbnail for this TV program.
+ *
+ * <p>The value should match one of the followings:
+ * {@link #ASPECT_RATIO_16_9},
+ * {@link #ASPECT_RATIO_3_2},
+ * {@link #ASPECT_RATIO_1_1}, and
+ * {@link #ASPECT_RATIO_2_3}.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio";
+
+ /**
+ * The URI for the logo of this TV program.
+ *
+ * <p>This is a small badge shown on top of the poster art or thumbnail representing the
+ * source of the content.
+ *
+ * <p>The data in the column must be a URL, or a URI in one of the following formats:
+ *
+ * <ul>
+ * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
+ * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
+ * </li>
+ * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
+ * </ul>
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_LOGO = "logo";
+
+ /**
+ * The availability of this TV program.
+ *
+ * <p>The value should match one of the followings:
+ * {@link #AVAILABILITY_AVAILABLE},
+ * {@link #AVAILABILITY_FREE_WITH_SUBSCRIPTION}, and
+ * {@link #AVAILABILITY_PAID_CONTENT}.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_AVAILABILITY = "availability";
+
+ /**
+ * The starting price of this TV program.
+ *
+ * <p>This indicates the lowest regular acquisition cost of the content. It is only used
+ * if the availability of the program is {@link #AVAILABILITY_PAID_CONTENT}.
+ *
+ * <p>Type: TEXT
+ * @see #COLUMN_OFFER_PRICE
+ */
+ public static final String COLUMN_STARTING_PRICE = "starting_price";
+
+ /**
+ * The offer price of this TV program.
+ *
+ * <p>This is the promotional cost of the content. It is only used if the availability of
+ * the program is {@link #AVAILABILITY_PAID_CONTENT}.
+ *
+ * <p>Type: TEXT
+ * @see #COLUMN_STARTING_PRICE
+ */
+ public static final String COLUMN_OFFER_PRICE = "offer_price";
+
+ /**
+ * The release date of this TV program.
+ *
+ * <p>The value should be in the form of either "yyyy-MM-dd" or "yyyy".
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_RELEASE_DATE = "release_date";
+
+ /**
+ * The count of the items included in this TV program.
+ *
+ * <p>This is only relevant if the program represents a collection of items such as series,
+ * episodes, or music tracks.
+ *
+ * <p>Type: INTEGER
+ */
+ public static final String COLUMN_ITEM_COUNT = "item_count";
+
+ /**
+ * The flag indicating whether this TV program is live or not.
+ *
+ * <p>A value of 1 indicates that the content is airing and should be consumed now, a value
+ * of 0 indicates that the content is off the air and does not need to be consumed at the
+ * present time. If not specified, the value is set to 0 (not live) by default.
+ *
+ * <p>Type: INTEGER (boolean)
+ */
+ public static final String COLUMN_LIVE = "live";
+
+ /**
* The flag indicating whether this TV program is searchable or not.
*
* <p>The columns of searchable programs can be read by other applications that have proper
@@ -1645,7 +1954,7 @@
* The review rating score style used for {@link #COLUMN_REVIEW_RATING}.
*
* <p> The value should match one of the followings: {@link #REVIEW_RATING_STYLE_STARS},
- * {@link #REVIEW_RATING_STYLE_THUMPS_UP_DOWN}, and {@link #REVIEW_RATING_STYLE_PERCENTAGE}.
+ * {@link #REVIEW_RATING_STYLE_THUMBS_UP_DOWN}, and {@link #REVIEW_RATING_STYLE_PERCENTAGE}.
*
* <p>Type: TEXT
* @see #COLUMN_REVIEW_RATING
@@ -1657,7 +1966,7 @@
*
* <p>The format of the value is dependent on {@link #COLUMN_REVIEW_RATING_STYLE}. If the
* style is {@link #REVIEW_RATING_STYLE_STARS}, the value should be a real number between
- * 0.0 and 5.0. (e.g. "4.5") If the style is {@link #REVIEW_RATING_STYLE_THUMPS_UP_DOWN},
+ * 0.0 and 5.0. (e.g. "4.5") If the style is {@link #REVIEW_RATING_STYLE_THUMBS_UP_DOWN},
* the value should be two integers, one for thumbs-up count and the other for thumbs-down
* count, with a comma between them. (e.g. "200,40") If the style is
* {@link #REVIEW_RATING_STYLE_PERCENTAGE}, the value shoule be a real number between 0 and
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index e0157a6..7bbca5e 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -103,10 +103,11 @@
<!-- Bluetooth settings -->
- <!-- Titles for Bluetooth Audio Codec selection preference. [CHAR LIMIT=40] -->
+ <!-- Titles for Bluetooth Audio Codec selection preference. [CHAR LIMIT=50] -->
<string-array name="bluetooth_a2dp_codec_titles">
<item>Use System Selection (Default)</item>
<item>SBC</item>
+ <item>AAC</item>
<item>aptX</item>
<item>aptX HD</item>
<item>LDAC</item>
@@ -119,18 +120,20 @@
<item>1</item>
<item>2</item>
<item>3</item>
+ <item>4</item>
</string-array>
- <!-- Summaries for Bluetooth Audio Codec selection preference. [CHAR LIMIT=40]-->
+ <!-- Summaries for Bluetooth Audio Codec selection preference. [CHAR LIMIT=50]-->
<string-array name="bluetooth_a2dp_codec_summaries" >
<item>Use System Selection (Default)</item>
<item>SBC</item>
+ <item>AAC</item>
<item>aptX</item>
<item>aptX HD</item>
<item>LDAC</item>
</string-array>
- <!-- Titles for Bluetooth Audio Codec Sample Rate selection preference. [CHAR LIMIT=40] -->
+ <!-- Titles for Bluetooth Audio Codec Sample Rate selection preference. [CHAR LIMIT=50] -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item>Use System Selection (Default)</item>
<item>44.1 kHz</item>
@@ -148,7 +151,7 @@
<item>8</item>
</string-array>
- <!-- Summaries for Bluetooth Audio Codec Sample Rate selection preference. [CHAR LIMIT=40]-->
+ <!-- Summaries for Bluetooth Audio Codec Sample Rate selection preference. [CHAR LIMIT=50]-->
<string-array name="bluetooth_a2dp_codec_sample_rate_summaries" >
<item>Use System Selection (Default)</item>
<item>44.1 kHz</item>
@@ -157,7 +160,7 @@
<item>96.0 kHz</item>
</string-array>
- <!-- Titles for Bluetooth Audio Codec Bits Per Sample selection preference. [CHAR LIMIT=40] -->
+ <!-- Titles for Bluetooth Audio Codec Bits Per Sample selection preference. [CHAR LIMIT=50] -->
<string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
<item>Use System Selection (Default)</item>
<item>16 bits/sample</item>
@@ -173,7 +176,7 @@
<item>4</item>
</string-array>
- <!-- Summaries for Bluetooth Audio Codec Bits Per Sample selection preference. [CHAR LIMIT=40]-->
+ <!-- Summaries for Bluetooth Audio Codec Bits Per Sample selection preference. [CHAR LIMIT=50]-->
<string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries" >
<item>Use System Selection (Default)</item>
<item>16 bits/sample</item>
@@ -181,7 +184,7 @@
<item>32 bits/sample</item>
</string-array>
- <!-- Titles for Bluetooth Audio Codec Channel Mode selection preference. [CHAR LIMIT=40] -->
+ <!-- Titles for Bluetooth Audio Codec Channel Mode selection preference. [CHAR LIMIT=50] -->
<string-array name="bluetooth_a2dp_codec_channel_mode_titles">
<item>Use System Selection (Default)</item>
<item>Mono</item>
@@ -195,7 +198,7 @@
<item>2</item>
</string-array>
- <!-- Summaries for Bluetooth Audio Codec Channel Mode selection preference. [CHAR LIMIT=40]-->
+ <!-- Summaries for Bluetooth Audio Codec Channel Mode selection preference. [CHAR LIMIT=50]-->
<string-array name="bluetooth_a2dp_codec_channel_mode_summaries" >
<item>Use System Selection (Default)</item>
<item>Mono</item>
diff --git a/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java b/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
index b037a3da..fc697ce 100644
--- a/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
@@ -198,7 +198,7 @@
intent.putExtra(feedbackIntentExtraKey, packageNameKey);
intent.putExtra(feedbackIntentNameKey, packageNameValue);
}
- intent.putExtra(EXTRA_THEME, 1 /* Light, dark action bar */);
+ intent.putExtra(EXTRA_THEME, 0 /* Light theme */);
TypedArray array = context.obtainStyledAttributes(new int[]{android.R.attr.colorPrimary});
intent.putExtra(EXTRA_PRIMARY_COLOR, array.getColor(0, 0));
array.recycle();
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 799f388..c617994 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -16,9 +16,11 @@
package com.android.settingslib.wifi;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.database.ContentObserver;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
@@ -38,6 +40,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.provider.Settings;
import android.support.annotation.WorkerThread;
import android.util.ArraySet;
import android.util.Log;
@@ -136,6 +139,8 @@
private final NetworkScoreManager mNetworkScoreManager;
private final WifiNetworkScoreCache mScoreCache;
private final Set<NetworkKey> mRequestedScores = new ArraySet<>();
+ private boolean mNetworkScoringUiEnabled;
+ private final ContentObserver mObserver;
@VisibleForTesting
Scanner mScanner;
@@ -215,6 +220,16 @@
Message.obtain(mWorkHandler, WorkHandler.MSG_UPDATE_NETWORK_SCORES).sendToTarget();
}
});
+
+ mObserver = new ContentObserver(mWorkHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ mNetworkScoringUiEnabled =
+ Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.NETWORK_SCORING_UI_ENABLED, 0) == 1;
+ }
+ };
}
/**
@@ -274,6 +289,11 @@
}
});
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.NETWORK_SCORING_UI_ENABLED),
+ false /* notifyForDescendants */,
+ mObserver);
+ mObserver.onChange(false /* selfChange */); // Set the initial value for mScoringUiEnabled
resumeScanning();
if (!mRegistered) {
@@ -327,6 +347,7 @@
unregisterAndClearScoreCache();
}
});
+ mContext.getContentResolver().unregisterContentObserver(mObserver);
}
@WorkerThread
@@ -537,10 +558,11 @@
}
}
-
- requestScoresForNetworkKeys(scoresToRequest);
- for (AccessPoint ap : accessPoints) {
- ap.updateScores(mScoreCache);
+ if (mNetworkScoringUiEnabled) {
+ requestScoresForNetworkKeys(scoresToRequest);
+ for (AccessPoint ap : accessPoints) {
+ ap.updateScores(mScoreCache);
+ }
}
// Pre-sort accessPoints to speed preference insertion
@@ -641,7 +663,7 @@
if (ap.update(connectionConfig, mLastInfo, mLastNetworkInfo)) {
reorder = true;
}
- if (ap.updateScores(mScoreCache)) {
+ if (mNetworkScoringUiEnabled && ap.updateScores(mScoreCache)) {
reorder = true;
}
}
@@ -657,6 +679,10 @@
* <p>Will trigger a resort and notify listeners of changes if applicable.
*/
private void updateNetworkScores() {
+ if (!mNetworkScoringUiEnabled) {
+ return;
+ }
+
// Lock required to prevent accidental copying of AccessPoint states while the modification
// is in progress. see #copyAndNotifyListeners
long before = System.currentTimeMillis();
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index eaf0367..08736c7 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -110,7 +110,7 @@
private HandlerThread mWorkerThread;
private Looper mLooper;
private Looper mMainLooper;
- private int mOriginalSettingValue;
+ private int mOriginalScoringUiSettingValue;
@Before
public void setUp() {
@@ -175,19 +175,23 @@
}
}).when(mockWifiListener).onAccessPointsChanged();
- mOriginalSettingValue = Settings.Global.getInt(
- InstrumentationRegistry.getTargetContext().getContentResolver(),
- Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED,
- 0 /* disabled */);
-
+ // Turn on Scoring UI features
+ mOriginalScoringUiSettingValue = Settings.Global.getInt(
+ InstrumentationRegistry.getTargetContext().getContentResolver(),
+ Settings.Global.NETWORK_SCORING_UI_ENABLED,
+ 0 /* disabled */);
+ Settings.Global.putInt(
+ InstrumentationRegistry.getTargetContext().getContentResolver(),
+ Settings.Global.NETWORK_SCORING_UI_ENABLED,
+ 1 /* enabled */);
}
@After
public void cleanUp() {
Settings.Global.putInt(
- InstrumentationRegistry.getTargetContext().getContentResolver(),
- Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED,
- mOriginalSettingValue);
+ InstrumentationRegistry.getTargetContext().getContentResolver(),
+ Settings.Global.NETWORK_SCORING_UI_ENABLED,
+ mOriginalScoringUiSettingValue);
}
private static ScanResult buildScanResult1() {
@@ -333,9 +337,18 @@
WifiNetworkScoreCache scoreCache = mScoreCacheCaptor.getValue();
+ CountDownLatch latch = new CountDownLatch(1);
+ doAnswer(
+ (invocation) -> {
+ latch.countDown();
+ return null;
+ }).when(mockNetworkScoreManager)
+ .unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, scoreCache);
+
// Test unregister
tracker.stopTracking();
+ latch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
verify(mockNetworkScoreManager)
.unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, scoreCache);
}
@@ -385,7 +398,28 @@
assertTrue(aps.size() == 2);
assertEquals(aps.get(0).getSsidStr(), SSID_2);
assertEquals(aps.get(1).getSsidStr(), SSID_1);
+ }
+ @Test
+ public void scoreCacheUpdateScoresShouldNotChangeSortOrderWhenSortingDisabled()
+ throws InterruptedException {
+ Settings.Global.putInt(
+ InstrumentationRegistry.getTargetContext().getContentResolver(),
+ Settings.Global.NETWORK_SCORING_UI_ENABLED,
+ 0 /* disabled */);
+
+ WifiTracker tracker = createTrackerAndInjectInitialScanResults();
+ List<AccessPoint> aps = tracker.getAccessPoints();
+ assertTrue(aps.size() == 2);
+ assertEquals(aps.get(0).getSsidStr(), SSID_1);
+ assertEquals(aps.get(1).getSsidStr(), SSID_2);
+
+ updateScoresAndWaitForAccessPointsChangedCallback();
+
+ aps = tracker.getAccessPoints();
+ assertTrue(aps.size() == 2);
+ assertEquals(aps.get(0).getSsidStr(), SSID_1);
+ assertEquals(aps.get(1).getSsidStr(), SSID_2);
}
@Test
@@ -405,6 +439,28 @@
}
@Test
+ public void noBadgesShouldBeInsertedIntoAccessPointWhenScoringUiDisabled()
+ throws InterruptedException {
+ Settings.Global.putInt(
+ InstrumentationRegistry.getTargetContext().getContentResolver(),
+ Settings.Global.NETWORK_SCORING_UI_ENABLED,
+ 0 /* disabled */);
+
+ WifiTracker tracker = createTrackerAndInjectInitialScanResults();
+ updateScoresAndWaitForAccessPointsChangedCallback();
+
+ List<AccessPoint> aps = tracker.getAccessPoints();
+
+ for (AccessPoint ap : aps) {
+ if (ap.getSsidStr().equals(SSID_1)) {
+ assertEquals(ScoredNetwork.BADGING_NONE, ap.getBadge());
+ } else if (ap.getSsidStr().equals(SSID_2)) {
+ assertEquals(ScoredNetwork.BADGING_NONE, ap.getBadge());
+ }
+ }
+ }
+
+ @Test
public void scoresShouldBeRequestedForNewScanResultOnly() throws InterruptedException {
mRequestScoresLatch = new CountDownLatch(2);
WifiTracker tracker = createTrackerAndInjectInitialScanResults();
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 25e1f16..7a9ba20 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -182,6 +182,18 @@
private static final Bundle NULL_SETTING_BUNDLE = Bundle.forPair(
Settings.NameValueTable.VALUE, null);
+ // Changes to these global settings are synchronously persisted
+ private static final Set<String> CRITICAL_GLOBAL_SETTINGS = new ArraySet<>();
+ static {
+ CRITICAL_GLOBAL_SETTINGS.add(Settings.Global.DEVICE_PROVISIONED);
+ }
+
+ // Changes to these secure settings are synchronously persisted
+ private static final Set<String> CRITICAL_SECURE_SETTINGS = new ArraySet<>();
+ static {
+ CRITICAL_SECURE_SETTINGS.add(Settings.Secure.USER_SETUP_COMPLETE);
+ }
+
// Per user secure settings that moved to the for all users global settings.
static final Set<String> sSecureMovedToGlobalSettings = new ArraySet<>();
static {
@@ -949,18 +961,18 @@
case MUTATION_OPERATION_INSERT: {
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_GLOBAL,
UserHandle.USER_SYSTEM, name, value, tag, makeDefault,
- getCallingPackage(), forceNotify);
+ getCallingPackage(), forceNotify, CRITICAL_GLOBAL_SETTINGS);
}
case MUTATION_OPERATION_DELETE: {
return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_GLOBAL,
- UserHandle.USER_SYSTEM, name, forceNotify);
+ UserHandle.USER_SYSTEM, name, forceNotify, CRITICAL_GLOBAL_SETTINGS);
}
case MUTATION_OPERATION_UPDATE: {
return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_GLOBAL,
UserHandle.USER_SYSTEM, name, value, tag, makeDefault,
- getCallingPackage(), forceNotify);
+ getCallingPackage(), forceNotify, CRITICAL_GLOBAL_SETTINGS);
}
case MUTATION_OPERATION_RESET: {
@@ -1156,18 +1168,18 @@
case MUTATION_OPERATION_INSERT: {
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SECURE,
owningUserId, name, value, tag, makeDefault,
- getCallingPackage(), forceNotify);
+ getCallingPackage(), forceNotify, CRITICAL_SECURE_SETTINGS);
}
case MUTATION_OPERATION_DELETE: {
return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SECURE,
- owningUserId, name, forceNotify);
+ owningUserId, name, forceNotify, CRITICAL_SECURE_SETTINGS);
}
case MUTATION_OPERATION_UPDATE: {
return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SECURE,
owningUserId, name, value, tag, makeDefault,
- getCallingPackage(), forceNotify);
+ getCallingPackage(), forceNotify, CRITICAL_SECURE_SETTINGS);
}
case MUTATION_OPERATION_RESET: {
@@ -1304,18 +1316,20 @@
case MUTATION_OPERATION_INSERT: {
validateSystemSettingValue(name, value);
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SYSTEM,
- owningUserId, name, value, null, false, getCallingPackage(), false);
+ owningUserId, name, value, null, false, getCallingPackage(),
+ false, null);
}
case MUTATION_OPERATION_DELETE: {
return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SYSTEM,
- owningUserId, name, false);
+ owningUserId, name, false, null);
}
case MUTATION_OPERATION_UPDATE: {
validateSystemSettingValue(name, value);
return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SYSTEM,
- owningUserId, name, value, null, false, getCallingPackage(), false);
+ owningUserId, name, value, null, false, getCallingPackage(),
+ false, null);
}
}
@@ -1689,7 +1703,7 @@
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SECURE,
owningUserId, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, newProviders,
- tag, makeDefault, getCallingPackage(), forceNotify);
+ tag, makeDefault, getCallingPackage(), forceNotify, CRITICAL_SECURE_SETTINGS);
}
private static void warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk(
@@ -2234,7 +2248,8 @@
}
public boolean insertSettingLocked(int type, int userId, String name, String value,
- String tag, boolean makeDefault, String packageName, boolean forceNotify) {
+ String tag, boolean makeDefault, String packageName, boolean forceNotify,
+ Set<String> criticalSettings) {
final int key = makeKey(type, userId);
boolean success = false;
@@ -2244,13 +2259,18 @@
tag, makeDefault, packageName);
}
+ if (success && criticalSettings != null && criticalSettings.contains(name)) {
+ settingsState.persistSyncLocked();
+ }
+
if (forceNotify || success) {
notifyForSettingsChange(key, name);
}
return success;
}
- public boolean deleteSettingLocked(int type, int userId, String name, boolean forceNotify) {
+ public boolean deleteSettingLocked(int type, int userId, String name, boolean forceNotify,
+ Set<String> criticalSettings) {
final int key = makeKey(type, userId);
boolean success = false;
@@ -2259,12 +2279,39 @@
success = settingsState.deleteSettingLocked(name);
}
+ if (success && criticalSettings != null && criticalSettings.contains(name)) {
+ settingsState.persistSyncLocked();
+ }
+
if (forceNotify || success) {
notifyForSettingsChange(key, name);
}
return success;
}
+ public boolean updateSettingLocked(int type, int userId, String name, String value,
+ String tag, boolean makeDefault, String packageName, boolean forceNotify,
+ Set<String> criticalSettings) {
+ final int key = makeKey(type, userId);
+
+ boolean success = false;
+ SettingsState settingsState = peekSettingsStateLocked(key);
+ if (settingsState != null) {
+ success = settingsState.updateSettingLocked(name, value, tag,
+ makeDefault, packageName);
+ }
+
+ if (success && criticalSettings != null && criticalSettings.contains(name)) {
+ settingsState.persistSyncLocked();
+ }
+
+ if (forceNotify || success) {
+ notifyForSettingsChange(key, name);
+ }
+
+ return success;
+ }
+
public Setting getSettingLocked(int type, int userId, String name) {
final int key = makeKey(type, userId);
@@ -2277,24 +2324,6 @@
return settingsState.getSettingLocked(name);
}
- public boolean updateSettingLocked(int type, int userId, String name, String value,
- String tag, boolean makeDefault, String packageName, boolean forceNotify) {
- final int key = makeKey(type, userId);
-
- boolean success = false;
- SettingsState settingsState = peekSettingsStateLocked(key);
- if (settingsState != null) {
- success = settingsState.updateSettingLocked(name, value, tag,
- makeDefault, packageName);
- }
-
- if (forceNotify || success) {
- notifyForSettingsChange(key, name);
- }
-
- return success;
- }
-
public void resetSettingsLocked(int type, int userId, String packageName, int mode,
String tag) {
final int key = makeKey(type, userId);
@@ -2306,56 +2335,78 @@
switch (mode) {
case Settings.RESET_MODE_PACKAGE_DEFAULTS: {
for (String name : settingsState.getSettingNamesLocked()) {
+ boolean someSettingChanged = false;
Setting setting = settingsState.getSettingLocked(name);
if (packageName.equals(setting.getPackageName())) {
if (tag != null && !tag.equals(setting.getTag())) {
continue;
}
- if (settingsState.resetSettingLocked(name, packageName)) {
+ if (settingsState.resetSettingLocked(name)) {
+ someSettingChanged = true;
notifyForSettingsChange(key, name);
}
}
+ if (someSettingChanged) {
+ settingsState.persistSyncLocked();
+ }
}
} break;
case Settings.RESET_MODE_UNTRUSTED_DEFAULTS: {
for (String name : settingsState.getSettingNamesLocked()) {
+ boolean someSettingChanged = false;
Setting setting = settingsState.getSettingLocked(name);
if (!SettingsState.isSystemPackage(getContext(),
setting.getPackageName())) {
- if (settingsState.resetSettingLocked(name, packageName)) {
+ if (settingsState.resetSettingLocked(name)) {
+ someSettingChanged = true;
notifyForSettingsChange(key, name);
}
}
+ if (someSettingChanged) {
+ settingsState.persistSyncLocked();
+ }
}
} break;
case Settings.RESET_MODE_UNTRUSTED_CHANGES: {
for (String name : settingsState.getSettingNamesLocked()) {
+ boolean someSettingChanged = false;
Setting setting = settingsState.getSettingLocked(name);
if (!SettingsState.isSystemPackage(getContext(),
setting.getPackageName())) {
if (setting.isDefaultFromSystem()) {
- if (settingsState.resetSettingLocked(name, packageName)) {
+ if (settingsState.resetSettingLocked(name)) {
+ someSettingChanged = true;
notifyForSettingsChange(key, name);
}
} else if (settingsState.deleteSettingLocked(name)) {
+ someSettingChanged = true;
notifyForSettingsChange(key, name);
}
}
+ if (someSettingChanged) {
+ settingsState.persistSyncLocked();
+ }
}
} break;
case Settings.RESET_MODE_TRUSTED_DEFAULTS: {
for (String name : settingsState.getSettingNamesLocked()) {
Setting setting = settingsState.getSettingLocked(name);
+ boolean someSettingChanged = false;
if (setting.isDefaultFromSystem()) {
- if (settingsState.resetSettingLocked(name, packageName)) {
+ if (settingsState.resetSettingLocked(name)) {
+ someSettingChanged = true;
notifyForSettingsChange(key, name);
}
} else if (settingsState.deleteSettingLocked(name)) {
+ someSettingChanged = true;
notifyForSettingsChange(key, name);
}
+ if (someSettingChanged) {
+ settingsState.persistSyncLocked();
+ }
}
} break;
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
index 2d59324..a6fadf9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
@@ -113,7 +113,7 @@
String mKey = null;
String mValue = null;
String mPackageName = null;
- String mToken = null;
+ String mTag = null;
int mResetMode = -1;
boolean mMakeDefault;
@@ -185,7 +185,7 @@
if (peekNextArg() == null) {
valid = true;
} else {
- mToken = getNextArg();
+ mTag = getNextArg();
if (peekNextArg() == null) {
valid = true;
} else {
@@ -218,10 +218,10 @@
// what we have so far is a valid command
valid = true;
// keep going; there may be another PUT arg
- } else if (mToken == null) {
- mToken = arg;
- if ("default".equalsIgnoreCase(mToken)) {
- mToken = null;
+ } else if (mTag == null) {
+ mTag = arg;
+ if ("default".equalsIgnoreCase(mTag)) {
+ mTag = null;
mMakeDefault = true;
if (peekNextArg() == null) {
valid = true;
@@ -282,7 +282,7 @@
pout.println(getForUser(iprovider, mUser, mTable, mKey));
break;
case PUT:
- putForUser(iprovider, mUser, mTable, mKey, mValue, mToken, mMakeDefault);
+ putForUser(iprovider, mUser, mTable, mKey, mValue, mTag, mMakeDefault);
break;
case DELETE:
pout.println("Deleted "
@@ -294,7 +294,7 @@
}
break;
case RESET:
- resetForUser(iprovider, mUser, mTable, mToken);
+ resetForUser(iprovider, mUser, mTable, mTag);
break;
default:
perr.println("Unspecified command");
@@ -358,7 +358,7 @@
}
void putForUser(IContentProvider provider, int userHandle, final String table,
- final String key, final String value, String token, boolean makeDefault) {
+ final String key, final String value, String tag, boolean makeDefault) {
final String callPutCommand;
if ("system".equals(table)) {
callPutCommand = Settings.CALL_METHOD_PUT_SYSTEM;
@@ -378,7 +378,9 @@
Bundle arg = new Bundle();
arg.putString(Settings.NameValueTable.VALUE, value);
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- arg.putString(Settings.CALL_METHOD_TAG_KEY, token);
+ if (tag != null) {
+ arg.putString(Settings.CALL_METHOD_TAG_KEY, tag);
+ }
if (makeDefault) {
arg.putBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY, true);
}
@@ -409,7 +411,7 @@
}
void resetForUser(IContentProvider provider, int userHandle,
- String table, String token) {
+ String table, String tag) {
final String callResetCommand;
if ("secure".equals(table)) callResetCommand = Settings.CALL_METHOD_RESET_SECURE;
else if ("global".equals(table)) callResetCommand = Settings.CALL_METHOD_RESET_GLOBAL;
@@ -422,7 +424,9 @@
Bundle arg = new Bundle();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
arg.putInt(Settings.CALL_METHOD_RESET_MODE_KEY, mResetMode);
- arg.putString(Settings.CALL_METHOD_TAG_KEY, token);
+ if (tag != null) {
+ arg.putString(Settings.CALL_METHOD_TAG_KEY, tag);
+ }
String packageName = mPackageName != null ? mPackageName : resolveCallingPackage();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
provider.call(packageName, callResetCommand, null, arg);
@@ -465,9 +469,9 @@
pw.println(" Print this help text.");
pw.println(" get [--user <USER_ID> | current] NAMESPACE KEY");
pw.println(" Retrieve the current value of KEY.");
- pw.println(" put [--user <USER_ID> | current] NAMESPACE KEY VALUE [TOKEN] [default]");
+ pw.println(" put [--user <USER_ID> | current] NAMESPACE KEY VALUE [TAG] [default]");
pw.println(" Change the contents of KEY to VALUE.");
- pw.println(" TOKEN to associate with the setting.");
+ pw.println(" TAG to associate with the setting.");
pw.println(" {default} to set as the default, case-insensitive only for global/secure namespace");
pw.println(" delete NAMESPACE KEY");
pw.println(" Delete the entry for KEY.");
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index a74be35..56ae618 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -64,6 +64,7 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
/**
* This class contains the state for one type of settings. It is responsible
@@ -129,7 +130,7 @@
private static final String HISTORICAL_OPERATION_INITIALIZE = "initialize";
private static final String HISTORICAL_OPERATION_RESET = "reset";
- private static final String SHELL_PACKAGE_NAME = "shell";
+ private static final String SHELL_PACKAGE_NAME = "com.android.shell";
private static final String ROOT_PACKAGE_NAME = "root";
private static final String NULL_VALUE = "null";
@@ -307,7 +308,7 @@
Setting newState;
if (oldState != null) {
- if (!oldState.update(value, makeDefault, packageName, tag)) {
+ if (!oldState.update(value, makeDefault, packageName, tag, false)) {
return false;
}
newState = oldState;
@@ -351,7 +352,7 @@
}
// The settings provider must hold its lock when calling here.
- public boolean resetSettingLocked(String name, String packageName) {
+ public boolean resetSettingLocked(String name) {
if (TextUtils.isEmpty(name) || !hasSettingLocked(name)) {
return false;
}
@@ -362,7 +363,7 @@
String oldValue = setting.getValue();
String oldDefaultValue = setting.getDefaultValue();
- if (!setting.reset(packageName)) {
+ if (!setting.reset()) {
return false;
}
@@ -817,7 +818,7 @@
public Setting(String name, String value, boolean makeDefault, String packageName,
String tag) {
this.name = name;
- update(value, makeDefault, packageName, tag);
+ update(value, makeDefault, packageName, tag, false);
}
public Setting(String name, String value, String defaultValue,
@@ -877,16 +878,18 @@
}
/** @return whether the value changed */
- public boolean reset(String packageName) {
- return update(this.defaultValue, false, packageName, null);
+ public boolean reset() {
+ return update(this.defaultValue, false, packageName, null, true);
}
- public boolean update(String value, boolean setDefault, String packageName, String tag) {
+ public boolean update(String value, boolean setDefault, String packageName, String tag,
+ boolean forceNonSystemPackage) {
if (NULL_VALUE.equals(value)) {
value = null;
}
- final boolean callerSystem = !isNull() && isSystemPackage(mContext, packageName);
+ final boolean callerSystem = !forceNonSystemPackage &&
+ !isNull() && isSystemPackage(mContext, packageName);
// Settings set by the system are always defaults.
if (callerSystem) {
setDefault = true;
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/BaseSettingsProviderTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/BaseSettingsProviderTest.java
index 0454b51..ab23af3 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/BaseSettingsProviderTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/BaseSettingsProviderTest.java
@@ -206,19 +206,22 @@
resetToDefaultsViaShell(type, packageName, null);
}
- protected static void resetToDefaultsViaShell(int type, String packageName, String token)
+ protected static void resetToDefaultsViaShell(int type, String packageName, String tag)
throws IOException {
switch (type) {
case SETTING_TYPE_GLOBAL: {
- executeShellCommand("settings reset global " + packageName + " " + token);
+ executeShellCommand("settings reset global " + packageName + " "
+ + (tag != null ? tag : ""));
} break;
case SETTING_TYPE_SECURE: {
- executeShellCommand("settings reset secure " + packageName + " " + token);
+ executeShellCommand("settings reset secure " + packageName + " "
+ + (tag != null ? tag : ""));
} break;
case SETTING_TYPE_SYSTEM: {
- executeShellCommand("settings reset system " + packageName + " " + token);
+ executeShellCommand("settings reset system " + packageName + " "
+ + (tag != null ? tag : ""));
} break;
default: {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index f72d091..4b8734f 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -500,7 +500,6 @@
android:label="@string/accessibility_desc_work_lock"
android:permission="android.permission.MANAGE_USERS"
android:exported="false"
- android:launchMode="singleTop"
android:excludeFromRecents="true"
android:stateNotNeeded="true"
android:resumeWhilePausing="true"
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/Plugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/Plugin.java
index b31b199..e75ecb7 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/Plugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/Plugin.java
@@ -64,7 +64,7 @@
* new PluginListener<OverlayPlugin>() {
* @Override
* public void onPluginConnected(OverlayPlugin plugin) {
- * PhoneStatusBar phoneStatusBar = getComponent(PhoneStatusBar.class);
+ * StatusBar phoneStatusBar = getComponent(StatusBar.class);
* if (phoneStatusBar != null) {
* plugin.setup(phoneStatusBar.getStatusBarWindow(),
* phoneStatusBar.getNavigationBarView());
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowProvider.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowProvider.java
index c0a48a8..93ba39c 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowProvider.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowProvider.java
@@ -3,6 +3,7 @@
import android.content.Context;
import android.graphics.drawable.Drawable;
+import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
import android.view.View;
@@ -48,7 +49,7 @@
}
public interface SnoozeListener {
- public void snoozeNotification(StatusBarNotification sbn, long snoozeUntil);
+ public void snoozeNotification(StatusBarNotification sbn, SnoozeOption snoozeOption);
}
public static class MenuItem {
@@ -71,4 +72,19 @@
return false;
}
}
+
+ public static class SnoozeOption {
+ public SnoozeCriterion criterion;
+ public int snoozeForMinutes;
+ public CharSequence description;
+ public CharSequence confirmation;
+
+ public SnoozeOption(SnoozeCriterion crit, int minsToSnoozeFor, CharSequence desc,
+ CharSequence confirm) {
+ criterion = crit;
+ snoozeForMinutes = minsToSnoozeFor;
+ description = desc;
+ confirmation = confirm;
+ }
+ }
}
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 364885a..6d76798 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -11,7 +11,7 @@
}
-keep class com.android.systemui.statusbar.car.CarStatusBar
--keep class com.android.systemui.statusbar.phone.PhoneStatusBar
+-keep class com.android.systemui.statusbar.phone.StatusBar
-keep class com.android.systemui.statusbar.tv.TvStatusBar
-keep class com.android.systemui.car.CarSystemUIFactory
-keep class com.android.systemui.SystemUIFactory
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_r.xml b/packages/SystemUI/res/drawable/ic_qs_signal_r.xml
deleted file mode 100644
index 40bfbe6..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_signal_r.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
-Copyright (C) 2014 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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="32.0dp"
- android:height="32dp"
- android:viewportWidth="6.0"
- android:viewportHeight="6.0">
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M2.800000,7.900000l-1.000000,0.000000L1.800000,11.000000L0.200000,11.000000L0.200000,2.500000l2.700000,0.000000c0.900000,0.000000 1.500000,0.200000 2.000000,0.700000s0.700000,1.100000 0.700000,1.900000c0.000000,0.600000 -0.100000,1.100000 -0.300000,1.500000S4.800000,7.200000 4.400000,7.400000l1.500000,3.500000L5.900000,11.000000L4.100000,11.000000L2.800000,7.900000zM1.800000,6.500000l1.100000,0.000000c0.400000,0.000000 0.600000,-0.100000 0.800000,-0.400000S4.000000,5.600000 4.000000,5.200000c0.000000,-0.400000 -0.100000,-0.800000 -0.300000,-1.000000S3.300000,3.800000 2.900000,3.800000L1.800000,3.800000L1.800000,6.500000z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_roam.xml b/packages/SystemUI/res/drawable/stat_sys_roaming.xml
similarity index 95%
rename from packages/SystemUI/res/drawable/stat_sys_data_fully_connected_roam.xml
rename to packages/SystemUI/res/drawable/stat_sys_roaming.xml
index 363e231..4baa472 100644
--- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_roam.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_roaming.xml
@@ -14,10 +14,10 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="4.25dp"
+ android:width="8.5dp"
android:height="17dp"
android:viewportWidth="6.0"
- android:viewportHeight="24.0">
+ android:viewportHeight="12.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M2.800000,7.900000l-1.000000,0.000000L1.800000,11.000000L0.200000,11.000000L0.200000,2.500000l2.700000,0.000000c0.900000,0.000000 1.500000,0.200000 2.000000,0.700000s0.700000,1.100000 0.700000,1.900000c0.000000,0.600000 -0.100000,1.100000 -0.300000,1.500000S4.800000,7.200000 4.400000,7.400000l1.500000,3.500000L5.900000,11.000000L4.100000,11.000000L2.800000,7.900000zM1.800000,6.500000l1.100000,0.000000c0.400000,0.000000 0.600000,-0.100000 0.800000,-0.400000S4.000000,5.600000 4.000000,5.200000c0.000000,-0.400000 -0.100000,-0.800000 -0.300000,-1.000000S3.300000,3.800000 2.900000,3.800000L1.800000,3.800000L1.800000,6.500000z"/>
diff --git a/packages/SystemUI/res/layout/data_usage.xml b/packages/SystemUI/res/layout/data_usage.xml
index c943f3d..fdc6f14 100644
--- a/packages/SystemUI/res/layout/data_usage.xml
+++ b/packages/SystemUI/res/layout/data_usage.xml
@@ -59,6 +59,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:textAppearance="@style/TextAppearance.QS.DataUsage" />
+
</LinearLayout>
<LinearLayout
@@ -82,4 +83,13 @@
android:textAppearance="@style/TextAppearance.QS.DataUsage.Secondary" />
</LinearLayout>
-</com.android.systemui.qs.tiles.DataUsageDetailView>
\ No newline at end of file
+ <TextView
+ android:id="@+id/roaming_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp"
+ android:text="@string/accessibility_data_connection_roaming"
+ android:textAppearance="@style/TextAppearance.QS.DataUsage.Secondary"
+ android:visibility="gone" />
+
+</com.android.systemui.qs.tiles.DataUsageDetailView>
diff --git a/packages/SystemUI/res/layout/mobile_signal_group.xml b/packages/SystemUI/res/layout/mobile_signal_group.xml
index a20ec8e..8b10074 100644
--- a/packages/SystemUI/res/layout/mobile_signal_group.xml
+++ b/packages/SystemUI/res/layout/mobile_signal_group.xml
@@ -43,4 +43,15 @@
android:layout_height="wrap_content"
android:layout_width="wrap_content"
/>
+ <ImageView
+ android:id="@+id/mobile_roaming"
+ android:layout_width="wrap_content"
+ android:layout_height="17dp"
+ android:paddingStart="22dp"
+ android:paddingTop="1.5dp"
+ android:paddingBottom="3dp"
+ android:scaleType="fitCenter"
+ android:src="@drawable/stat_sys_roaming"
+ android:contentDescription="@string/accessibility_data_connection_roaming"
+ android:visibility="gone" />
</FrameLayout>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index b7647cf..80f3a0a 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -43,7 +43,7 @@
<!-- Component to be used as the status bar service. Must implement the IStatusBar
interface. This name is in the ComponentName flattened format (package/class) -->
- <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.PhoneStatusBar</string>
+ <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.StatusBar</string>
<!-- Whether or not we show the number in the bar. -->
<bool name="config_statusBarShowNumber">false</bool>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 7f4baa5..5d2117a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1390,6 +1390,8 @@
<string name="snooze_option_30_min">30 minutes</string>
<!-- Notification: Menu row: Snooze options: 1 hour option. [CHAR LIMIT=50]-->
<string name="snooze_option_1_hour">1 hour</string>
+ <!-- Notification: Menu row: Snooze options: don't snooze option. [CHAR LIMIT=50] -->
+ <string name="snooze_option_dont_snooze">Don\'t snooze</string>
<!-- Notification: Menu row: Snooze undo button label. [CHAR LIMIT=50]-->
<string name="snooze_undo">UNDO</string>
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
index 4ae81a7..14c67fe 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -19,7 +19,7 @@
/**
* Single common instance of ActivityStarter that can be gotten and referenced from anywhere, but
- * delegates to an actual implementation such as PhoneStatusBar, assuming it exists.
+ * delegates to an actual implementation such as StatusBar, assuming it exists.
*/
public class ActivityStarterDelegate implements ActivityStarter {
diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
index c14b17f..1d55ee5 100644
--- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java
+++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
@@ -27,7 +27,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.LatencyTracker;
import com.android.systemui.statusbar.phone.FingerprintUnlockController;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.phone.StatusBar;
/**
* Class that only runs on debuggable builds that listens to broadcasts that simulate actions in the
@@ -72,7 +72,7 @@
}
private void fakeWakeAndUnlock() {
- FingerprintUnlockController fingerprintUnlockController = getComponent(PhoneStatusBar.class)
+ FingerprintUnlockController fingerprintUnlockController = getComponent(StatusBar.class)
.getFingerprintUnlockController();
fingerprintUnlockController.onFingerprintAcquired();
fingerprintUnlockController.onFingerprintAuthenticated(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SystemBars.java b/packages/SystemUI/src/com/android/systemui/SystemBars.java
similarity index 80%
rename from packages/SystemUI/src/com/android/systemui/statusbar/SystemBars.java
rename to packages/SystemUI/src/com/android/systemui/SystemBars.java
index 275fd70..6623cabe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SystemBars.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemBars.java
@@ -1,20 +1,18 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2017 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
+ * 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.
+ * 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;
+package com.android.systemui;
import android.content.res.Configuration;
import android.provider.Settings;
@@ -36,7 +34,7 @@
private static final int WAIT_FOR_BARS_TO_DIE = 500;
// in-process fallback implementation, per the product config
- private BaseStatusBar mStatusBar;
+ private SystemUI mStatusBar;
@Override
public void start() {
@@ -71,7 +69,7 @@
throw andLog("Error loading status bar component: " + clsName, t);
}
try {
- mStatusBar = (BaseStatusBar) cls.newInstance();
+ mStatusBar = (SystemUI) cls.newInstance();
} catch (Throwable t) {
throw andLog("Error creating status bar component: " + clsName, t);
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index afe88c1..9515585 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -42,8 +42,7 @@
import com.android.systemui.shortcut.ShortcutKeyDispatcher;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.SystemBars;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.usb.StorageNotification;
import com.android.systemui.util.NotificationChannels;
@@ -210,10 +209,10 @@
new PluginListener<OverlayPlugin>() {
@Override
public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) {
- PhoneStatusBar phoneStatusBar = getComponent(PhoneStatusBar.class);
- if (phoneStatusBar != null) {
- plugin.setup(phoneStatusBar.getStatusBarWindow(),
- phoneStatusBar.getNavigationBarView());
+ StatusBar statusBar = getComponent(StatusBar.class);
+ if (statusBar != null) {
+ plugin.setup(statusBar.getStatusBarWindow(),
+ statusBar.getNavigationBarView());
}
}
}, OverlayPlugin.VERSION, true /* Allow multiple plugins */);
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index ec11812..1ff0701 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -26,10 +26,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.Dependency.DependencyProvider;
-import com.android.systemui.R;
-import com.android.systemui.assist.AssistManager;
import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
@@ -37,26 +34,11 @@
import com.android.systemui.statusbar.phone.LockIcon;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.QSTileHost;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.StatusBarWindowManager;
-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 com.android.systemui.volume.VolumeDialogController;
/**
@@ -113,8 +95,8 @@
}
public NotificationIconAreaController createNotificationIconAreaController(Context context,
- PhoneStatusBar phoneStatusBar) {
- return new NotificationIconAreaController(context, phoneStatusBar);
+ StatusBar statusBar) {
+ return new NotificationIconAreaController(context, statusBar);
}
public KeyguardIndicationController createKeyguardIndicationController(Context context,
@@ -122,7 +104,7 @@
return new KeyguardIndicationController(context, indicationArea, lockIcon);
}
- public QSTileHost createQSTileHost(Context context, PhoneStatusBar statusBar,
+ public QSTileHost createQSTileHost(Context context, StatusBar statusBar,
StatusBarIconController iconController) {
return new QSTileHost(context, statusBar, iconController);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index ce89aab..99c8c6b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -80,7 +80,7 @@
import com.android.systemui.SystemUIFactory;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.statusbar.phone.FingerprintUnlockController;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarWindowManager;
@@ -1955,11 +1955,11 @@
Trace.endSection();
}
- public StatusBarKeyguardViewManager registerStatusBar(PhoneStatusBar phoneStatusBar,
+ public StatusBarKeyguardViewManager registerStatusBar(StatusBar statusBar,
ViewGroup container, StatusBarWindowManager statusBarWindowManager,
ScrimController scrimController,
FingerprintUnlockController fingerprintUnlockController) {
- mStatusBarKeyguardViewManager.registerStatusBar(phoneStatusBar, container,
+ mStatusBarKeyguardViewManager.registerStatusBar(statusBar, container,
statusBarWindowManager, scrimController, fingerprintUnlockController,
mDismissCallbackRegistry);
return mStatusBarKeyguardViewManager;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
index c7514a9..23eaed9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard;
import static android.app.ActivityManager.TaskDescription;
+import static android.app.ActivityManager.StackId;
import android.annotation.ColorInt;
import android.annotation.UserIdInt;
@@ -31,6 +32,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
+import android.graphics.Rect;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -160,9 +162,23 @@
credential.putExtra(Intent.EXTRA_INTENT, target.getIntentSender());
try {
- ActivityManager.getService().startConfirmDeviceCredentialIntent(credential);
+ ActivityManager.getService().startConfirmDeviceCredentialIntent(credential,
+ getChallengeOptions().toBundle());
} catch (RemoteException e) {
Log.e(TAG, "Failed to start confirm credential intent", e);
}
}
+
+ private ActivityOptions getChallengeOptions() {
+ // If we are taking up the whole screen, just use the default animation of clipping the
+ // credentials activity into the entire foreground.
+ if (!isInMultiWindowMode()) {
+ return ActivityOptions.makeBasic();
+ }
+
+ // Otherwise, animate the transition from this part of the screen to fullscreen
+ // using our own decor as the starting position.
+ final View view = getWindow().getDecorView();
+ return ActivityOptions.makeScaleUpAnimation(view, 0, 0, view.getWidth(), view.getHeight());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
index 2c41a08..e6483f6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
@@ -42,9 +42,8 @@
Intent intent = new Intent(KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER)
.setComponent(new ComponentName(mContext, WorkLockActivity.class))
.putExtra(Intent.EXTRA_USER_ID, userId)
- .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
- | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ .addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP);
final ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchTaskId(taskId);
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 12fda14..5727684 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -67,7 +67,7 @@
private static final int MINIMIZE_STACK_MAX_DURATION = 200;
// 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;
+ private static final float MINIMIZE_OFFSCREEN_FRACTION = 0.2f;
private final Context mContext;
private final IActivityManager mActivityManager;
@@ -183,7 +183,7 @@
mTouchState = new PipTouchState(mViewConfig);
mFlingAnimationUtils = new FlingAnimationUtils(context, 2f);
mGestures = new PipTouchGesture[] {
- mDragToDismissGesture, mTapThroughGesture, mMinimizeGesture, mDefaultMovementGesture
+ mDragToDismissGesture, mDefaultMovementGesture
};
mMotionHelper = new PipMotionHelper(BackgroundThread.getHandler());
registerInputConsumer();
@@ -231,6 +231,7 @@
public void onMinimizedStateChanged(boolean isMinimized) {
mIsMinimized = isMinimized;
+ mSnapAlgorithm.setMinimized(isMinimized);
}
public void onSnapToEdgeStateChanged(boolean isSnapToEdge) {
@@ -298,14 +299,13 @@
}
/**
- * @return whether the current touch state is a horizontal drag offscreen.
+ * @return whether the current touch state places the pip partially 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);
+ return !(mBoundedPinnedStackBounds.left <= left && left <= mBoundedPinnedStackBounds.right);
}
/**
@@ -429,7 +429,7 @@
mUpdatePinnedStackBoundsListener);
mPinnedStackBoundsAnimator.addListener(new AnimatorListenerAdapter() {
@Override
- public void onAnimationEnd(Animator animation) {
+ public void onAnimationStart(Animator animation) {
mMenuController.hideMenu();
}
});
@@ -590,115 +590,22 @@
/**** Gestures ****/
/**
- * 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 (touchState.allowDraggingOffscreen() && 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) {
- return false;
- }
-
- @Override
- public boolean onUp(PipTouchState touchState) {
- if (!touchState.isDragging() && !mIsMinimized && !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.startedDragging()) {
- // For now, once the user has started a drag that the other gestures have not
- // intercepted, disallow those gestures from intercepting again to drag offscreen
- touchState.setDisallowDraggingOffscreen();
- }
-
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) {
+ if (!touchState.allowDraggingOffscreen()) {
left = Math.max(mBoundedPinnedStackBounds.left, Math.min(
mBoundedPinnedStackBounds.right, left));
- top = Math.max(mBoundedPinnedStackBounds.top, Math.min(
- mBoundedPinnedStackBounds.bottom, top));
}
+ top = Math.max(mBoundedPinnedStackBounds.top, Math.min(
+ mBoundedPinnedStackBounds.bottom, top));
mTmpBounds.set(mPinnedStackBounds);
mTmpBounds.offsetTo((int) left, (int) top);
movePinnedStack(mTmpBounds);
@@ -711,16 +618,58 @@
public boolean onUp(PipTouchState touchState) {
if (touchState.isDragging()) {
PointF vel = mTouchState.getVelocity();
- float velocity = PointF.length(vel.x, vel.y);
+ if (!mIsMinimized && (shouldMinimizedPinnedStack()
+ || isHorizontalFlingTowardsCurrentEdge(vel))) {
+ // Pip should be minimized
+ setMinimizedState(true);
+ animateToClosestMinimizedTarget();
+ return true;
+ }
+ if (mIsMinimized) {
+ // If we're dragging and it wasn't a minimize gesture
+ // then we shouldn't be minimized.
+ setMinimizedState(false);
+ }
+
+ final float velocity = PointF.length(vel.x, vel.y);
if (velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
flingToSnapTarget(velocity, vel.x, vel.y);
} else {
animateToClosestSnapTarget();
}
+ } else if (mIsMinimized) {
+ // This was a tap, so no longer minimized
+ animateToClosestSnapTarget();
+ setMinimizedState(false);
+ } else if (!mIsTappingThrough) {
+ mMenuController.showMenu();
+ mIsTappingThrough = true;
} else {
expandPinnedStackToFullscreen();
}
return true;
}
};
+
+ /**
+ * @return whether the gesture ending in the {@param vel} is fast enough to be a fling towards
+ * the same edge the PIP is on. Used to identify a minimize gesture.
+ */
+ private boolean isHorizontalFlingTowardsCurrentEdge(PointF vel) {
+ final boolean isHorizontal = Math.abs(vel.x) > Math.abs(vel.y);
+ final boolean isFling = PointF.length(vel.x, vel.y) > mFlingAnimationUtils
+ .getMinVelocityPxPerSecond();
+ final boolean towardsCurrentEdge = onEdge(true /* left */) && vel.x < 0
+ || onEdge(false /* right */) && vel.x > 0;
+ return towardsCurrentEdge && isHorizontal && isFling;
+ }
+
+ private boolean onEdge(boolean checkLeft) {
+ if (checkLeft) {
+ return mPinnedStackBounds.left <= mBoundedPinnedStackBounds.left;
+ } else {
+ return mPinnedStackBounds.right >= mBoundedPinnedStackBounds.right
+ + mPinnedStackBounds.width();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 94ea4dc..82ec69d 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -41,7 +41,7 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.util.NotificationChannels;
@@ -98,7 +98,7 @@
private SystemUIDialog mHighTempDialog;
public PowerNotificationWarnings(Context context, NotificationManager notificationManager,
- PhoneStatusBar phoneStatusBar) {
+ StatusBar statusBar) {
mContext = context;
mNoMan = notificationManager;
mPowerMan = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 1d4a5c7..3d36868 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -36,7 +36,7 @@
import android.util.Slog;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.phone.StatusBar;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -72,7 +72,7 @@
mWarnings = new PowerNotificationWarnings(
mContext,
(NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE),
- getComponent(PhoneStatusBar.class));
+ getComponent(StatusBar.class));
ContentObserver obs = new ContentObserver(mHandler) {
@Override
@@ -250,8 +250,8 @@
}
private void updateTemperatureWarning() {
- PhoneStatusBar phoneStatusBar = getComponent(PhoneStatusBar.class);
- if (phoneStatusBar != null && phoneStatusBar.isDeviceInVrMode()) {
+ StatusBar statusBar = getComponent(StatusBar.class);
+ if (statusBar != null && statusBar.isDeviceInVrMode()) {
// ensure the warning isn't showing, since VR shows its own warning
mWarnings.dismissTemperatureWarning();
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 409943d..602f9bf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -193,9 +193,9 @@
translationXBuilder.addFloat(label, "translationX", -xDiff, 0);
translationYBuilder.addFloat(label, "translationY", -yDiff, 0);
- mTopFiveQs.add(tileIcon);
+ mTopFiveQs.add(tileView.getIcon());
mTopFiveQs.add(tileView.getBgCicle());
- mAllViews.add(tileIcon);
+ mAllViews.add(tileView.getIcon());
mAllViews.add(quickTileView);
} else if (mFullRows && isIconInAnimatedRow(count)) {
// TODO: Refactor some of this, it shares a lot with the above block.
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 7e04b67..06f4d9d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
@@ -19,7 +19,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.graphics.drawable.Drawable;
import android.service.quicksettings.Tile;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
@@ -42,8 +41,6 @@
import com.android.systemui.R;
import com.android.systemui.plugins.qs.QS.DetailAdapter;
import com.android.systemui.qs.QSTile;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
-import com.android.systemui.qs.external.TileColorPicker;
import com.android.systemui.statusbar.policy.BatteryController;
import java.text.NumberFormat;
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 75c4a75..bdc95c0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -38,7 +38,7 @@
import com.android.systemui.qs.SignalTileView;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.SignalCallbackAdapter;
+import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
/** Quick settings tile: Cellular **/
public class CellularTile extends QSTile<QSTile.SignalState> {
@@ -197,9 +197,10 @@
String enabledDesc;
boolean noSim;
boolean isDataTypeIconWide;
+ boolean roaming;
}
- private final class CellSignalCallback extends SignalCallbackAdapter {
+ private final class CellSignalCallback implements SignalCallback {
private final CallbackInfo mInfo = new CallbackInfo();
@Override
public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
@@ -211,7 +212,7 @@
@Override
public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
- String description, boolean isWide, int subId) {
+ String description, boolean isWide, int subId, boolean roaming) {
if (qsIcon == null) {
// Not data sim, don't display.
return;
@@ -225,6 +226,7 @@
mInfo.activityOut = activityOut;
mInfo.enabledDesc = description;
mInfo.isDataTypeIconWide = qsType != 0 && isWide;
+ mInfo.roaming = roaming;
refreshState(mInfo);
}
@@ -294,6 +296,8 @@
final DataUsageController.DataUsageInfo info = mDataController.getDataUsageInfo();
if (info == null) return v;
v.bind(info);
+ v.findViewById(R.id.roaming_text).setVisibility(mSignalCallback.mInfo.roaming
+ ? View.VISIBLE : View.INVISIBLE);
return v;
}
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 54b41ac..2d61857 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -43,7 +43,7 @@
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.SignalCallbackAdapter;
+import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import java.util.List;
@@ -256,7 +256,7 @@
}
}
- protected final class WifiSignalCallback extends SignalCallbackAdapter {
+ protected final class WifiSignalCallback implements SignalCallback {
final CallbackInfo mInfo = new CallbackInfo();
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 06fadd1..a6fe0ea 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -88,7 +88,7 @@
import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.views.RecentsView;
import com.android.systemui.recents.views.SystemBarScrimViews;
-import com.android.systemui.statusbar.BaseStatusBar;
+import com.android.systemui.statusbar.phone.StatusBar;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -283,7 +283,7 @@
dismissEvent.addPostAnimationCallback(new LaunchHomeRunnable(mHomeIntent,
overrideAnimation));
Recents.getSystemServices().sendCloseSystemWindows(
- BaseStatusBar.SYSTEM_DIALOG_REASON_HOME_KEY);
+ StatusBar.SYSTEM_DIALOG_REASON_HOME_KEY);
EventBus.getDefault().send(dismissEvent);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index cf6357b..9a8b267 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -18,7 +18,6 @@
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
import static android.app.ActivityManager.StackId.isHomeOrRecentsStack;
import static android.view.View.MeasureSpec;
@@ -77,9 +76,8 @@
import com.android.systemui.recents.views.TaskViewHeader;
import com.android.systemui.recents.views.TaskViewTransform;
import com.android.systemui.stackdivider.DividerView;
-import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.phone.StatusBar;
import java.util.ArrayList;
@@ -229,7 +227,7 @@
*/
public void onStartScreenPinning(Context context, int taskId) {
SystemUIApplication app = (SystemUIApplication) context;
- PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
+ StatusBar statusBar = app.getComponent(StatusBar.class);
if (statusBar != null) {
statusBar.showScreenPinningRequest(taskId, false);
}
@@ -351,7 +349,7 @@
growTarget);
// Only close the other system windows if we are actually showing recents
- ssp.sendCloseSystemWindows(BaseStatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS);
+ ssp.sendCloseSystemWindows(StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS);
mLastToggleTime = SystemClock.elapsedRealtime();
}
} catch (ActivityNotFoundException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
index a2a8199..a691a424 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -30,6 +30,8 @@
import android.widget.FrameLayout.LayoutParams;
import com.android.systemui.R;
+import com.android.systemui.pip.tv.PipManager;
+import com.android.systemui.pip.tv.PipRecentsOverlayManager;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
@@ -57,9 +59,7 @@
import com.android.systemui.recents.tv.views.TaskCardView;
import com.android.systemui.recents.tv.views.TaskStackHorizontalGridView;
import com.android.systemui.recents.tv.views.TaskStackHorizontalViewAdapter;
-import com.android.systemui.statusbar.BaseStatusBar;
-import com.android.systemui.pip.tv.PipManager;
-import com.android.systemui.pip.tv.PipRecentsOverlayManager;
+import com.android.systemui.statusbar.phone.StatusBar;
import java.util.ArrayList;
import java.util.Collections;
@@ -258,7 +258,7 @@
@Override
public void run() {
Recents.getSystemServices().sendCloseSystemWindows(
- BaseStatusBar.SYSTEM_DIALOG_REASON_HOME_KEY);
+ StatusBar.SYSTEM_DIALOG_REASON_HOME_KEY);
}
};
DismissRecentsToHomeAnimationStarted dismissEvent =
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index 2bd651b..6a66fca7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -51,7 +51,7 @@
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.statusbar.BaseStatusBar;
+import com.android.systemui.statusbar.phone.StatusBar;
import java.util.ArrayList;
import java.util.Collections;
@@ -170,7 +170,7 @@
}
}
Recents.getSystemServices().sendCloseSystemWindows(
- BaseStatusBar.SYSTEM_DIALOG_REASON_HOME_KEY);
+ StatusBar.SYSTEM_DIALOG_REASON_HOME_KEY);
}
public IRemoteCallback wrapStartedListener(final OnAnimationStartedListener listener) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 3cd2a7a..b9a0f74 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -44,6 +44,7 @@
private boolean mVisible = false;
private boolean mMinimized = false;
private boolean mAdjustedForIme = false;
+ private boolean mHomeStackResizable = false;
private ForcedResizableInfoActivityController mForcedResizableController;
@Override
@@ -75,6 +76,7 @@
mView = (DividerView)
LayoutInflater.from(mContext).inflate(R.layout.docked_stack_divider, null);
mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE);
+ mView.setMinimizedDockStack(mMinimized, mHomeStackResizable);
final int size = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_thickness);
final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
@@ -92,7 +94,7 @@
removeDivider();
addDivider(configuration);
if (mMinimized) {
- mView.setMinimizedDockStack(true);
+ mView.setMinimizedDockStack(true, mHomeStackResizable);
updateTouchable();
}
}
@@ -106,13 +108,14 @@
mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
// Update state because animations won't finish.
- mView.setMinimizedDockStack(mMinimized);
+ mView.setMinimizedDockStack(mMinimized, mHomeStackResizable);
}
}
});
}
- private void updateMinimizedDockedStack(final boolean minimized, final long animDuration) {
+ private void updateMinimizedDockedStack(final boolean minimized, final long animDuration,
+ final boolean isHomeStackResizable) {
mView.post(new Runnable() {
@Override
public void run() {
@@ -120,9 +123,9 @@
mMinimized = minimized;
updateTouchable();
if (animDuration > 0) {
- mView.setMinimizedDockStack(minimized, animDuration);
+ mView.setMinimizedDockStack(minimized, animDuration, isHomeStackResizable);
} else {
- mView.setMinimizedDockStack(minimized);
+ mView.setMinimizedDockStack(minimized, isHomeStackResizable);
}
}
}
@@ -139,7 +142,7 @@
}
private void updateTouchable() {
- mWindowManager.setTouchable(!mMinimized && !mAdjustedForIme);
+ mWindowManager.setTouchable((mHomeStackResizable || !mMinimized) && !mAdjustedForIme);
}
@Override
@@ -162,9 +165,10 @@
}
@Override
- public void onDockedStackMinimizedChanged(boolean minimized, long animDuration)
- throws RemoteException {
- updateMinimizedDockedStack(minimized, animDuration);
+ public void onDockedStackMinimizedChanged(boolean minimized, long animDuration,
+ boolean isHomeStackResizable) throws RemoteException {
+ mHomeStackResizable = isHomeStackResizable;
+ updateMinimizedDockedStack(minimized, animDuration, isHomeStackResizable);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 47d2def..49035ba 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -64,6 +64,7 @@
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
+import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
import com.android.systemui.recents.events.activity.UndockingTaskEvent;
import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
import com.android.systemui.recents.events.ui.RecentsGrowingEvent;
@@ -123,6 +124,8 @@
private boolean mMoving;
private int mTouchSlop;
private boolean mBackgroundLifted;
+ private boolean mIsInMinimizeInteraction;
+ private int mDividerPositionBeforeMinimized;
private int mDividerInsets;
private int mDisplayWidth;
@@ -145,6 +148,7 @@
private VelocityTracker mVelocityTracker;
private FlingAnimationUtils mFlingAnimationUtils;
private DividerSnapAlgorithm mSnapAlgorithm;
+ private DividerSnapAlgorithm mMinimizedSnapAlgorithm;
private final Rect mStableInsets = new Rect();
private boolean mGrowRecents;
@@ -154,6 +158,7 @@
private int mExitStartPosition;
private GestureDetector mGestureDetector;
private boolean mDockedStackMinimized;
+ private boolean mHomeStackResizable;
private boolean mAdjustedForIme;
private DividerState mState;
@@ -350,8 +355,9 @@
|| mStableInsets.bottom != insets.getStableInsetBottom()) {
mStableInsets.set(insets.getStableInsetLeft(), insets.getStableInsetTop(),
insets.getStableInsetRight(), insets.getStableInsetBottom());
- if (mSnapAlgorithm != null) {
+ if (mSnapAlgorithm != null || mMinimizedSnapAlgorithm != null) {
mSnapAlgorithm = null;
+ mMinimizedSnapAlgorithm = null;
initializeSnapAlgorithm();
}
}
@@ -446,11 +452,17 @@
mSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(), mDisplayWidth,
mDisplayHeight, mDividerSize, isHorizontalDivision(), mStableInsets);
}
+ if (mMinimizedSnapAlgorithm == null) {
+ mMinimizedSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(),
+ mDisplayWidth, mDisplayHeight, mDividerSize, isHorizontalDivision(),
+ mStableInsets, mDockedStackMinimized && mHomeStackResizable);
+ }
}
public DividerSnapAlgorithm getSnapAlgorithm() {
initializeSnapAlgorithm();
- return mSnapAlgorithm;
+ return mDockedStackMinimized && mHomeStackResizable ? mMinimizedSnapAlgorithm :
+ mSnapAlgorithm;
}
public int getCurrentPosition() {
@@ -495,7 +507,7 @@
mMoving = true;
}
if (mMoving && mDockSide != WindowManager.DOCKED_INVALID) {
- SnapTarget snapTarget = mSnapAlgorithm.calculateSnapTarget(
+ SnapTarget snapTarget = getSnapAlgorithm().calculateSnapTarget(
mStartPosition, 0 /* velocity */, false /* hardDismiss */);
resizeStackDelayed(calculatePosition(x, y), mStartPosition, snapTarget);
}
@@ -551,9 +563,10 @@
private void fling(int position, float velocity, boolean avoidDismissStart,
boolean logMetrics) {
- SnapTarget snapTarget = mSnapAlgorithm.calculateSnapTarget(position, velocity);
- if (avoidDismissStart && snapTarget == mSnapAlgorithm.getDismissStartTarget()) {
- snapTarget = mSnapAlgorithm.getFirstSplitTarget();
+ DividerSnapAlgorithm currentSnapAlgorithm = getSnapAlgorithm();
+ SnapTarget snapTarget = currentSnapAlgorithm.calculateSnapTarget(position, velocity);
+ if (avoidDismissStart && snapTarget == currentSnapAlgorithm.getDismissStartTarget()) {
+ snapTarget = currentSnapAlgorithm.getFirstSplitTarget();
}
if (logMetrics) {
logResizeEvent(snapTarget);
@@ -574,6 +587,10 @@
private ValueAnimator getFlingAnimator(int position, final SnapTarget snapTarget,
final long endDelay) {
+ if (mCurrentAnimator != null) {
+ cancelFlingAnimation();
+ updateDockSide();
+ }
final boolean taskPositionSameAtEnd = snapTarget.flag == SnapTarget.FLAG_NONE;
ValueAnimator anim = ValueAnimator.ofInt(position, snapTarget.position);
anim.addUpdateListener(animation -> resizeStackDelayed((int) animation.getAnimatedValue(),
@@ -590,6 +607,12 @@
mExitAnimationRunning = false;
EventBus.getDefault().send(new StoppedDragingEvent());
};
+ Runnable notCancelledEndAction = () -> {
+ // Reset minimized divider position after unminimized state animation finishes
+ if (!mDockedStackMinimized && mIsInMinimizeInteraction) {
+ mIsInMinimizeInteraction = false;
+ }
+ };
anim.addListener(new AnimatorListenerAdapter() {
private boolean mCancelled;
@@ -612,8 +635,14 @@
}
if (delay == 0) {
endAction.run();
+ if (!mCancelled) {
+ notCancelledEndAction.run();
+ }
} else {
mHandler.postDelayed(endAction, delay);
+ if (!mCancelled) {
+ mHandler.postDelayed(notCancelledEndAction, delay);
+ }
}
}
});
@@ -692,57 +721,92 @@
}
- public void setMinimizedDockStack(boolean minimized) {
+ public void setMinimizedDockStack(boolean minimized, boolean isHomeStackResizable) {
+ mHomeStackResizable = isHomeStackResizable;
updateDockSide();
- mHandle.setAlpha(minimized ? 0f : 1f);
if (!minimized) {
resetBackground();
- } else if (mDockSide == WindowManager.DOCKED_TOP) {
- mBackground.setPivotY(0);
- mBackground.setScaleY(MINIMIZE_DOCK_SCALE);
- } else if (mDockSide == WindowManager.DOCKED_LEFT
- || mDockSide == WindowManager.DOCKED_RIGHT) {
- mBackground.setPivotX(mDockSide == WindowManager.DOCKED_LEFT
- ? 0
- : mBackground.getWidth());
- mBackground.setScaleX(MINIMIZE_DOCK_SCALE);
+ } else if (!isHomeStackResizable) {
+ if (mDockSide == WindowManager.DOCKED_TOP) {
+ mBackground.setPivotY(0);
+ mBackground.setScaleY(MINIMIZE_DOCK_SCALE);
+ } else if (mDockSide == WindowManager.DOCKED_LEFT
+ || mDockSide == WindowManager.DOCKED_RIGHT) {
+ mBackground.setPivotX(mDockSide == WindowManager.DOCKED_LEFT
+ ? 0
+ : mBackground.getWidth());
+ mBackground.setScaleX(MINIMIZE_DOCK_SCALE);
+ }
}
mMinimizedShadow.setAlpha(minimized ? 1f : 0f);
- mDockedStackMinimized = minimized;
+ if (!isHomeStackResizable) {
+ mHandle.setAlpha(minimized ? 0f : 1f);
+ mDockedStackMinimized = minimized;
+ } else if (mDockedStackMinimized != minimized) {
+ if (mStableInsets.isEmpty()) {
+ SystemServicesProxy.getInstance(mContext).getStableInsets(mStableInsets);
+ }
+ if (!mIsInMinimizeInteraction && minimized) {
+ mIsInMinimizeInteraction = true;
+ mDividerPositionBeforeMinimized = DockedDividerUtils.calculateMiddlePosition(
+ isHorizontalDivision(), mStableInsets, mDisplayWidth, mDisplayHeight,
+ mDividerSize);
+ }
+ mMinimizedSnapAlgorithm = null;
+ mDockedStackMinimized = minimized;
+ initializeSnapAlgorithm();
+ }
}
- public void setMinimizedDockStack(boolean minimized, long animDuration) {
+ public void setMinimizedDockStack(boolean minimized, long animDuration,
+ boolean isHomeStackResizable) {
+ mHomeStackResizable = isHomeStackResizable;
updateDockSide();
- mHandle.animate()
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .setDuration(animDuration)
- .alpha(minimized ? 0f : 1f)
- .start();
- if (mDockSide == WindowManager.DOCKED_TOP) {
- mBackground.setPivotY(0);
- mBackground.animate()
- .scaleY(minimized ? MINIMIZE_DOCK_SCALE : 1f);
- } else if (mDockSide == WindowManager.DOCKED_LEFT
- || mDockSide == WindowManager.DOCKED_RIGHT) {
- mBackground.setPivotX(mDockSide == WindowManager.DOCKED_LEFT
- ? 0
- : mBackground.getWidth());
- mBackground.animate()
- .scaleX(minimized ? MINIMIZE_DOCK_SCALE : 1f);
+ if (!isHomeStackResizable) {
+ mMinimizedShadow.animate()
+ .alpha(minimized ? 1f : 0f)
+ .setInterpolator(Interpolators.ALPHA_IN)
+ .setDuration(animDuration)
+ .start();
+ mHandle.animate()
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .setDuration(animDuration)
+ .alpha(minimized ? 0f : 1f)
+ .start();
+ if (mDockSide == WindowManager.DOCKED_TOP) {
+ mBackground.setPivotY(0);
+ mBackground.animate()
+ .scaleY(minimized ? MINIMIZE_DOCK_SCALE : 1f);
+ } else if (mDockSide == WindowManager.DOCKED_LEFT
+ || mDockSide == WindowManager.DOCKED_RIGHT) {
+ mBackground.setPivotX(mDockSide == WindowManager.DOCKED_LEFT
+ ? 0
+ : mBackground.getWidth());
+ mBackground.animate()
+ .scaleX(minimized ? MINIMIZE_DOCK_SCALE : 1f);
+ }
+ mDockedStackMinimized = minimized;
+ } else if (mDockedStackMinimized != minimized) {
+ mIsInMinimizeInteraction = true;
+ if (minimized) {
+ mDividerPositionBeforeMinimized = getCurrentPosition();
+ }
+ mMinimizedSnapAlgorithm = null;
+ mDockedStackMinimized = minimized;
+ initializeSnapAlgorithm();
+ stopDragging(getCurrentPosition(), minimized ?
+ mMinimizedSnapAlgorithm.getMiddleTarget() :
+ mSnapAlgorithm.calculateNonDismissingSnapTarget(
+ mDividerPositionBeforeMinimized),
+ animDuration, Interpolators.FAST_OUT_SLOW_IN, 0);
}
if (!minimized) {
mBackground.animate().withEndAction(mResetBackgroundRunnable);
}
- mMinimizedShadow.animate()
- .alpha(minimized ? 1f : 0f)
- .setInterpolator(Interpolators.ALPHA_IN)
- .setDuration(animDuration)
- .start();
mBackground.animate()
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.setDuration(animDuration)
.start();
- mDockedStackMinimized = minimized;
}
public void setAdjustedForIme(boolean adjustedForIme) {
@@ -809,6 +873,7 @@
mDisplayWidth = info.logicalWidth;
mDisplayHeight = info.logicalHeight;
mSnapAlgorithm = null;
+ mMinimizedSnapAlgorithm = null;
initializeSnapAlgorithm();
}
@@ -871,6 +936,15 @@
}
mLastResizeRect.set(mDockedRect);
+ if (mHomeStackResizable && mIsInMinimizeInteraction) {
+ calculateBoundsForPosition(mDividerPositionBeforeMinimized, mDockSide, mDockedTaskRect);
+ calculateBoundsForPosition(mDividerPositionBeforeMinimized,
+ DockedDividerUtils.invertDockSide(mDockSide), mOtherTaskRect);
+ mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedTaskRect,
+ mOtherTaskRect, null);
+ return;
+ }
+
if (mEntranceAnimationRunning && taskPosition != TASK_POSITION_SAME) {
if (mCurrentAnimator != null) {
calculateBoundsForPosition(taskPosition, mDockSide, mDockedTaskRect);
@@ -922,7 +996,7 @@
} else {
mWindowManagerProxy.resizeDockedStack(mDockedRect, null, null, null, null);
}
- SnapTarget closestDismissTarget = mSnapAlgorithm.getClosestDismissTarget(position);
+ SnapTarget closestDismissTarget = getSnapAlgorithm().getClosestDismissTarget(position);
float dimFraction = getDimFraction(position, closestDismissTarget);
mWindowManagerProxy.setResizeDimLayer(dimFraction != 0f,
getStackIdForDismissTarget(closestDismissTarget),
@@ -943,7 +1017,7 @@
if (mEntranceAnimationRunning) {
return 0f;
}
- float fraction = mSnapAlgorithm.calculateDismissingFraction(position);
+ float fraction = getSnapAlgorithm().calculateDismissingFraction(position);
fraction = Math.max(0, Math.min(fraction, 1f));
fraction = DIM_INTERPOLATOR.getInterpolation(fraction);
if (hasInsetsAtDismissTarget(dismissTarget)) {
@@ -959,13 +1033,13 @@
*/
private boolean hasInsetsAtDismissTarget(SnapTarget dismissTarget) {
if (isHorizontalDivision()) {
- if (dismissTarget == mSnapAlgorithm.getDismissStartTarget()) {
+ if (dismissTarget == getSnapAlgorithm().getDismissStartTarget()) {
return mStableInsets.top != 0;
} else {
return mStableInsets.bottom != 0;
}
} else {
- if (dismissTarget == mSnapAlgorithm.getDismissStartTarget()) {
+ if (dismissTarget == getSnapAlgorithm().getDismissStartTarget()) {
return mStableInsets.left != 0;
} else {
return mStableInsets.right != 0;
@@ -1135,6 +1209,7 @@
if (mStableInsets.isEmpty()) {
SystemServicesProxy.getInstance(mContext).getStableInsets(mStableInsets);
mSnapAlgorithm = null;
+ mMinimizedSnapAlgorithm = null;
initializeSnapAlgorithm();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
deleted file mode 100644
index 340b603..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ /dev/null
@@ -1,2587 +0,0 @@
-/*
- * Copyright (C) 2010 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;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.app.ActivityManager;
-import android.app.ActivityManager.StackId;
-import android.app.ActivityOptions;
-import android.app.INotificationManager;
-import android.app.KeyguardManager;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.RemoteInput;
-import android.app.TaskStackBuilder;
-import android.app.admin.DevicePolicyManager;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.IntentSender;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.UserInfo;
-import android.content.res.Configuration;
-import android.database.ContentObserver;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.os.AsyncTask;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.service.dreams.DreamService;
-import android.service.dreams.IDreamManager;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationListenerService.RankingMap;
-import android.service.notification.StatusBarNotification;
-import android.service.vr.IVrManager;
-import android.service.vr.IVrStateCallbacks;
-import android.text.TextUtils;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-import android.view.Display;
-import android.view.IWindowManager;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewAnimationUtils;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
-import android.view.accessibility.AccessibilityManager;
-import android.widget.RemoteViews;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardHostView.OnDismissAction;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.DejankUtils;
-import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.RecentsComponent;
-import com.android.systemui.SwipeHelper;
-import com.android.systemui.SystemUI;
-import com.android.systemui.assist.AssistManager;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.MenuItem;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.SnoozeGutsContent;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.SnoozeListener;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.statusbar.NotificationData.Entry;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.PreviewInflater;
-import com.android.systemui.statusbar.policy.RemoteInputView;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
-import com.android.systemui.util.NotificationChannels;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-import java.util.Stack;
-
-public abstract class BaseStatusBar extends SystemUI implements
- CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
- ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
- ExpandableNotificationRow.OnExpandClickListener {
- public static final String TAG = "StatusBar";
- public static final boolean DEBUG = false;
- public static final boolean MULTIUSER_DEBUG = false;
-
- public static final boolean ENABLE_REMOTE_INPUT =
- SystemProperties.getBoolean("debug.enable_remote_input", true);
- public static final boolean ENABLE_CHILD_NOTIFICATIONS
- = SystemProperties.getBoolean("debug.child_notifs", true);
- public static final boolean FORCE_REMOTE_INPUT_HISTORY =
- SystemProperties.getBoolean("debug.force_remoteinput_history", false);
- private static boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false;
-
- protected static final int MSG_SHOW_RECENT_APPS = 1019;
- protected static final int MSG_HIDE_RECENT_APPS = 1020;
- protected static final int MSG_TOGGLE_RECENTS_APPS = 1021;
- protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
- protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
- protected static final int MSG_SHOW_NEXT_AFFILIATED_TASK = 1024;
- protected static final int MSG_SHOW_PREV_AFFILIATED_TASK = 1025;
- protected static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU = 1026;
- protected static final int MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU = 1027;
-
- protected static final boolean ENABLE_HEADS_UP = true;
- protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
-
- private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
-
- // Should match the values in PhoneWindowManager
- public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
- public static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
-
- private static final String BANNER_ACTION_CANCEL =
- "com.android.systemui.statusbar.banner_action_cancel";
- private static final String BANNER_ACTION_SETUP =
- "com.android.systemui.statusbar.banner_action_setup";
- private static final String NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION
- = "com.android.systemui.statusbar.work_challenge_unlocked_notification_action";
-
- protected CommandQueue mCommandQueue;
- protected IStatusBarService mBarService;
- protected H mHandler = createHandler();
-
- // all notifications
- protected NotificationData mNotificationData;
- protected NotificationStackScrollLayout mStackScroller;
-
- protected NotificationGroupManager mGroupManager = new NotificationGroupManager();
-
- protected RemoteInputController mRemoteInputController;
-
- // for heads up notifications
- protected HeadsUpManager mHeadsUpManager;
-
- // handling reordering
- protected VisualStabilityManager mVisualStabilityManager = new VisualStabilityManager();
-
- protected int mCurrentUserId = 0;
- final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
-
- protected int mLayoutDirection = -1; // invalid
- protected AccessibilityManager mAccessibilityManager;
-
- protected boolean mDeviceInteractive;
-
- protected boolean mVisible;
- protected ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>();
- protected ArraySet<Entry> mRemoteInputEntriesToRemoveOnCollapse = new ArraySet<>();
-
- /**
- * Notifications with keys in this set are not actually around anymore. We kept them around
- * when they were canceled in response to a remote input interaction. This allows us to show
- * what you replied and allows you to continue typing into it.
- */
- protected ArraySet<String> mKeysKeptForRemoteInput = new ArraySet<>();
-
- // mScreenOnFromKeyguard && mVisible.
- private boolean mVisibleToUser;
-
- private Locale mLocale;
- private float mFontScale;
-
- protected boolean mUseHeadsUp = false;
- protected boolean mHeadsUpTicker = false;
- protected boolean mDisableNotificationAlerts = false;
-
- protected DevicePolicyManager mDevicePolicyManager;
- protected IDreamManager mDreamManager;
- protected PowerManager mPowerManager;
- protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
-
- // public mode, private notifications, etc
- private final SparseBooleanArray mLockscreenPublicMode = new SparseBooleanArray();
- private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
- private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray();
-
- private UserManager mUserManager;
- private int mDensity;
-
- protected KeyguardManager mKeyguardManager;
- private LockPatternUtils mLockPatternUtils;
- private DeviceProvisionedController mDeviceProvisionedController;
-
- // UI-specific methods
-
- /**
- * Create all windows necessary for the status bar (including navigation, overlay panels, etc)
- * and add them to the window manager.
- */
- protected abstract void createAndAddWindows();
-
- protected WindowManager mWindowManager;
- protected IWindowManager mWindowManagerService;
-
- protected Display mDisplay;
-
- protected RecentsComponent mRecents;
-
- protected int mZenMode;
-
- // which notification is currently being longpress-examined by the user
- private NotificationGuts mNotificationGutsExposed;
- private MenuItem mGutsMenuItem;
-
- private KeyboardShortcuts mKeyboardShortcuts;
-
- /**
- * The {@link StatusBarState} of the status bar.
- */
- protected int mState;
- protected boolean mBouncerShowing;
- protected boolean mShowLockscreenNotifications;
- protected boolean mAllowLockscreenRemoteInput;
-
- protected NotificationShelf mNotificationShelf;
- protected DismissView mDismissView;
- protected EmptyShadeView mEmptyShadeView;
-
- private NotificationClicker mNotificationClicker = new NotificationClicker();
-
- protected AssistManager mAssistManager;
-
- protected boolean mVrMode;
-
- private Set<String> mNonBlockablePkgs;
-
- @Override // NotificationData.Environment
- public boolean isDeviceProvisioned() {
- return mDeviceProvisionedController.isDeviceProvisioned();
- }
-
- private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
- @Override
- public void onVrStateChanged(boolean enabled) {
- mVrMode = enabled;
- }
- };
-
- public boolean isDeviceInVrMode() {
- return mVrMode;
- }
-
- private final DeviceProvisionedListener mDeviceProvisionedListener =
- new DeviceProvisionedListener() {
- @Override
- public void onDeviceProvisionedChanged() {
- updateNotifications();
- }
- };
-
- protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange) {
- final int mode = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
- setZenMode(mode);
-
- updateLockscreenNotificationSetting();
- }
- };
-
- private final ContentObserver mLockscreenSettingsObserver = new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange) {
- // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or
- // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ...
- mUsersAllowingPrivateNotifications.clear();
- mUsersAllowingNotifications.clear();
- // ... and refresh all the notifications
- updateLockscreenNotificationSetting();
- updateNotifications();
- }
- };
-
- private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
- private final int[] mTmpInt2 = new int[2];
-
- @Override
- public boolean onClickHandler(
- final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
- view.getLocationInWindow(mTmpInt2);
- wakeUpIfDozing(SystemClock.uptimeMillis(), new PointF(
- mTmpInt2[0] + view.getWidth() / 2, mTmpInt2[1] + view.getHeight() / 2));
-
-
- if (handleRemoteInput(view, pendingIntent, fillInIntent)) {
- return true;
- }
-
- if (DEBUG) {
- Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
- }
- logActionClick(view);
- // The intent we are sending is for the application, which
- // won't have permission to immediately start an activity after
- // the user switches to home. We know it is safe to do at this
- // point, so make sure new activity switches are now allowed.
- try {
- ActivityManager.getService().resumeAppSwitches();
- } catch (RemoteException e) {
- }
- final boolean isActivity = pendingIntent.isActivity();
- if (isActivity) {
- final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
- final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
- mContext, pendingIntent.getIntent(), mCurrentUserId);
- dismissKeyguardThenExecute(new OnDismissAction() {
- @Override
- public boolean onDismiss() {
- try {
- ActivityManager.getService().resumeAppSwitches();
- } catch (RemoteException e) {
- }
-
- boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
-
- // close the shade if it was open
- if (handled) {
- animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
- true /* force */);
- visibilityChanged(false);
- mAssistManager.hideAssist();
- }
-
- // Wait for activity start.
- return handled;
- }
- }, afterKeyguardGone);
- return true;
- } else {
- return superOnClickHandler(view, pendingIntent, fillInIntent);
- }
- }
-
- private void logActionClick(View view) {
- ViewParent parent = view.getParent();
- String key = getNotificationKeyForParent(parent);
- if (key == null) {
- Log.w(TAG, "Couldn't determine notification for click.");
- return;
- }
- int index = -1;
- // If this is a default template, determine the index of the button.
- if (view.getId() == com.android.internal.R.id.action0 &&
- parent != null && parent instanceof ViewGroup) {
- ViewGroup actionGroup = (ViewGroup) parent;
- index = actionGroup.indexOfChild(view);
- }
- try {
- mBarService.onNotificationActionClick(key, index);
- } catch (RemoteException e) {
- // Ignore
- }
- }
-
- private String getNotificationKeyForParent(ViewParent parent) {
- while (parent != null) {
- if (parent instanceof ExpandableNotificationRow) {
- return ((ExpandableNotificationRow) parent).getStatusBarNotification().getKey();
- }
- parent = parent.getParent();
- }
- return null;
- }
-
- private boolean superOnClickHandler(View view, PendingIntent pendingIntent,
- Intent fillInIntent) {
- return super.onClickHandler(view, pendingIntent, fillInIntent,
- StackId.FULLSCREEN_WORKSPACE_STACK_ID);
- }
-
- private boolean handleRemoteInput(View view, PendingIntent pendingIntent, Intent fillInIntent) {
- Object tag = view.getTag(com.android.internal.R.id.remote_input_tag);
- RemoteInput[] inputs = null;
- if (tag instanceof RemoteInput[]) {
- inputs = (RemoteInput[]) tag;
- }
-
- if (inputs == null) {
- return false;
- }
-
- RemoteInput input = null;
-
- for (RemoteInput i : inputs) {
- if (i.getAllowFreeFormInput()) {
- input = i;
- }
- }
-
- if (input == null) {
- return false;
- }
-
- ViewParent p = view.getParent();
- RemoteInputView riv = null;
- while (p != null) {
- if (p instanceof View) {
- View pv = (View) p;
- if (pv.isRootNamespace()) {
- riv = (RemoteInputView) pv.findViewWithTag(RemoteInputView.VIEW_TAG);
- break;
- }
- }
- p = p.getParent();
- }
- ExpandableNotificationRow row = null;
- while (p != null) {
- if (p instanceof ExpandableNotificationRow) {
- row = (ExpandableNotificationRow) p;
- break;
- }
- p = p.getParent();
- }
-
- if (riv == null || row == null) {
- return false;
- }
-
- row.setUserExpanded(true);
-
- if (!mAllowLockscreenRemoteInput) {
- final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
- if (isLockscreenPublicMode(userId)) {
- onLockedRemoteInput(row, view);
- return true;
- }
- if (mUserManager.getUserInfo(userId).isManagedProfile()
- && mKeyguardManager.isDeviceLocked(userId)) {
- onLockedWorkRemoteInput(userId, row, view);
- return true;
- }
- }
-
- int width = view.getWidth();
- if (view instanceof TextView) {
- // Center the reveal on the text which might be off-center from the TextView
- TextView tv = (TextView) view;
- if (tv.getLayout() != null) {
- int innerWidth = (int) tv.getLayout().getLineWidth(0);
- innerWidth += tv.getCompoundPaddingLeft() + tv.getCompoundPaddingRight();
- width = Math.min(width, innerWidth);
- }
- }
- int cx = view.getLeft() + width / 2;
- int cy = view.getTop() + view.getHeight() / 2;
- int w = riv.getWidth();
- int h = riv.getHeight();
- int r = Math.max(
- Math.max(cx + cy, cx + (h - cy)),
- Math.max((w - cx) + cy, (w - cx) + (h - cy)));
-
- riv.setRevealParameters(cx, cy, r);
- riv.setPendingIntent(pendingIntent);
- riv.setRemoteInput(inputs, input);
- riv.focusAnimated();
-
- return true;
- }
-
- };
-
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- updateCurrentProfilesCache();
- if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
-
- updateLockscreenNotificationSetting();
-
- userSwitched(mCurrentUserId);
- } else if (Intent.ACTION_USER_ADDED.equals(action)) {
- updateCurrentProfilesCache();
- } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
- List<ActivityManager.RecentTaskInfo> recentTask = null;
- try {
- recentTask = ActivityManager.getService().getRecentTasks(1,
- ActivityManager.RECENT_WITH_EXCLUDED
- | ActivityManager.RECENT_INCLUDE_PROFILES,
- mCurrentUserId).getList();
- } catch (RemoteException e) {
- // Abandon hope activity manager not running.
- }
- if (recentTask != null && recentTask.size() > 0) {
- UserInfo user = mUserManager.getUserInfo(recentTask.get(0).userId);
- if (user != null && user.isManagedProfile()) {
- Toast toast = Toast.makeText(mContext,
- R.string.managed_profile_foreground_toast,
- Toast.LENGTH_SHORT);
- TextView text = (TextView) toast.getView().findViewById(
- android.R.id.message);
- text.setCompoundDrawablesRelativeWithIntrinsicBounds(
- R.drawable.stat_sys_managed_profile_status, 0, 0, 0);
- int paddingPx = mContext.getResources().getDimensionPixelSize(
- R.dimen.managed_profile_toast_padding);
- text.setCompoundDrawablePadding(paddingPx);
- toast.show();
- }
- }
- } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) {
- NotificationManager noMan = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- noMan.cancel(SystemMessage.NOTE_HIDDEN_NOTIFICATIONS);
-
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
- if (BANNER_ACTION_SETUP.equals(action)) {
- animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
- true /* force */);
- mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION)
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-
- );
- }
- } else if (NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION.equals(action)) {
- final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT);
- final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
- if (intentSender != null) {
- try {
- mContext.startIntentSender(intentSender, null, 0, 0, 0);
- } catch (IntentSender.SendIntentException e) {
- /* ignore */
- }
- }
- if (notificationKey != null) {
- try {
- mBarService.onNotificationClick(notificationKey);
- } catch (RemoteException e) {
- /* ignore */
- }
- }
- }
- }
- };
-
- private final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
-
- if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) &&
- isCurrentProfile(getSendingUserId())) {
- mUsersAllowingPrivateNotifications.clear();
- updateLockscreenNotificationSetting();
- updateNotifications();
- } else if (Intent.ACTION_DEVICE_LOCKED_CHANGED.equals(action)) {
- if (userId != mCurrentUserId && isCurrentProfile(userId)) {
- onWorkChallengeChanged();
- }
- }
- }
- };
-
- private final NotificationListenerService mNotificationListener =
- new NotificationListenerService() {
- @Override
- public void onListenerConnected() {
- if (DEBUG) Log.d(TAG, "onListenerConnected");
- final StatusBarNotification[] notifications = getActiveNotifications();
- if (notifications == null) {
- Log.w(TAG, "onListenerConnected unable to get active notifications.");
- return;
- }
- final RankingMap currentRanking = getCurrentRanking();
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- for (StatusBarNotification sbn : notifications) {
- addNotification(sbn, currentRanking, null /* oldEntry */);
- }
- }
- });
- }
-
- @Override
- public void onNotificationPosted(final StatusBarNotification sbn,
- final RankingMap rankingMap) {
- if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
- if (sbn != null) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- processForRemoteInput(sbn.getNotification());
- String key = sbn.getKey();
- mKeysKeptForRemoteInput.remove(key);
- boolean isUpdate = mNotificationData.get(key) != null;
- // In case we don't allow child notifications, we ignore children of
- // notifications that have a summary, since we're not going to show them
- // anyway. This is true also when the summary is canceled,
- // because children are automatically canceled by NoMan in that case.
- if (!ENABLE_CHILD_NOTIFICATIONS
- && mGroupManager.isChildInGroupWithSummary(sbn)) {
- if (DEBUG) {
- Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
- }
-
- // Remove existing notification to avoid stale data.
- if (isUpdate) {
- removeNotification(key, rankingMap);
- } else {
- mNotificationData.updateRanking(rankingMap);
- }
- return;
- }
- if (isUpdate) {
- updateNotification(sbn, rankingMap);
- } else {
- addNotification(sbn, rankingMap, null /* oldEntry */);
- }
- }
- });
- }
- }
-
- @Override
- public void onNotificationRemoved(StatusBarNotification sbn,
- final RankingMap rankingMap) {
- if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
- if (sbn != null) {
- final String key = sbn.getKey();
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- removeNotification(key, rankingMap);
- }
- });
- }
- }
-
- @Override
- public void onNotificationRankingUpdate(final RankingMap rankingMap) {
- if (DEBUG) Log.d(TAG, "onRankingUpdate");
- if (rankingMap != null) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- updateNotificationRanking(rankingMap);
- }
- });
- } }
-
- };
-
- private void updateCurrentProfilesCache() {
- synchronized (mCurrentProfiles) {
- mCurrentProfiles.clear();
- if (mUserManager != null) {
- for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
- mCurrentProfiles.put(user.id, user);
- }
- }
- }
- }
-
- @Override
- public void start() {
- mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
- mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
- mDisplay = mWindowManager.getDefaultDisplay();
- mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService(
- Context.DEVICE_POLICY_SERVICE);
-
- mNotificationData = new NotificationData(this);
-
- mAccessibilityManager = (AccessibilityManager)
- mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
-
- mDreamManager = IDreamManager.Stub.asInterface(
- ServiceManager.checkService(DreamService.DREAM_SERVICE));
- mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-
- mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
- mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
- mSettingsObserver);
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
- mLockscreenSettingsObserver,
- UserHandle.USER_ALL);
- if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT),
- false,
- mSettingsObserver,
- UserHandle.USER_ALL);
- }
-
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
- true,
- mLockscreenSettingsObserver,
- UserHandle.USER_ALL);
-
- mBarService = IStatusBarService.Stub.asInterface(
- ServiceManager.getService(Context.STATUS_BAR_SERVICE));
-
- mRecents = getComponent(Recents.class);
-
- final Configuration currentConfig = mContext.getResources().getConfiguration();
- mLocale = currentConfig.locale;
- mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
- mFontScale = currentConfig.fontScale;
- mDensity = currentConfig.densityDpi;
-
- mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- mLockPatternUtils = new LockPatternUtils(mContext);
-
- // Connect in to the status bar manager service
- mCommandQueue = getComponent(CommandQueue.class);
- mCommandQueue.addCallbacks(this);
-
- int[] switches = new int[9];
- ArrayList<IBinder> binders = new ArrayList<IBinder>();
- ArrayList<String> iconSlots = new ArrayList<>();
- ArrayList<StatusBarIcon> icons = new ArrayList<>();
- Rect fullscreenStackBounds = new Rect();
- Rect dockedStackBounds = new Rect();
- try {
- mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
- fullscreenStackBounds, dockedStackBounds);
- } catch (RemoteException ex) {
- // If the system process isn't there we're doomed anyway.
- }
-
- createAndAddWindows();
-
- mSettingsObserver.onChange(false); // set up
- disable(switches[0], switches[6], false /* animate */);
- setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,
- fullscreenStackBounds, dockedStackBounds);
- topAppWindowChanged(switches[2] != 0);
- // StatusBarManagerService has a back up of IME token and it's restored here.
- setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
-
- // Set up the initial icon state
- int N = iconSlots.size();
- int viewIndex = 0;
- for (int i=0; i < N; i++) {
- setIcon(iconSlots.get(i), icons.get(i));
- }
-
- // Set up the initial notification state.
- try {
- mNotificationListener.registerAsSystemService(mContext,
- new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
- UserHandle.USER_ALL);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to register notification listener", e);
- }
-
-
- if (DEBUG) {
- Log.d(TAG, String.format(
- "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
- icons.size(),
- switches[0],
- switches[1],
- switches[2],
- switches[3]
- ));
- }
-
- mCurrentUserId = ActivityManager.getCurrentUser();
- setHeadsUpUser(mCurrentUserId);
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_SWITCHED);
- filter.addAction(Intent.ACTION_USER_ADDED);
- filter.addAction(Intent.ACTION_USER_PRESENT);
- mContext.registerReceiver(mBroadcastReceiver, filter);
-
- IntentFilter internalFilter = new IntentFilter();
- internalFilter.addAction(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
- internalFilter.addAction(BANNER_ACTION_CANCEL);
- internalFilter.addAction(BANNER_ACTION_SETUP);
- mContext.registerReceiver(mBroadcastReceiver, internalFilter, PERMISSION_SELF, null);
-
- IntentFilter allUsersFilter = new IntentFilter();
- allUsersFilter.addAction(
- DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
- allUsersFilter.addAction(Intent.ACTION_DEVICE_LOCKED_CHANGED);
- mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter,
- null, null);
- updateCurrentProfilesCache();
-
- IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService("vrmanager"));
- try {
- vrManager.registerListener(mVrStateCallbacks);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to register VR mode state listener: " + e);
- }
-
- mNonBlockablePkgs = new HashSet<String>();
- Collections.addAll(mNonBlockablePkgs, mContext.getResources().getStringArray(
- com.android.internal.R.array.config_nonBlockableNotificationPackages));
- }
-
- protected void notifyUserAboutHiddenNotifications() {
- if (0 != Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) {
- Log.d(TAG, "user hasn't seen notification about hidden notifications");
- if (!mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
- Log.d(TAG, "insecure lockscreen, skipping notification");
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
- return;
- }
- Log.d(TAG, "disabling lockecreen notifications and alerting the user");
- // disable lockscreen notifications until user acts on the banner.
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
-
- final String packageName = mContext.getPackageName();
- PendingIntent cancelIntent = PendingIntent.getBroadcast(mContext, 0,
- new Intent(BANNER_ACTION_CANCEL).setPackage(packageName),
- PendingIntent.FLAG_CANCEL_CURRENT);
- PendingIntent setupIntent = PendingIntent.getBroadcast(mContext, 0,
- new Intent(BANNER_ACTION_SETUP).setPackage(packageName),
- PendingIntent.FLAG_CANCEL_CURRENT);
-
- final int colorRes = com.android.internal.R.color.system_notification_accent_color;
- Notification.Builder note = new Notification.Builder(mContext)
- .setSmallIcon(R.drawable.ic_android)
- .setContentTitle(mContext.getString(R.string.hidden_notifications_title))
- .setContentText(mContext.getString(R.string.hidden_notifications_text))
- .setChannel(NotificationChannels.SECURITY)
- .setOngoing(true)
- .setColor(mContext.getColor(colorRes))
- .setContentIntent(setupIntent)
- .addAction(R.drawable.ic_close,
- mContext.getString(R.string.hidden_notifications_cancel),
- cancelIntent)
- .addAction(R.drawable.ic_settings,
- mContext.getString(R.string.hidden_notifications_setup),
- setupIntent);
- overrideNotificationAppName(mContext, note);
-
- NotificationManager noMan =
- (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- noMan.notify(SystemMessage.NOTE_HIDDEN_NOTIFICATIONS, note.build());
- }
- }
-
- public void userSwitched(int newUserId) {
- setHeadsUpUser(newUserId);
- }
-
- protected abstract void setHeadsUpUser(int newUserId);
-
- @Override // NotificationData.Environment
- public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
- final int thisUserId = mCurrentUserId;
- final int notificationUserId = n.getUserId();
- if (DEBUG && MULTIUSER_DEBUG) {
- Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
- n, thisUserId, notificationUserId));
- }
- return isCurrentProfile(notificationUserId);
- }
-
- protected void setNotificationShown(StatusBarNotification n) {
- setNotificationsShown(new String[]{n.getKey()});
- }
-
- protected void setNotificationsShown(String[] keys) {
- try {
- mNotificationListener.setNotificationsShown(keys);
- } catch (RuntimeException e) {
- Log.d(TAG, "failed setNotificationsShown: ", e);
- }
- }
-
- protected boolean isCurrentProfile(int userId) {
- synchronized (mCurrentProfiles) {
- return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
- }
- }
-
- @Override
- public String getCurrentMediaNotificationKey() {
- return null;
- }
-
- @Override
- public NotificationGroupManager getGroupManager() {
- return mGroupManager;
- }
-
- /**
- * Takes the necessary steps to prepare the status bar for starting an activity, then starts it.
- * @param action A dismiss action that is called if it's safe to start the activity.
- * @param afterKeyguardGone Whether the action should be executed after the Keyguard is gone.
- */
- protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) {
- action.onDismiss();
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- final float fontScale = newConfig.fontScale;
- final int density = newConfig.densityDpi;
- if (density != mDensity || mFontScale != fontScale) {
- onDensityOrFontScaleChanged();
- mDensity = density;
- mFontScale = fontScale;
- }
- }
-
- protected void onDensityOrFontScaleChanged() {
- ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
- for (int i = 0; i < activeNotifications.size(); i++) {
- Entry entry = activeNotifications.get(i);
- boolean exposedGuts = entry.row.getGuts() == mNotificationGutsExposed;
- entry.row.reInflateViews();
- if (exposedGuts) {
- mNotificationGutsExposed = entry.row.getGuts();
- bindGuts(entry.row, mGutsMenuItem);
- }
- inflateViews(entry, mStackScroller);
- }
- }
-
- protected void bindDismissRunnable(final ExpandableNotificationRow row) {
- row.setOnDismissRunnable(() -> performRemoveNotification(row.getStatusBarNotification()));
- }
-
- protected void performRemoveNotification(StatusBarNotification n) {
- final String pkg = n.getPackageName();
- final String tag = n.getTag();
- final int id = n.getId();
- final int userId = n.getUserId();
- try {
- mBarService.onNotificationClear(pkg, tag, id, userId);
- if (FORCE_REMOTE_INPUT_HISTORY
- && mKeysKeptForRemoteInput.contains(n.getKey())) {
- mKeysKeptForRemoteInput.remove(n.getKey());
- }
- removeNotification(n.getKey(), null);
-
- } catch (RemoteException ex) {
- // system process is dead if we're here.
- }
- }
-
-
- protected void applyColorsAndBackgrounds(StatusBarNotification sbn,
- NotificationData.Entry entry) {
-
- if (entry.getContentView().getId()
- != com.android.internal.R.id.status_bar_latest_event_content) {
- // Using custom RemoteViews
- if (entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
- && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP) {
- entry.row.setShowingLegacyBackground(true);
- entry.legacy = true;
- }
- }
-
- entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
- }
-
- public boolean isMediaNotification(NotificationData.Entry entry) {
- // TODO: confirm that there's a valid media key
- return entry.getExpandedContentView() != null &&
- entry.getExpandedContentView()
- .findViewById(com.android.internal.R.id.media_actions) != null;
- }
-
- // The (i) button in the guts that links to the system notification settings for that app
- private void startAppNotificationSettingsActivity(String packageName, final int appUid) {
- final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
- intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
- intent.putExtra(Settings.EXTRA_APP_UID, appUid);
- startNotificationGutsIntent(intent, appUid);
- }
-
- private void startNotificationGutsIntent(final Intent intent, final int appUid) {
- dismissKeyguardThenExecute(new OnDismissAction() {
- @Override
- public boolean onDismiss() {
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- TaskStackBuilder.create(mContext)
- .addNextIntentWithParentStack(intent)
- .startActivities(getActivityOptions(),
- new UserHandle(UserHandle.getUserId(appUid)));
- }
- });
- animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
- return true;
- }
- }, false /* afterKeyguardGone */);
- }
-
- protected void setNotificationSnoozed(StatusBarNotification sbn, long snoozeUntil) {
- mNotificationListener.snoozeNotification(sbn.getKey(), snoozeUntil);
- }
-
- public SnoozeListener getSnoozeListener() {
- return null;
- }
-
- private void bindGuts(final ExpandableNotificationRow row, MenuItem item) {
- row.inflateGuts();
- row.setGutsView(item);
- final StatusBarNotification sbn = row.getStatusBarNotification();
- row.setTag(sbn.getPackageName());
- final NotificationGuts guts = row.getGuts();
- guts.setClosedListener((NotificationGuts g) -> {
- if (!row.isRemoved()) {
- mStackScroller.onHeightChanged(row, !isPanelFullyCollapsed() /* needsAnimation */);
- }
- mNotificationGutsExposed = null;
- });
-
- if (item.gutsContent instanceof SnoozeGutsContent) {
- ((SnoozeGutsContent) item.gutsContent).setSnoozeListener(getSnoozeListener());
- ((SnoozeGutsContent) item.gutsContent).setStatusBarNotification(sbn);
- }
-
- if (item.gutsContent instanceof NotificationInfo) {
- final NotificationChannel channel = row.getEntry().channel;
- PackageManager pmUser = getPackageManagerForUser(mContext,
- sbn.getUser().getIdentifier());
- final INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
- ServiceManager.getService(Context.NOTIFICATION_SERVICE));
- final String pkg = sbn.getPackageName();
- NotificationInfo info = (NotificationInfo) item.gutsContent;
- final NotificationInfo.OnSettingsClickListener onSettingsClick = (View v,
- int appUid) -> {
- MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_INFO);
- guts.resetFalsingCheck();
- startAppNotificationSettingsActivity(pkg, appUid);
- };
- final View.OnClickListener onDoneClick = (View v) -> {
- // If the user has security enabled, show challenge if the setting is changed.
- if (info.hasImportanceChanged()
- && isLockscreenPublicMode(sbn.getUser().getIdentifier())
- && (mState == StatusBarState.KEYGUARD
- || mState == StatusBarState.SHADE_LOCKED)) {
- OnDismissAction dismissAction = new OnDismissAction() {
- @Override
- public boolean onDismiss() {
- saveAndCloseNotificationMenu(info, row, guts, v);
- return true;
- }
- };
- onLockedNotificationImportanceChange(dismissAction);
- } else {
- saveAndCloseNotificationMenu(info, row, guts, v);
- }
- };
- info.bindNotification(pmUser, iNotificationManager, sbn, channel, onSettingsClick,
- onDoneClick,
- mNonBlockablePkgs);
- }
- }
-
- private void saveAndCloseNotificationMenu(NotificationInfo info,
- ExpandableNotificationRow row, NotificationGuts guts, View done) {
- guts.resetFalsingCheck();
- info.saveImportance();
- int[] rowLocation = new int[2];
- int[] doneLocation = new int[2];
- row.getLocationOnScreen(rowLocation);
- done.getLocationOnScreen(doneLocation);
-
- final int centerX = done.getWidth() / 2;
- final int centerY = done.getHeight() / 2;
- final int x = doneLocation[0] - rowLocation[0] + centerX;
- final int y = doneLocation[1] - rowLocation[1] + centerY;
- dismissPopups(x, y);
- }
-
- protected SwipeHelper.LongPressListener getNotificationLongClicker() {
- return new SwipeHelper.LongPressListener() {
- @Override
- public boolean onLongPress(View v, final int x, final int y,
- MenuItem item) {
- if (!(v instanceof ExpandableNotificationRow)) {
- return false;
- }
- if (v.getWindowToken() == null) {
- Log.e(TAG, "Trying to show notification guts, but not attached to window");
- return false;
- }
-
- final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
- bindGuts(row, item);
- NotificationGuts guts = row.getGuts();
-
- // Assume we are a status_bar_notification_row
- if (guts == null) {
- // This view has no guts. Examples are the more card or the dismiss all view
- return false;
- }
-
- // Already showing?
- if (guts.getVisibility() == View.VISIBLE) {
- dismissPopups(x, y);
- return false;
- }
-
- MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_CONTROLS);
-
- // ensure that it's laid but not visible until actually laid out
- guts.setVisibility(View.INVISIBLE);
- // Post to ensure the the guts are properly laid out.
- guts.post(new Runnable() {
- @Override
- public void run() {
- if (row.getWindowToken() == null) {
- Log.e(TAG, "Trying to show notification guts, but not attached to "
- + "window");
- return;
- }
- dismissPopups(-1 /* x */, -1 /* y */, false /* resetGear */,
- false /* animate */);
- guts.setVisibility(View.VISIBLE);
- final double horz = Math.max(guts.getWidth() - x, x);
- final double vert = Math.max(guts.getHeight() - y, y);
- final float r = (float) Math.hypot(horz, vert);
- final Animator a
- = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
- a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- a.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- // Move the notification view back over the gear
- row.resetTranslation();
- }
- });
- a.start();
- guts.setExposed(true /* exposed */,
- mState == StatusBarState.KEYGUARD /* needsFalsingProtection */);
- row.closeRemoteInput();
- mStackScroller.onHeightChanged(row, true /* needsAnimation */);
- mNotificationGutsExposed = guts;
- mGutsMenuItem = item;
- }
- });
- return true;
- }
- };
- }
-
- /**
- * Returns the exposed NotificationGuts or null if none are exposed.
- */
- public NotificationGuts getExposedGuts() {
- return mNotificationGutsExposed;
- }
-
- public void dismissPopups() {
- dismissPopups(-1 /* x */, -1 /* y */, true /* resetGear */, false /* animate */);
- }
-
- private void dismissPopups(int x, int y) {
- dismissPopups(x, y, true /* resetGear */, false /* animate */);
- }
-
- public void dismissPopups(int x, int y, boolean resetGear, boolean animate) {
- if (mNotificationGutsExposed != null) {
- mNotificationGutsExposed.closeControls(x, y, true /* save */);
- }
- if (resetGear) {
- mStackScroller.resetExposedGearView(animate, true /* force */);
- }
- }
-
- @Override
- public void showRecentApps(boolean triggeredFromAltTab, boolean fromHome) {
- int msg = MSG_SHOW_RECENT_APPS;
- mHandler.removeMessages(msg);
- mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0, fromHome ? 1 : 0).sendToTarget();
- }
-
- @Override
- public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
- int msg = MSG_HIDE_RECENT_APPS;
- mHandler.removeMessages(msg);
- mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0,
- triggeredFromHomeKey ? 1 : 0).sendToTarget();
- }
-
- @Override
- public void toggleRecentApps() {
- toggleRecents();
- }
-
- @Override
- public void toggleSplitScreen() {
- toggleSplitScreenMode(-1 /* metricsDockAction */, -1 /* metricsUndockAction */);
- }
-
- @Override
- public void preloadRecentApps() {
- int msg = MSG_PRELOAD_RECENT_APPS;
- mHandler.removeMessages(msg);
- mHandler.sendEmptyMessage(msg);
- }
-
- @Override
- public void cancelPreloadRecentApps() {
- int msg = MSG_CANCEL_PRELOAD_RECENT_APPS;
- mHandler.removeMessages(msg);
- mHandler.sendEmptyMessage(msg);
- }
-
- @Override
- public void dismissKeyboardShortcutsMenu() {
- int msg = MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU;
- mHandler.removeMessages(msg);
- mHandler.sendEmptyMessage(msg);
- }
-
- @Override
- public void toggleKeyboardShortcutsMenu(int deviceId) {
- int msg = MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU;
- mHandler.removeMessages(msg);
- mHandler.obtainMessage(msg, deviceId, 0).sendToTarget();
- }
-
- /** Jumps to the next affiliated task in the group. */
- public void showNextAffiliatedTask() {
- int msg = MSG_SHOW_NEXT_AFFILIATED_TASK;
- mHandler.removeMessages(msg);
- mHandler.sendEmptyMessage(msg);
- }
-
- /** Jumps to the previous affiliated task in the group. */
- public void showPreviousAffiliatedTask() {
- int msg = MSG_SHOW_PREV_AFFILIATED_TASK;
- mHandler.removeMessages(msg);
- mHandler.sendEmptyMessage(msg);
- }
-
- protected H createHandler() {
- return new H();
- }
-
- protected void sendCloseSystemWindows(String reason) {
- try {
- ActivityManager.getService().closeSystemDialogs(reason);
- } catch (RemoteException e) {
- }
- }
-
- protected abstract View getStatusBarView();
-
- /**
- * Toggle docking the app window
- *
- * @param metricsDockAction the action to log when docking is successful, or -1 to not log
- * anything on successful docking
- * @param metricsUndockAction the action to log when undocking, or -1 to not log anything when
- * undocking
- * @return true if toggle split screen was successful
- */
- protected abstract boolean toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction);
-
- /** Proxy for RecentsComponent */
-
- protected void showRecents(boolean triggeredFromAltTab, boolean fromHome) {
- if (mRecents != null) {
- sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
- mRecents.showRecents(triggeredFromAltTab, fromHome);
- }
- }
-
- protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
- if (mRecents != null) {
- mRecents.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
- }
- }
-
- protected void toggleRecents() {
- if (mRecents != null) {
- mRecents.toggleRecents(mDisplay);
- }
- }
-
- protected void preloadRecents() {
- if (mRecents != null) {
- mRecents.preloadRecents();
- }
- }
-
- protected void toggleKeyboardShortcuts(int deviceId) {
- KeyboardShortcuts.toggle(mContext, deviceId);
- }
-
- protected void dismissKeyboardShortcuts() {
- KeyboardShortcuts.dismiss();
- }
-
- protected void cancelPreloadingRecents() {
- if (mRecents != null) {
- mRecents.cancelPreloadingRecents();
- }
- }
-
- protected void showRecentsNextAffiliatedTask() {
- if (mRecents != null) {
- mRecents.showNextAffiliatedTask();
- }
- }
-
- protected void showRecentsPreviousAffiliatedTask() {
- if (mRecents != null) {
- mRecents.showPrevAffiliatedTask();
- }
- }
-
- /**
- * If there is an active heads-up notification and it has a fullscreen intent, fire it now.
- */
- public abstract void maybeEscalateHeadsUp();
-
- /**
- * Save the current "public" (locked and secure) state of the lockscreen.
- */
- public void setLockscreenPublicMode(boolean publicMode, int userId) {
- mLockscreenPublicMode.put(userId, publicMode);
- }
-
- public boolean isLockscreenPublicMode(int userId) {
- return mLockscreenPublicMode.get(userId, false);
- }
-
- protected void onWorkChallengeChanged() {}
-
- /**
- * Has the given user chosen to allow notifications to be shown even when the lockscreen is in
- * "public" (secure & locked) mode?
- */
- public boolean userAllowsNotificationsInPublic(int userHandle) {
- if (userHandle == UserHandle.USER_ALL) {
- return true;
- }
-
- if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
- final boolean allowed = 0 != Settings.Secure.getIntForUser(
- mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle);
- mUsersAllowingNotifications.append(userHandle, allowed);
- return allowed;
- }
-
- return mUsersAllowingNotifications.get(userHandle);
- }
-
- /**
- * Has the given user chosen to allow their private (full) notifications to be shown even
- * when the lockscreen is in "public" (secure & locked) mode?
- */
- public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
- if (userHandle == UserHandle.USER_ALL) {
- return true;
- }
-
- if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
- final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
- mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
- final boolean allowedByDpm = adminAllowsUnredactedNotifications(userHandle);
- final boolean allowed = allowedByUser && allowedByDpm;
- mUsersAllowingPrivateNotifications.append(userHandle, allowed);
- return allowed;
- }
-
- return mUsersAllowingPrivateNotifications.get(userHandle);
- }
-
- private boolean adminAllowsUnredactedNotifications(int userHandle) {
- if (userHandle == UserHandle.USER_ALL) {
- return true;
- }
- final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */,
- userHandle);
- return (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0;
- }
-
- /**
- * Returns true if we're on a secure lockscreen and the user wants to hide notification data.
- * If so, notifications should be hidden.
- */
- @Override // NotificationData.Environment
- public boolean shouldHideNotifications(int userId) {
- return isLockscreenPublicMode(userId) && !userAllowsNotificationsInPublic(userId)
- || (userId != mCurrentUserId && shouldHideNotifications(mCurrentUserId));
- }
-
- /**
- * Returns true if we're on a secure lockscreen and the user wants to hide notifications via
- * package-specific override.
- */
- @Override // NotificationDate.Environment
- public boolean shouldHideNotifications(String key) {
- return isLockscreenPublicMode(mCurrentUserId)
- && mNotificationData.getVisibilityOverride(key) == Notification.VISIBILITY_SECRET;
- }
-
- /**
- * Returns true if we're on a secure lockscreen.
- */
- @Override // NotificationData.Environment
- public boolean isSecurelyLocked(int userId) {
- return isLockscreenPublicMode(userId);
- }
-
- public void onNotificationClear(StatusBarNotification notification) {
- try {
- mBarService.onNotificationClear(
- notification.getPackageName(),
- notification.getTag(),
- notification.getId(),
- notification.getUserId());
- } catch (android.os.RemoteException ex) {
- // oh well
- }
- }
-
- /**
- * Called when the notification panel layouts
- */
- public void onPanelLaidOut() {
- if (mState == StatusBarState.KEYGUARD) {
- // Since the number of notifications is determined based on the height of the view, we
- // need to update them.
- int maxBefore = getMaxKeyguardNotifications(false /* recompute */);
- int maxNotifications = getMaxKeyguardNotifications(true /* recompute */);
- if (maxBefore != maxNotifications) {
- updateRowStates();
- }
- }
- }
-
- protected void onLockedNotificationImportanceChange(OnDismissAction dismissAction) {}
-
- protected void onLockedRemoteInput(ExpandableNotificationRow row, View clickedView) {}
-
- protected void onLockedWorkRemoteInput(int userId, ExpandableNotificationRow row,
- View clicked) {}
-
- @Override
- public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
- }
-
- protected class H extends Handler {
- @Override
- public void handleMessage(Message m) {
- switch (m.what) {
- case MSG_SHOW_RECENT_APPS:
- showRecents(m.arg1 > 0, m.arg2 != 0);
- break;
- case MSG_HIDE_RECENT_APPS:
- hideRecents(m.arg1 > 0, m.arg2 > 0);
- break;
- case MSG_TOGGLE_RECENTS_APPS:
- toggleRecents();
- break;
- case MSG_PRELOAD_RECENT_APPS:
- preloadRecents();
- break;
- case MSG_CANCEL_PRELOAD_RECENT_APPS:
- cancelPreloadingRecents();
- break;
- case MSG_SHOW_NEXT_AFFILIATED_TASK:
- showRecentsNextAffiliatedTask();
- break;
- case MSG_SHOW_PREV_AFFILIATED_TASK:
- showRecentsPreviousAffiliatedTask();
- break;
- case MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU:
- toggleKeyboardShortcuts(m.arg1);
- break;
- case MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU:
- dismissKeyboardShortcuts();
- break;
- }
- }
- }
-
- protected void workAroundBadLayerDrawableOpacity(View v) {
- }
-
- protected boolean inflateViews(Entry entry, ViewGroup parent) {
- PackageManager pmUser = getPackageManagerForUser(mContext,
- entry.notification.getUser().getIdentifier());
-
- final StatusBarNotification sbn = entry.notification;
- boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
- try {
- entry.cacheContentViews(mContext, null, isLowPriority);
- } catch (RuntimeException e) {
- Log.e(TAG, "Unable to get notification remote views", e);
- return false;
- }
-
- final RemoteViews contentView = entry.cachedContentView;
- final RemoteViews bigContentView = entry.cachedBigContentView;
- final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView;
- final RemoteViews publicContentView = entry.cachedPublicContentView;
- final RemoteViews ambientContentView = entry.cachedAmbientContentView;
-
- if (contentView == null) {
- Log.v(TAG, "no contentView for: " + sbn.getNotification());
- return false;
- }
-
- if (DEBUG) {
- Log.v(TAG, "publicContentView: " + publicContentView);
- }
-
- ExpandableNotificationRow row;
-
- // Stash away previous user expansion state so we can restore it at
- // the end.
- boolean hasUserChangedExpansion = false;
- boolean userExpanded = false;
- boolean userLocked = false;
-
- if (entry.row != null) {
- row = entry.row;
- hasUserChangedExpansion = row.hasUserChangedExpansion();
- userExpanded = row.isUserExpanded();
- userLocked = row.isUserLocked();
- entry.reset();
- if (hasUserChangedExpansion) {
- row.setUserExpanded(userExpanded);
- }
- } else {
- // create the row view
- LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row,
- parent, false);
- row.setExpansionLogger(this, entry.notification.getKey());
- row.setGroupManager(mGroupManager);
- row.setHeadsUpManager(mHeadsUpManager);
- row.setRemoteInputController(mRemoteInputController);
- row.setOnExpandClickListener(this);
-
- // Get the app name.
- // Note that Notification.Builder#bindHeaderAppName has similar logic
- // but since this field is used in the guts, it must be accurate.
- // Therefore we will only show the application label, or, failing that, the
- // package name. No substitutions.
- final String pkg = sbn.getPackageName();
- String appname = pkg;
- try {
- final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
- PackageManager.MATCH_UNINSTALLED_PACKAGES
- | PackageManager.MATCH_DISABLED_COMPONENTS);
- if (info != null) {
- appname = String.valueOf(pmUser.getApplicationLabel(info));
- }
- } catch (NameNotFoundException e) {
- // Do nothing
- }
- row.setAppName(appname);
- }
-
- workAroundBadLayerDrawableOpacity(row);
- bindDismissRunnable(row);
- row.setIsLowPriority(isLowPriority);
-
- // NB: the large icon is now handled entirely by the template
-
- // bind the click event to the content area
- NotificationContentView contentContainer = row.getPrivateLayout();
- NotificationContentView contentContainerPublic = row.getPublicLayout();
-
- row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
- if (ENABLE_REMOTE_INPUT) {
- row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
- }
-
- mNotificationClicker.register(row, sbn);
-
- // set up the adaptive layout
- View contentViewLocal = null;
- View bigContentViewLocal = null;
- View headsUpContentViewLocal = null;
- View publicViewLocal = null;
- View ambientViewLocal = null;
- try {
- contentViewLocal = contentView.apply(
- sbn.getPackageContext(mContext),
- contentContainer,
- mOnClickHandler);
- if (bigContentView != null) {
- bigContentViewLocal = bigContentView.apply(
- sbn.getPackageContext(mContext),
- contentContainer,
- mOnClickHandler);
- }
- if (headsUpContentView != null) {
- headsUpContentViewLocal = headsUpContentView.apply(
- sbn.getPackageContext(mContext),
- contentContainer,
- mOnClickHandler);
- }
- if (publicContentView != null) {
- publicViewLocal = publicContentView.apply(
- sbn.getPackageContext(mContext),
- contentContainerPublic, mOnClickHandler);
- }
- if (ambientContentView != null) {
- ambientViewLocal = ambientContentView.apply(
- sbn.getPackageContext(mContext),
- contentContainer, mOnClickHandler);
- }
-
- if (contentViewLocal != null) {
- contentViewLocal.setIsRootNamespace(true);
- contentContainer.setContractedChild(contentViewLocal);
- }
- if (bigContentViewLocal != null) {
- bigContentViewLocal.setIsRootNamespace(true);
- contentContainer.setExpandedChild(bigContentViewLocal);
- }
- if (headsUpContentViewLocal != null) {
- headsUpContentViewLocal.setIsRootNamespace(true);
- contentContainer.setHeadsUpChild(headsUpContentViewLocal);
- }
- if (publicViewLocal != null) {
- publicViewLocal.setIsRootNamespace(true);
- contentContainerPublic.setContractedChild(publicViewLocal);
- }
-
- if (ambientViewLocal != null) {
- ambientViewLocal.setIsRootNamespace(true);
- contentContainer.setAmbientChild(ambientViewLocal);
- }
- }
- catch (RuntimeException e) {
- final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
- Log.e(TAG, "couldn't inflate view for notification " + ident, e);
- return false;
- }
-
- // Extract target SDK version.
- try {
- ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
- entry.targetSdk = info.targetSdkVersion;
- } catch (NameNotFoundException ex) {
- Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
- }
- entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
-
- entry.row = row;
- entry.row.setOnActivatedListener(this);
- entry.row.setExpandable(bigContentViewLocal != null);
-
- applyColorsAndBackgrounds(sbn, entry);
-
- // Restore previous flags.
- if (hasUserChangedExpansion) {
- // Note: setUserExpanded() conveniently ignores calls with
- // userExpanded=true if !isExpandable().
- row.setUserExpanded(userExpanded);
- }
- row.setUserLocked(userLocked);
- row.onNotificationUpdated(entry);
- return true;
- }
-
- /**
- * Adds RemoteInput actions from the WearableExtender; to be removed once more apps support this
- * via first-class API.
- *
- * TODO: Remove once enough apps specify remote inputs on their own.
- */
- private void processForRemoteInput(Notification n) {
- if (!ENABLE_REMOTE_INPUT) return;
-
- if (n.extras != null && n.extras.containsKey("android.wearable.EXTENSIONS") &&
- (n.actions == null || n.actions.length == 0)) {
- Notification.Action viableAction = null;
- Notification.WearableExtender we = new Notification.WearableExtender(n);
-
- List<Notification.Action> actions = we.getActions();
- final int numActions = actions.size();
-
- for (int i = 0; i < numActions; i++) {
- Notification.Action action = actions.get(i);
- if (action == null) {
- continue;
- }
- RemoteInput[] remoteInputs = action.getRemoteInputs();
- if (remoteInputs == null) {
- continue;
- }
- for (RemoteInput ri : remoteInputs) {
- if (ri.getAllowFreeFormInput()) {
- viableAction = action;
- break;
- }
- }
- if (viableAction != null) {
- break;
- }
- }
-
- if (viableAction != null) {
- Notification.Builder rebuilder = Notification.Builder.recoverBuilder(mContext, n);
- rebuilder.setActions(viableAction);
- rebuilder.build(); // will rewrite n
- }
- }
- }
-
- public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
- if (!isDeviceProvisioned()) return;
-
- final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
- final boolean afterKeyguardGone = intent.isActivity()
- && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
- mCurrentUserId);
- dismissKeyguardThenExecute(new OnDismissAction() {
- @Override
- public boolean onDismiss() {
- new Thread() {
- @Override
- public void run() {
- try {
- // The intent we are sending is for the application, which
- // won't have permission to immediately start an activity after
- // the user switches to home. We know it is safe to do at this
- // point, so make sure new activity switches are now allowed.
- ActivityManager.getService().resumeAppSwitches();
- } catch (RemoteException e) {
- }
- try {
- intent.send(null, 0, null, null, null, null, getActivityOptions());
- } catch (PendingIntent.CanceledException e) {
- // the stack trace isn't very helpful here.
- // Just log the exception message.
- Log.w(TAG, "Sending intent failed: " + e);
-
- // TODO: Dismiss Keyguard.
- }
- if (intent.isActivity()) {
- mAssistManager.hideAssist();
- }
- }
- }.start();
-
- // close the shade if it was open
- animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
- true /* force */, true /* delayed */);
- visibilityChanged(false);
-
- return true;
- }
- }, afterKeyguardGone);
- }
-
- public void addPostCollapseAction(Runnable r) {
- }
-
- public boolean isCollapsing() {
- return false;
- }
-
- public void wakeUpIfDozing(long time, PointF where) {
- }
-
- private final class NotificationClicker implements View.OnClickListener {
- private final int[] mTmpInt2 = new int[2];
-
- @Override
- public void onClick(final View v) {
- if (!(v instanceof ExpandableNotificationRow)) {
- Log.e(TAG, "NotificationClicker called on a view that is not a notification row.");
- return;
- }
-
- v.getLocationInWindow(mTmpInt2);
- wakeUpIfDozing(SystemClock.uptimeMillis(),
- new PointF(mTmpInt2[0] + v.getWidth() / 2, mTmpInt2[1] + v.getHeight() / 2));
-
- final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
- final StatusBarNotification sbn = row.getStatusBarNotification();
- if (sbn == null) {
- Log.e(TAG, "NotificationClicker called on an unclickable notification,");
- return;
- }
-
- // Check if the notification is displaying the gear, if so slide notification back
- if (row.getSettingsRow() != null && row.getSettingsRow().isVisible()) {
- row.animateTranslateNotification(0);
- return;
- }
-
- Notification notification = sbn.getNotification();
- final PendingIntent intent = notification.contentIntent != null
- ? notification.contentIntent
- : notification.fullScreenIntent;
- final String notificationKey = sbn.getKey();
-
- // Mark notification for one frame.
- row.setJustClicked(true);
- DejankUtils.postAfterTraversal(new Runnable() {
- @Override
- public void run() {
- row.setJustClicked(false);
- }
- });
-
- final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
- final boolean afterKeyguardGone = intent.isActivity()
- && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
- mCurrentUserId);
- dismissKeyguardThenExecute(new OnDismissAction() {
- @Override
- public boolean onDismiss() {
- if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) {
- // Release the HUN notification to the shade.
-
- if (isPanelFullyCollapsed()) {
- HeadsUpManager.setIsClickedNotification(row, true);
- }
- //
- // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
- // become canceled shortly by NoMan, but we can't assume that.
- mHeadsUpManager.releaseImmediately(notificationKey);
- }
- StatusBarNotification parentToCancel = null;
- if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
- StatusBarNotification summarySbn = mGroupManager.getLogicalGroupSummary(sbn)
- .getStatusBarNotification();
- if (shouldAutoCancel(summarySbn)) {
- parentToCancel = summarySbn;
- }
- }
- final StatusBarNotification parentToCancelFinal = parentToCancel;
- new Thread() {
- @Override
- public void run() {
- try {
- // The intent we are sending is for the application, which
- // won't have permission to immediately start an activity after
- // the user switches to home. We know it is safe to do at this
- // point, so make sure new activity switches are now allowed.
- ActivityManager.getService().resumeAppSwitches();
- } catch (RemoteException e) {
- }
- if (intent != null) {
- // If we are launching a work activity and require to launch
- // separate work challenge, we defer the activity action and cancel
- // notification until work challenge is unlocked.
- if (intent.isActivity()) {
- final int userId = intent.getCreatorUserHandle()
- .getIdentifier();
- if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
- && mKeyguardManager.isDeviceLocked(userId)) {
- boolean canBypass = false;
- try {
- canBypass = ActivityManager.getService()
- .canBypassWorkChallenge(intent);
- } catch (RemoteException e) {
- }
- // For direct-boot aware activities, they can be shown when
- // the device is still locked without triggering the work
- // challenge.
- if ((!canBypass) && startWorkChallengeIfNecessary(userId,
- intent.getIntentSender(), notificationKey)) {
- // Show work challenge, do not run PendingIntent and
- // remove notification
- return;
- }
- }
- }
- try {
- intent.send(null, 0, null, null, null, null,
- getActivityOptions());
- } catch (PendingIntent.CanceledException e) {
- // the stack trace isn't very helpful here.
- // Just log the exception message.
- Log.w(TAG, "Sending contentIntent failed: " + e);
-
- // TODO: Dismiss Keyguard.
- }
- if (intent.isActivity()) {
- mAssistManager.hideAssist();
- }
- }
-
- try {
- mBarService.onNotificationClick(notificationKey);
- } catch (RemoteException ex) {
- // system process is dead if we're here.
- }
- if (parentToCancelFinal != null) {
- // We have to post it to the UI thread for synchronization
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- Runnable removeRunnable = new Runnable() {
- @Override
- public void run() {
- performRemoveNotification(parentToCancelFinal);
- }
- };
- if (isCollapsing()) {
- // To avoid lags we're only performing the remove
- // after the shade was collapsed
- addPostCollapseAction(removeRunnable);
- } else {
- removeRunnable.run();
- }
- }
- });
- }
- }
- }.start();
-
- // close the shade if it was open
- animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
- true /* force */, true /* delayed */);
- visibilityChanged(false);
-
- return true;
- }
- }, afterKeyguardGone);
- }
-
- private boolean shouldAutoCancel(StatusBarNotification sbn) {
- int flags = sbn.getNotification().flags;
- if ((flags & Notification.FLAG_AUTO_CANCEL) != Notification.FLAG_AUTO_CANCEL) {
- return false;
- }
- if ((flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
- return false;
- }
- return true;
- }
-
- public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
- Notification notification = sbn.getNotification();
- if (notification.contentIntent != null || notification.fullScreenIntent != null) {
- row.setOnClickListener(this);
- } else {
- row.setOnClickListener(null);
- }
- }
- }
-
- public void animateCollapsePanels(int flags, boolean force) {
- }
-
- public void animateCollapsePanels(int flags, boolean force, boolean delayed) {
- }
-
- protected boolean startWorkChallengeIfNecessary(int userId, IntentSender intendSender,
- String notificationKey) {
- final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null,
- null, userId);
- if (newIntent == null) {
- return false;
- }
- final Intent callBackIntent = new Intent(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
- callBackIntent.putExtra(Intent.EXTRA_INTENT, intendSender);
- callBackIntent.putExtra(Intent.EXTRA_INDEX, notificationKey);
- callBackIntent.setPackage(mContext.getPackageName());
-
- PendingIntent callBackPendingIntent = PendingIntent.getBroadcast(
- mContext,
- 0,
- callBackIntent,
- PendingIntent.FLAG_CANCEL_CURRENT |
- PendingIntent.FLAG_ONE_SHOT |
- PendingIntent.FLAG_IMMUTABLE);
- newIntent.putExtra(
- Intent.EXTRA_INTENT,
- callBackPendingIntent.getIntentSender());
- try {
- ActivityManager.getService().startConfirmDeviceCredentialIntent(newIntent);
- } catch (RemoteException ex) {
- // ignore
- }
- return true;
- }
-
- protected Bundle getActivityOptions() {
- // Anything launched from the notification shade should always go into the
- // fullscreen stack.
- ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchStackId(StackId.FULLSCREEN_WORKSPACE_STACK_ID);
- return options.toBundle();
- }
-
- protected void visibilityChanged(boolean visible) {
- if (mVisible != visible) {
- mVisible = visible;
- if (!visible) {
- dismissPopups();
- }
- }
- updateVisibleToUser();
- }
-
- protected void updateVisibleToUser() {
- boolean oldVisibleToUser = mVisibleToUser;
- mVisibleToUser = mVisible && mDeviceInteractive;
-
- if (oldVisibleToUser != mVisibleToUser) {
- handleVisibleToUserChanged(mVisibleToUser);
- }
- }
-
- /**
- * The LEDs are turned off when the notification panel is shown, even just a little bit.
- * See also PhoneStatusBar.setPanelExpanded for another place where we attempt to do this.
- */
- protected void handleVisibleToUserChanged(boolean visibleToUser) {
- try {
- if (visibleToUser) {
- boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
- boolean clearNotificationEffects =
- !isPanelFullyCollapsed() &&
- (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED);
- int notificationLoad = mNotificationData.getActiveNotifications().size();
- if (pinnedHeadsUp && isPanelFullyCollapsed()) {
- notificationLoad = 1;
- } else {
- MetricsLogger.histogram(mContext, "note_load", notificationLoad);
- }
- mBarService.onPanelRevealed(clearNotificationEffects, notificationLoad);
- } else {
- mBarService.onPanelHidden();
- }
- } catch (RemoteException ex) {
- // Won't fail unless the world has ended.
- }
- }
-
- /**
- * Clear Buzz/Beep/Blink.
- */
- public void clearNotificationEffects() {
- try {
- mBarService.clearNotificationEffects();
- } catch (RemoteException e) {
- // Won't fail unless the world has ended.
- }
- }
-
- public abstract boolean isPanelFullyCollapsed();
-
- /**
- * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
- * about the failure.
- *
- * WARNING: this will call back into us. Don't hold any locks.
- */
- void handleNotificationError(StatusBarNotification n, String message) {
- removeNotification(n.getKey(), null);
- try {
- mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
- n.getInitialPid(), message, n.getUserId());
- } catch (RemoteException ex) {
- // The end is nigh.
- }
- }
-
- protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) {
- NotificationData.Entry entry = mNotificationData.remove(key, ranking);
- if (entry == null) {
- Log.w(TAG, "removeNotification for unknown key: " + key);
- return null;
- }
- updateNotifications();
- return entry.notification;
- }
-
- protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) {
- if (DEBUG) {
- Log.d(TAG, "createNotificationViews(notification=" + sbn);
- }
- NotificationData.Entry entry = new NotificationData.Entry(sbn);
- try {
- entry.createIcons(mContext, sbn);
- } catch (NotificationData.IconException exception) {
- handleNotificationError(sbn, exception.getMessage());
- }
-
- // Construct the expanded view.
- if (!inflateViews(entry, mStackScroller)) {
- handleNotificationError(sbn, "Couldn't expand RemoteViews for: " + sbn);
- return null;
- }
- return entry;
- }
-
- protected void addNotificationViews(Entry entry, RankingMap ranking) {
- if (entry == null) {
- return;
- }
- // Add the expanded view and icon.
- mNotificationData.add(entry, ranking);
- updateNotifications();
- }
-
- /**
- * @param recompute wheter the number should be recomputed
- * @return The number of notifications we show on Keyguard.
- */
- protected abstract int getMaxKeyguardNotifications(boolean recompute);
-
- /**
- * Updates expanded, dimmed and locked states of notification rows.
- */
- protected void updateRowStates() {
- final int N = mStackScroller.getChildCount();
-
- int visibleNotifications = 0;
- boolean onKeyguard = mState == StatusBarState.KEYGUARD;
- int maxNotifications = -1;
- if (onKeyguard) {
- maxNotifications = getMaxKeyguardNotifications(true /* recompute */);
- }
- mStackScroller.setMaxDisplayedNotifications(maxNotifications);
- Stack<ExpandableNotificationRow> stack = new Stack<>();
- for (int i = N - 1; i >= 0; i--) {
- View child = mStackScroller.getChildAt(i);
- if (!(child instanceof ExpandableNotificationRow)) {
- continue;
- }
- stack.push((ExpandableNotificationRow) child);
- }
- while(!stack.isEmpty()) {
- ExpandableNotificationRow row = stack.pop();
- NotificationData.Entry entry = row.getEntry();
- boolean childNotification = mGroupManager.isChildInGroupWithSummary(entry.notification);
- if (onKeyguard) {
- row.setOnKeyguard(true);
- } else {
- row.setOnKeyguard(false);
- row.setSystemExpanded(visibleNotifications == 0 && !childNotification);
- }
- entry.row.setShowAmbient(isDozing());
- int userId = entry.notification.getUserId();
- boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
- entry.notification) && !entry.row.isRemoved();
- boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
- if (suppressedSummary
- || (isLockscreenPublicMode(userId) && !mShowLockscreenNotifications)
- || (onKeyguard && !showOnKeyguard)) {
- entry.row.setVisibility(View.GONE);
- } else {
- boolean wasGone = entry.row.getVisibility() == View.GONE;
- if (wasGone) {
- entry.row.setVisibility(View.VISIBLE);
- }
- if (!childNotification && !entry.row.isRemoved()) {
- if (wasGone) {
- // notify the scroller of a child addition
- mStackScroller.generateAddAnimation(entry.row,
- !showOnKeyguard /* fromMoreCard */);
- }
- visibleNotifications++;
- }
- }
- if (row.isSummaryWithChildren()) {
- List<ExpandableNotificationRow> notificationChildren =
- row.getNotificationChildren();
- int size = notificationChildren.size();
- for (int i = size - 1; i >= 0; i--) {
- stack.push(notificationChildren.get(i));
- }
- }
- }
-
- mStackScroller.changeViewPosition(mDismissView, mStackScroller.getChildCount() - 1);
- mStackScroller.changeViewPosition(mEmptyShadeView, mStackScroller.getChildCount() - 2);
- mStackScroller.changeViewPosition(mNotificationShelf, mStackScroller.getChildCount() - 3);
- }
-
- public boolean isDozing() {
- return false;
- }
-
- public boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
- return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey());
- }
-
- protected void setZenMode(int mode) {
- if (!isDeviceProvisioned()) return;
- mZenMode = mode;
- updateNotifications();
- }
-
- // extended in PhoneStatusBar
- protected void setShowLockscreenNotifications(boolean show) {
- mShowLockscreenNotifications = show;
- }
-
- protected void setLockScreenAllowRemoteInput(boolean allowLockscreenRemoteInput) {
- mAllowLockscreenRemoteInput = allowLockscreenRemoteInput;
- }
-
- private void updateLockscreenNotificationSetting() {
- final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
- 1,
- mCurrentUserId) != 0;
- final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
- null /* admin */, mCurrentUserId);
- final boolean allowedByDpm = (dpmFlags
- & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
-
- setShowLockscreenNotifications(show && allowedByDpm);
-
- if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
- final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
- 0,
- mCurrentUserId) != 0;
- final boolean remoteInputDpm =
- (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT) == 0;
-
- setLockScreenAllowRemoteInput(remoteInput && remoteInputDpm);
- } else {
- setLockScreenAllowRemoteInput(false);
- }
- }
-
- protected abstract void setAreThereNotifications();
- protected abstract void updateNotifications();
-
- public abstract void addNotification(StatusBarNotification notification,
- RankingMap ranking, Entry oldEntry);
- protected abstract void updateNotificationRanking(RankingMap ranking);
- public abstract void removeNotification(String key, RankingMap ranking);
-
- public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
- if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
-
- final String key = notification.getKey();
- Entry entry = mNotificationData.get(key);
- if (entry == null) {
- return;
- } else {
- mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
- mRemoteInputEntriesToRemoveOnCollapse.remove(entry);
- }
-
- Notification n = notification.getNotification();
- mNotificationData.updateRanking(ranking);
-
- boolean applyInPlace;
- try {
- applyInPlace = entry.cacheContentViews(mContext, notification.getNotification(),
- mNotificationData.isAmbient(key));
- } catch (RuntimeException e) {
- Log.e(TAG, "Unable to get notification remote views", e);
- applyInPlace = false;
- }
- boolean shouldPeek = shouldPeek(entry, notification);
- boolean alertAgain = alertAgain(entry, n);
- if (DEBUG) {
- Log.d(TAG, "applyInPlace=" + applyInPlace
- + " shouldPeek=" + shouldPeek
- + " alertAgain=" + alertAgain);
- }
-
- final StatusBarNotification oldNotification = entry.notification;
- entry.notification = notification;
- mGroupManager.onEntryUpdated(entry, oldNotification);
-
- boolean updateSuccessful = false;
- try {
- if (applyInPlace) {
- if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
- try {
- entry.updateIcons(mContext, n);
- updateNotificationViews(entry, notification);
- updateSuccessful = true;
- } catch (RuntimeException e) {
- // It failed to apply cleanly.
- Log.w(TAG, "Couldn't reapply views for package " +
- notification.getPackageName(), e);
- }
- }
- if (!updateSuccessful) {
- entry.updateIcons(mContext, n);
- if (!inflateViews(entry, mStackScroller)) {
- handleNotificationError(notification, "Couldn't update remote views for: "
- + notification);
- }
- }
- } catch (NotificationData.IconException e) {
- handleNotificationError(notification, e.getMessage());
- }
- updateHeadsUp(key, entry, shouldPeek, alertAgain);
- updateNotifications();
-
- if (!notification.isClearable()) {
- // The user may have performed a dismiss action on the notification, since it's
- // not clearable we should snap it back.
- mStackScroller.snapViewIfNeeded(entry.row);
- }
-
- if (DEBUG) {
- // Is this for you?
- boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);
- Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
- }
-
- setAreThereNotifications();
- }
-
- protected abstract void updateHeadsUp(String key, Entry entry, boolean shouldPeek,
- boolean alertAgain);
-
- private void updateNotificationViews(Entry entry, StatusBarNotification sbn) {
- final RemoteViews contentView = entry.cachedContentView;
- final RemoteViews bigContentView = entry.cachedBigContentView;
- final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView;
- final RemoteViews publicContentView = entry.cachedPublicContentView;
-
- // Reapply the RemoteViews
- contentView.reapply(mContext, entry.getContentView(), mOnClickHandler);
- if (bigContentView != null && entry.getExpandedContentView() != null) {
- bigContentView.reapply(sbn.getPackageContext(mContext),
- entry.getExpandedContentView(),
- mOnClickHandler);
- }
- View headsUpChild = entry.getHeadsUpContentView();
- if (headsUpContentView != null && headsUpChild != null) {
- headsUpContentView.reapply(sbn.getPackageContext(mContext),
- headsUpChild, mOnClickHandler);
- }
- if (publicContentView != null && entry.getPublicContentView() != null) {
- publicContentView.reapply(sbn.getPackageContext(mContext),
- entry.getPublicContentView(), mOnClickHandler);
- }
- // update the contentIntent
- mNotificationClicker.register(entry.row, sbn);
-
- entry.row.onNotificationUpdated(entry);
- entry.row.resetHeight();
- }
-
- protected void updatePublicContentView(Entry entry,
- StatusBarNotification sbn) {
- final RemoteViews publicContentView = entry.cachedPublicContentView;
- View inflatedView = entry.getPublicContentView();
- if (entry.autoRedacted && publicContentView != null && inflatedView != null) {
- final boolean disabledByPolicy =
- !adminAllowsUnredactedNotifications(entry.notification.getUserId());
- String notificationHiddenText = mContext.getString(disabledByPolicy
- ? com.android.internal.R.string.notification_hidden_by_policy_text
- : com.android.internal.R.string.notification_hidden_text);
- TextView titleView = (TextView) inflatedView.findViewById(android.R.id.title);
- if (titleView != null
- && !titleView.getText().toString().equals(notificationHiddenText)) {
- publicContentView.setTextViewText(android.R.id.title, notificationHiddenText);
- publicContentView.reapply(sbn.getPackageContext(mContext),
- inflatedView, mOnClickHandler);
- entry.row.onNotificationUpdated(entry);
- }
- }
- }
-
- protected void notifyHeadsUpScreenOff() {
- maybeEscalateHeadsUp();
- }
-
- private boolean alertAgain(Entry oldEntry, Notification newNotification) {
- return oldEntry == null || !oldEntry.hasInterrupted()
- || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
- }
-
- protected boolean shouldPeek(Entry entry) {
- return shouldPeek(entry, entry.notification);
- }
-
- protected boolean shouldPeek(Entry entry, StatusBarNotification sbn) {
- if (!mUseHeadsUp || isDeviceInVrMode()) {
- return false;
- }
-
- if (mNotificationData.shouldFilterOut(sbn)) {
- if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey());
- return false;
- }
-
- boolean inUse = mPowerManager.isScreenOn();
- try {
- inUse = inUse && !mDreamManager.isDreaming();
- } catch (RemoteException e) {
- Log.d(TAG, "failed to query dream manager", e);
- }
-
- if (!inUse && !isDozing()) {
- if (DEBUG) {
- Log.d(TAG, "No peeking: not in use: " + sbn.getKey());
- }
- return false;
- }
-
- if (mNotificationData.shouldSuppressScreenOn(sbn.getKey())) {
- if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
- return false;
- }
-
- if (entry.hasJustLaunchedFullScreenIntent()) {
- if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey());
- return false;
- }
-
- if (isSnoozedPackage(sbn)) {
- if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
- return false;
- }
-
- if (mNotificationData.getImportance(sbn.getKey()) < NotificationManager.IMPORTANCE_HIGH) {
- if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());
- return false;
- }
-
- if (sbn.getNotification().fullScreenIntent != null) {
- if (mAccessibilityManager.isTouchExplorationEnabled()) {
- if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
- return false;
- } else {
- // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent
- return !mStatusBarKeyguardViewManager.isShowing()
- || mStatusBarKeyguardViewManager.isOccluded();
- }
- }
-
- return true;
- }
-
- protected abstract boolean isSnoozedPackage(StatusBarNotification sbn);
-
- public void setInteracting(int barWindow, boolean interacting) {
- // hook for subclasses
- }
-
- public void setBouncerShowing(boolean bouncerShowing) {
- mBouncerShowing = bouncerShowing;
- }
-
- /**
- * @return Whether the security bouncer from Keyguard is showing.
- */
- public boolean isBouncerShowing() {
- return mBouncerShowing;
- }
-
- public void destroy() {
- mContext.unregisterReceiver(mBroadcastReceiver);
- try {
- mNotificationListener.unregisterAsSystemService();
- } catch (RemoteException e) {
- // Ignore.
- }
- mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener);
- }
-
- /**
- * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then
- * return PackageManager for mContext
- */
- public static PackageManager getPackageManagerForUser(Context context, int userId) {
- Context contextForUser = context;
- // UserHandle defines special userId as negative values, e.g. USER_ALL
- if (userId >= 0) {
- try {
- // Create a context for the correct user so if a package isn't installed
- // for user 0 we can still load information about the package.
- contextForUser =
- context.createPackageContextAsUser(context.getPackageName(),
- Context.CONTEXT_RESTRICTED,
- new UserHandle(userId));
- } catch (NameNotFoundException e) {
- // Shouldn't fail to find the package name for system ui.
- }
- }
- return contextForUser.getPackageManager();
- }
-
- @Override
- public void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
- try {
- mBarService.onNotificationExpansionChanged(key, userAction, expanded);
- } catch (RemoteException e) {
- // Ignore.
- }
- }
-
- public boolean isKeyguardSecure() {
- if (mStatusBarKeyguardViewManager == null) {
- // startKeyguard() hasn't been called yet, so we don't know.
- // Make sure anything that needs to know isKeyguardSecure() checks and re-checks this
- // value onVisibilityChanged().
- Slog.w(TAG, "isKeyguardSecure() called before startKeyguard(), returning false",
- new Throwable());
- return false;
- }
- return mStatusBarKeyguardViewManager.isSecure();
- }
-
- @Override
- public void showAssistDisclosure() {
- if (mAssistManager != null) {
- mAssistManager.showDisclosure();
- }
- }
-
- @Override
- public void startAssist(Bundle args) {
- if (mAssistManager != null) {
- mAssistManager.startAssist(args);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 9177f4b..d1245b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -56,6 +56,7 @@
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.stack.AnimationProperties;
import com.android.systemui.statusbar.stack.ExpandableViewState;
@@ -436,7 +437,7 @@
* @param parent the new parent notification
*/
public void setIsChildInGroup(boolean isChildInGroup, ExpandableNotificationRow parent) {;
- boolean childInGroup = BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS && isChildInGroup;
+ boolean childInGroup = StatusBar.ENABLE_CHILD_NOTIFICATIONS && isChildInGroup;
mNotificationParent = childInGroup ? parent : null;
mPrivateLayout.setIsChildInGroup(childInGroup);
resetBackgroundAlpha();
@@ -1420,7 +1421,7 @@
}
private void onChildrenCountChanged() {
- mIsSummaryWithChildren = BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS
+ mIsSummaryWithChildren = StatusBar.ENABLE_CHILD_NOTIFICATIONS
&& mChildrenContainer != null && mChildrenContainer.getNotificationChildCount() > 0;
if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() == null) {
mChildrenContainer.recreateNotificationHeader(mExpandClickListener,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 8c04a1a..bbfcf31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -25,6 +25,7 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.view.View;
@@ -34,12 +35,14 @@
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.NotificationColorUtil;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.List;
import java.util.Objects;
/**
@@ -71,6 +74,7 @@
public RemoteViews cachedPublicContentView;
public RemoteViews cachedAmbientContentView;
public CharSequence remoteInputText;
+ public List<SnoozeCriterion> snoozeCriteria;
private int mCachedContrastColor = COLOR_INVALID;
private int mCachedContrastColorIsFor = COLOR_INVALID;
@@ -456,6 +460,14 @@
return null;
}
+ public List<SnoozeCriterion> getSnoozeCriteria(String key) {
+ if (mRankingMap != null) {
+ mRankingMap.getRanking(key, mTmpRanking);
+ return mTmpRanking.getSnoozeCriteria();
+ }
+ return null;
+ }
+
public NotificationChannel getChannel(String key) {
if (mRankingMap != null) {
mRankingMap.getRanking(key, mTmpRanking);
@@ -478,6 +490,7 @@
mGroupManager.onEntryUpdated(entry, oldSbn);
}
entry.channel = getChannel(entry.key);
+ entry.snoozeCriteria = getSnoozeCriteria(entry.key);
}
}
}
@@ -506,7 +519,7 @@
Collections.sort(mSortedAndFiltered, mRankingComparator);
}
- boolean shouldFilterOut(StatusBarNotification sbn) {
+ public boolean shouldFilterOut(StatusBarNotification sbn) {
if (!(mEnvironment.isDeviceProvisioned() ||
showNotificationEvenIfUnprovisioned(sbn))) {
return true;
@@ -523,7 +536,7 @@
return true;
}
- if (!BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS
+ if (!StatusBar.ENABLE_CHILD_NOTIFICATIONS
&& mGroupManager.isChildInGroupWithSummary(sbn)) {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
index 0989846..bdbc9b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
@@ -91,11 +91,12 @@
super(context, attrs);
}
- interface OnSettingsClickListener {
+ public interface OnSettingsClickListener {
void onClick(View v, int appUid);
}
- void bindNotification(final PackageManager pm, final INotificationManager iNotificationManager,
+ public void bindNotification(final PackageManager pm,
+ final INotificationManager iNotificationManager,
final StatusBarNotification sbn, final NotificationChannel channel,
OnSettingsClickListener onSettingsClick,
OnClickListener onDoneClick, final Set<String> nonBlockablePkgs) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
index 670d73e..1992b6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
@@ -17,17 +17,21 @@
import java.util.ArrayList;
import java.util.Calendar;
+import java.util.List;
import java.util.concurrent.TimeUnit;
import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider;
import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.GutsInteractionListener;
import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.SnoozeListener;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.SnoozeOption;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
+import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
import android.view.View;
@@ -43,21 +47,17 @@
public class NotificationSnooze extends LinearLayout
implements NotificationMenuRowProvider.SnoozeGutsContent, View.OnClickListener {
+ private static final int MAX_ASSISTANT_SUGGESTIONS = 2;
private GutsInteractionListener mGutsInteractionListener;
private SnoozeListener mSnoozeListener;
private StatusBarNotification mSbn;
- private TextView mSelectedOption;
- private TextView mUndo;
+ private TextView mSelectedOptionText;
+ private TextView mUndoButton;
private ViewGroup mSnoozeOptionView;
+ private List<SnoozeOption> mSnoozeOptions;
- private long mTimeToSnooze;
-
- // Default is the first option in this list
- private static final int[] SNOOZE_OPTIONS = {
- R.string.snooze_option_15_min, R.string.snooze_option_30_min,
- R.string.snooze_option_1_hour
- };
+ private SnoozeOption mSelectedOption;
public NotificationSnooze(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -67,60 +67,88 @@
protected void onFinishInflate() {
super.onFinishInflate();
// Create the different options based on list
+ mSnoozeOptions = getDefaultSnoozeOptions();
createOptionViews();
// Snackbar
- mSelectedOption = (TextView) findViewById(R.id.snooze_option_default);
- mSelectedOption.setOnClickListener(this);
- mUndo = (TextView) findViewById(R.id.undo);
- mUndo.setOnClickListener(this);
+ mSelectedOptionText = (TextView) findViewById(R.id.snooze_option_default);
+ mSelectedOptionText.setOnClickListener(this);
+ mUndoButton = (TextView) findViewById(R.id.undo);
+ mUndoButton.setOnClickListener(this);
// Default to first option in list
- setTimeToSnooze(SNOOZE_OPTIONS[0]);
+ setSelected(mSnoozeOptions.get(0));
+ }
+
+ public void setSnoozeOptions(final List<SnoozeCriterion> snoozeList) {
+ if (snoozeList == null) {
+ return;
+ }
+ mSnoozeOptions.clear();
+ mSnoozeOptions = getDefaultSnoozeOptions();
+ final int count = Math.min(MAX_ASSISTANT_SUGGESTIONS, snoozeList.size());
+ for (int i = 0; i < count; i++) {
+ SnoozeCriterion sc = snoozeList.get(i);
+ mSnoozeOptions.add(new SnoozeOption(sc, 0, sc.getExplanation(), sc.getConfirmation()));
+ }
+ createOptionViews();
+ }
+
+ private ArrayList<SnoozeOption> getDefaultSnoozeOptions() {
+ ArrayList<SnoozeOption> options = new ArrayList<>();
+ options.add(createOption(R.string.snooze_option_15_min, 15));
+ options.add(createOption(R.string.snooze_option_30_min, 30));
+ options.add(createOption(R.string.snooze_option_1_hour, 60));
+ return options;
+ }
+
+ private SnoozeOption createOption(int descriptionResId, int minutes) {
+ Resources res = getResources();
+ String resultText = String.format(
+ res.getString(R.string.snoozed_for_time), res.getString(descriptionResId));
+ return new SnoozeOption(null, minutes, res.getString(descriptionResId), resultText);
}
private void createOptionViews() {
mSnoozeOptionView = (ViewGroup) findViewById(R.id.snooze_options);
+ mSnoozeOptionView.removeAllViews();
mSnoozeOptionView.setVisibility(View.GONE);
final Resources res = getResources();
final int textSize = res.getDimensionPixelSize(R.dimen.snooze_option_text_size);
final int p = res.getDimensionPixelSize(R.dimen.snooze_option_padding);
- for (int i = 0; i < SNOOZE_OPTIONS.length; i++) {
+
+ // Add all the options
+ for (int i = 0; i < mSnoozeOptions.size(); i++) {
+ SnoozeOption option = mSnoozeOptions.get(i);
TextView tv = new TextView(getContext());
tv.setTextColor(Color.WHITE);
tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
tv.setPadding(p, p, p, p);
mSnoozeOptionView.addView(tv);
- tv.setText(SNOOZE_OPTIONS[i]);
- tv.setTag(SNOOZE_OPTIONS[i]);
+ tv.setText(option.description);
+ tv.setTag(option);
tv.setOnClickListener(this);
}
+
+ // Add the undo option as final item
+ TextView tv = new TextView(getContext());
+ tv.setTextColor(Color.WHITE);
+ tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
+ tv.setPadding(p, p, p, p);
+ mSnoozeOptionView.addView(tv);
+ tv.setText(R.string.snooze_option_dont_snooze);
+ tv.setOnClickListener(this);
}
private void showSnoozeOptions(boolean show) {
- mSelectedOption.setVisibility(show ? View.GONE : View.VISIBLE);
- mUndo.setVisibility(show ? View.GONE : View.VISIBLE);
+ mSelectedOptionText.setVisibility(show ? View.GONE : View.VISIBLE);
+ mUndoButton.setVisibility(show ? View.GONE : View.VISIBLE);
mSnoozeOptionView.setVisibility(show ? View.VISIBLE : View.GONE);
}
- private void setTimeToSnooze(int optionId) {
- long snoozeUntilMillis = Calendar.getInstance().getTimeInMillis();
- switch (optionId) {
- case R.string.snooze_option_15_min:
- snoozeUntilMillis += TimeUnit.MINUTES.toMillis(15);
- break;
- case R.string.snooze_option_30_min:
- snoozeUntilMillis += TimeUnit.MINUTES.toMillis(30);
- break;
- case R.string.snooze_option_1_hour:
- snoozeUntilMillis += TimeUnit.MINUTES.toMillis(60);
- break;
- }
- mTimeToSnooze = snoozeUntilMillis;
- final Resources res = getResources();
- String selectedString = String.format(
- res.getString(R.string.snoozed_for_time), res.getString(optionId));
- mSelectedOption.setText(selectedString);
+ private void setSelected(SnoozeOption option) {
+ mSelectedOption = option;
+ mSelectedOptionText.setText(option.confirmation);
showSnoozeOptions(false);
}
@@ -130,19 +158,22 @@
mGutsInteractionListener.onInteraction(this);
}
final int id = v.getId();
- final Integer tag = (Integer) v.getTag();
+ final SnoozeOption tag = (SnoozeOption) v.getTag();
if (tag != null) {
- // From the option list
- setTimeToSnooze(tag);
+ setSelected(tag);
} else if (id == R.id.snooze_option_default) {
// Show more snooze options
showSnoozeOptions(true);
- } else if (id == R.id.undo) {
- mTimeToSnooze = -1;
- mGutsInteractionListener.closeGuts(this);
+ } else {
+ undoSnooze();
}
}
+ private void undoSnooze() {
+ mSelectedOption = null;
+ mGutsInteractionListener.closeGuts(this);
+ }
+
@Override
public View getContentView() {
return this;
@@ -167,9 +198,13 @@
public boolean handleCloseControls() {
// When snooze is closed (i.e. there was interaction outside of the notification)
// then we commit the snooze action.
- if (mSnoozeListener != null && mTimeToSnooze != -1) {
- mSnoozeListener.snoozeNotification(mSbn, mTimeToSnooze);
+ if (mSnoozeListener != null && mSelectedOption != null) {
+ mSnoozeListener.snoozeNotification(mSbn, mSelectedOption);
return true;
+ } else {
+ // Reset the view once it's closed
+ setSelected(mSnoozeOptions.get(0));
+ showSnoozeOptions(false);
}
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index c8e8973..9a76ad6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -273,7 +273,7 @@
@Override
public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
- String description, boolean isWide, int subId) {
+ String description, boolean isWide, int subId, boolean roaming) {
PhoneState state = getState(subId);
if (state == null) {
return;
@@ -284,6 +284,7 @@
state.mMobileDescription = statusIcon.contentDescription;
state.mMobileTypeDescription = typeContentDescription;
state.mIsMobileTypeIconWide = statusType != 0 && isWide;
+ state.mRoaming = roaming;
apply();
}
@@ -631,7 +632,8 @@
private String mMobileDescription, mMobileTypeDescription;
private ViewGroup mMobileGroup;
- private ImageView mMobile, mMobileDark, mMobileType;
+ private ImageView mMobile, mMobileDark, mMobileType, mMobileRoaming;
+ public boolean mRoaming;
public PhoneState(int subId, Context context) {
ViewGroup root = (ViewGroup) LayoutInflater.from(context)
@@ -645,6 +647,7 @@
mMobile = (ImageView) root.findViewById(R.id.mobile_signal);
mMobileDark = (ImageView) root.findViewById(R.id.mobile_signal_dark);
mMobileType = (ImageView) root.findViewById(R.id.mobile_type);
+ mMobileRoaming = (ImageView) root.findViewById(R.id.mobile_roaming);
}
public boolean apply(boolean isSecondaryIcon) {
@@ -680,6 +683,7 @@
(mMobileVisible ? "VISIBLE" : "GONE"), mMobileStrengthId, mMobileTypeId));
mMobileType.setVisibility(mMobileTypeId != 0 ? View.VISIBLE : View.GONE);
+ mMobileRoaming.setVisibility(mRoaming ? View.VISIBLE : View.GONE);
return mMobileVisible;
}
@@ -739,6 +743,8 @@
StatusBarIconController.getDarkIntensity(tintArea, mMobile, darkIntensity),
mMobile, mMobileDark);
setTint(mMobileType, StatusBarIconController.getTint(tintArea, mMobileType, tint));
+ setTint(mMobileRoaming, StatusBarIconController.getTint(tintArea, mMobileRoaming,
+ tint));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index f24e40b..f6a5687 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -39,7 +39,7 @@
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
@@ -47,7 +47,7 @@
/**
* A status bar (and navigation bar) tailored for the automotive use case.
*/
-public class CarStatusBar extends PhoneStatusBar implements
+public class CarStatusBar extends StatusBar implements
CarBatteryController.BatteryViewHandler {
private static final String TAG = "CarStatusBar";
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 c308930..67f8426 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
@@ -20,7 +20,7 @@
import com.android.systemui.statusbar.ScalingDrawableWrapper;
import com.android.systemui.statusbar.policy.BluetoothController;
-import static com.android.systemui.statusbar.phone.PhoneStatusBar.DEBUG;
+import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
/**
* Controller that monitors signal strength for a device that is connected via bluetooth.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index 8e6c153..f8b6dee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -20,7 +20,7 @@
import android.view.ViewStub;
import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.UserSwitcherController;
/**
@@ -32,7 +32,7 @@
private UserGridView mUserGridView;
private UserSwitcherController mUserSwitcherController;
- public FullscreenUserSwitcher(PhoneStatusBar statusBar,
+ public FullscreenUserSwitcher(StatusBar statusBar,
UserSwitcherController userSwitcherController,
ViewStub containerStub) {
mUserSwitcherController = userSwitcherController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridView.java
index d90a21d..137b5cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridView.java
@@ -29,12 +29,12 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.UserUtil;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.UserSwitcherController;
public class UserGridView extends GridView {
- private PhoneStatusBar mStatusBar;
+ private StatusBar mStatusBar;
private UserSwitcherController mUserSwitcherController;
private Adapter mAdapter;
private int mPendingUserId = UserHandle.USER_NULL;
@@ -43,7 +43,7 @@
super(context, attrs);
}
- public void init(PhoneStatusBar statusBar, UserSwitcherController userSwitcherController) {
+ public void init(StatusBar statusBar, UserSwitcherController userSwitcherController) {
mStatusBar = statusBar;
mUserSwitcherController = userSwitcherController;
mAdapter = new Adapter(mUserSwitcherController);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
index 42d9433..0773108 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
@@ -16,11 +16,7 @@
package com.android.systemui.statusbar.phone;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Build;
import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemClock;
@@ -98,7 +94,7 @@
private DozeScrimController mDozeScrimController;
private KeyguardViewMediator mKeyguardViewMediator;
private ScrimController mScrimController;
- private PhoneStatusBar mPhoneStatusBar;
+ private StatusBar mStatusBar;
private final UnlockMethodCache mUnlockMethodCache;
private final Context mContext;
private boolean mGoingToSleep;
@@ -109,7 +105,7 @@
DozeScrimController dozeScrimController,
KeyguardViewMediator keyguardViewMediator,
ScrimController scrimController,
- PhoneStatusBar phoneStatusBar,
+ StatusBar statusBar,
UnlockMethodCache unlockMethodCache) {
mContext = context;
mPowerManager = context.getSystemService(PowerManager.class);
@@ -119,7 +115,7 @@
mDozeScrimController = dozeScrimController;
mKeyguardViewMediator = keyguardViewMediator;
mScrimController = scrimController;
- mPhoneStatusBar = phoneStatusBar;
+ mStatusBar = statusBar;
mUnlockMethodCache = unlockMethodCache;
}
@@ -218,7 +214,7 @@
break;
case MODE_WAKE_AND_UNLOCK_PULSING:
Trace.beginSection("MODE_WAKE_AND_UNLOCK_PULSING");
- mPhoneStatusBar.updateMediaMetaData(false /* metaDataChanged */,
+ mStatusBar.updateMediaMetaData(false /* metaDataChanged */,
true /* allowEnterAnimation */);
// Fall through.
Trace.endSection();
@@ -228,8 +224,8 @@
mDozeScrimController.abortPulsing();
mKeyguardViewMediator.onWakeAndUnlocking();
mScrimController.setWakeAndUnlocking();
- if (mPhoneStatusBar.getNavigationBarView() != null) {
- mPhoneStatusBar.getNavigationBarView().setWakeAndUnlocking(true);
+ if (mStatusBar.getNavigationBarView() != null) {
+ mStatusBar.getNavigationBarView().setWakeAndUnlocking(true);
}
Trace.endSection();
break;
@@ -240,7 +236,7 @@
if (mMode != MODE_WAKE_AND_UNLOCK_PULSING) {
mStatusBarWindowManager.setForceDozeBrightness(false);
}
- mPhoneStatusBar.notifyFpAuthModeChanged();
+ mStatusBar.notifyFpAuthModeChanged();
Trace.endSection();
}
@@ -309,7 +305,7 @@
mMode = MODE_NONE;
releaseFingerprintWakeLock();
mStatusBarWindowManager.setForceDozeBrightness(false);
- mPhoneStatusBar.notifyFpAuthModeChanged();
+ mStatusBar.notifyFpAuthModeChanged();
}
public void startKeyguardFadingAway() {
@@ -320,14 +316,14 @@
public void run() {
mStatusBarWindowManager.setForceDozeBrightness(false);
}
- }, PhoneStatusBar.FADE_KEYGUARD_DURATION_PULSING);
+ }, StatusBar.FADE_KEYGUARD_DURATION_PULSING);
}
public void finishKeyguardFadingAway() {
mMode = MODE_NONE;
- if (mPhoneStatusBar.getNavigationBarView() != null) {
- mPhoneStatusBar.getNavigationBarView().setWakeAndUnlocking(false);
+ if (mStatusBar.getNavigationBarView() != null) {
+ mStatusBar.getNavigationBarView().setWakeAndUnlocking(false);
}
- mPhoneStatusBar.notifyFpAuthModeChanged();
+ mStatusBar.notifyFpAuthModeChanged();
}
}
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 7dcf811..6c71d7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -18,9 +18,8 @@
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_BUTTON;
+
import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_UNLOCK;
-import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_BUTTON;
import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_UNLOCK;
import static com.android.systemui.tuner.LockscreenFragment.getIntentButton;
@@ -36,9 +35,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.content.res.ColorStateList;
import android.content.res.Configuration;
-import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -95,7 +92,7 @@
AccessibilityController.AccessibilityStateChangedCallback, View.OnLongClickListener,
Tunable {
- final static String TAG = "PhoneStatusBar/KeyguardBottomAreaView";
+ final static String TAG = "StatusBar/KeyguardBottomAreaView";
public static final String CAMERA_LAUNCH_SOURCE_AFFORDANCE = "lockscreen_affordance";
public static final String CAMERA_LAUNCH_SOURCE_WIGGLE = "wiggle_gesture";
@@ -136,7 +133,7 @@
private PreviewInflater mPreviewInflater;
private KeyguardIndicationController mIndicationController;
private AccessibilityController mAccessibilityController;
- private PhoneStatusBar mPhoneStatusBar;
+ private StatusBar mStatusBar;
private KeyguardAffordanceHelper mAffordanceHelper;
private boolean mUserSetupComplete;
@@ -208,7 +205,7 @@
public boolean performAccessibilityAction(View host, int action, Bundle args) {
if (action == ACTION_CLICK) {
if (host == mLockIcon) {
- mPhoneStatusBar.animateCollapsePanels(
+ mStatusBar.animateCollapsePanels(
CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
return true;
} else if (host == mRightAffordanceView) {
@@ -326,8 +323,8 @@
mRightAffordanceView.setContentDescription(state.contentDescription);
}
- public void setPhoneStatusBar(PhoneStatusBar phoneStatusBar) {
- mPhoneStatusBar = phoneStatusBar;
+ public void setStatusBar(StatusBar statusBar) {
+ mStatusBar = statusBar;
updateCameraVisibility(); // in case onFinishInflate() was called too early
}
@@ -391,13 +388,13 @@
private boolean isCameraDisabledByDpm() {
final DevicePolicyManager dpm =
(DevicePolicyManager) getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
- if (dpm != null && mPhoneStatusBar != null) {
+ if (dpm != null && mStatusBar != null) {
try {
final int userId = ActivityManager.getService().getCurrentUser().id;
final int disabledFlags = dpm.getKeyguardDisabledFeatures(null, userId);
final boolean disabledBecauseKeyguardSecure =
(disabledFlags & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0
- && mPhoneStatusBar.isKeyguardSecure();
+ && mStatusBar.isKeyguardSecure();
return dpm.getCameraDisabled(null) || disabledBecauseKeyguardSecure;
} catch (RemoteException e) {
Log.e(TAG, "Can't get userId", e);
@@ -433,7 +430,7 @@
if (!mAccessibilityController.isAccessibilityEnabled()) {
handleTrustCircleClick();
} else {
- mPhoneStatusBar.animateCollapsePanels(
+ mStatusBar.animateCollapsePanels(
CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
}
}
@@ -570,12 +567,12 @@
mAssistManager.launchVoiceAssistFromKeyguard();
}
};
- if (mPhoneStatusBar.isKeyguardCurrentlySecure()) {
+ if (mStatusBar.isKeyguardCurrentlySecure()) {
AsyncTask.execute(runnable);
} else {
boolean dismissShade = !TextUtils.isEmpty(mRightButtonStr)
&& TunerService.get(getContext()).getValue(LOCKSCREEN_RIGHT_UNLOCK, 1) != 0;
- mPhoneStatusBar.executeRunnableDismissingKeyguard(runnable, null /* cancelAction */,
+ mStatusBar.executeRunnableDismissingKeyguard(runnable, null /* cancelAction */,
dismissShade, false /* afterKeyguardGone */, true /* deferred */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index af6e259..99b3aa8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -51,7 +51,7 @@
private static final String TAG = "LockscreenWallpaper";
- private final PhoneStatusBar mBar;
+ private final StatusBar mBar;
private final WallpaperManager mWallpaperManager;
private final Handler mH;
private final KeyguardUpdateMonitor mUpdateMonitor;
@@ -64,7 +64,7 @@
private UserHandle mSelectedUser;
private AsyncTask<Void, Void, LoaderResult> mLoader;
- public LockscreenWallpaper(Context ctx, PhoneStatusBar bar, Handler h) {
+ public LockscreenWallpaper(Context ctx, StatusBar bar, Handler h) {
mBar = bar;
mH = h;
mWallpaperManager = (WallpaperManager) ctx.getSystemService(Context.WALLPAPER_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index d40326a..62b536e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -20,8 +20,8 @@
import static android.app.StatusBarManager.windowStateToString;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.PhoneStatusBar.DEBUG_WINDOW_STATE;
-import static com.android.systemui.statusbar.phone.PhoneStatusBar.dumpBarTransitions;
+import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE;
+import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -67,7 +67,6 @@
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysUiServiceProvider;
-import com.android.systemui.SystemUIApplication;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
@@ -105,7 +104,7 @@
protected AccessibilityManager mAccessibilityManager;
private int mDisabledFlags1;
- private PhoneStatusBar mPhoneStatusBar;
+ private StatusBar mStatusBar;
private Recents mRecents;
private Divider mDivider;
private WindowManager mWindowManager;
@@ -128,7 +127,7 @@
super.onCreate(savedInstanceState);
mCommandQueue = SysUiServiceProvider.getComponent(getContext(), CommandQueue.class);
mCommandQueue.addCallbacks(this);
- mPhoneStatusBar = SysUiServiceProvider.getComponent(getContext(), PhoneStatusBar.class);
+ mStatusBar = SysUiServiceProvider.getComponent(getContext(), StatusBar.class);
mRecents = SysUiServiceProvider.getComponent(getContext(), Recents.class);
mDivider = SysUiServiceProvider.getComponent(getContext(), Divider.class);
mWindowManager = getContext().getSystemService(WindowManager.class);
@@ -262,7 +261,7 @@
if (mNavigationBarView != null) {
mNavigationBarView.setNavigationIconHints(hints);
}
- mPhoneStatusBar.checkBarModes();
+ mStatusBar.checkBarModes();
}
@Override
@@ -300,21 +299,21 @@
/**
* Calls appTransitionStarting for the nav bar regardless of whether keyguard is going away.
- * public so PhoneStatusBar can force this when needed.
+ * public so StatusBar can force this when needed.
*/
public void doAppTransitionStarting(long startTime, long duration) {
mNavigationBarView.getLightTransitionsController().appTransitionStarting(startTime,
duration);
}
- // Injected from PhoneStatusBar at creation.
+ // Injected from StatusBar at creation.
public void setCurrentSysuiVisibility(int systemUiVisibility) {
mSystemUiVisibility = systemUiVisibility;
- mNavigationBarMode = mPhoneStatusBar.computeBarMode(0, mSystemUiVisibility,
+ mNavigationBarMode = mStatusBar.computeBarMode(0, mSystemUiVisibility,
View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
View.NAVIGATION_BAR_TRANSPARENT);
checkNavBarModes();
- mPhoneStatusBar.touchAutoHide();
+ mStatusBar.touchAutoHide();
mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */,
true /* nbModeChanged */, mNavigationBarMode);
}
@@ -331,7 +330,7 @@
// update navigation bar mode
final int nbMode = getView() == null
- ? -1 : mPhoneStatusBar.computeBarMode(oldVal, newVal,
+ ? -1 : mStatusBar.computeBarMode(oldVal, newVal,
View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
View.NAVIGATION_BAR_TRANSPARENT);
nbModeChanged = nbMode != -1;
@@ -340,7 +339,7 @@
mNavigationBarMode = nbMode;
checkNavBarModes();
}
- mPhoneStatusBar.touchAutoHide();
+ mStatusBar.touchAutoHide();
}
}
@@ -370,7 +369,7 @@
}
private boolean shouldDisableNavbarGestures() {
- return !mPhoneStatusBar.isDeviceProvisioned()
+ return !mStatusBar.isDeviceProvisioned()
|| (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0;
}
@@ -418,7 +417,7 @@
TelecomManager telecomManager =
getContext().getSystemService(TelecomManager.class);
if (telecomManager != null && telecomManager.isRinging()) {
- if (mPhoneStatusBar.isKeyguardShowing()) {
+ if (mStatusBar.isKeyguardShowing()) {
Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " +
"No heads up");
mHomeBlockedThisTouch = true;
@@ -428,18 +427,18 @@
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- mPhoneStatusBar.awakenDreams();
+ mStatusBar.awakenDreams();
break;
}
return false;
}
private void onVerticalChanged(boolean isVertical) {
- mPhoneStatusBar.setQsScrimEnabled(!isVertical);
+ mStatusBar.setQsScrimEnabled(!isVertical);
}
private boolean onNavigationTouch(View v, MotionEvent event) {
- mPhoneStatusBar.checkUserAutohide(v, event);
+ mStatusBar.checkUserAutohide(v, event);
return false;
}
@@ -450,7 +449,7 @@
}
MetricsLogger.action(getContext(), MetricsEvent.ACTION_ASSIST_LONG_PRESS);
mAssistManager.startAssist(new Bundle() /* args */);
- mPhoneStatusBar.awakenDreams();
+ mStatusBar.awakenDreams();
if (mNavigationBarView != null) {
mNavigationBarView.abortCurrentGesture();
}
@@ -478,7 +477,7 @@
LatencyTracker.getInstance(getContext()).onActionStart(
LatencyTracker.ACTION_TOGGLE_RECENTS);
}
- mPhoneStatusBar.awakenDreams();
+ mStatusBar.awakenDreams();
mCommandQueue.toggleRecentApps();
}
@@ -550,11 +549,11 @@
return false;
}
- return mPhoneStatusBar.toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS,
+ return mStatusBar.toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS,
MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS);
}
- // ----- Methods that PhoneStatusBar talks to (should be minimized) -----
+ // ----- Methods that StatusBar talks to (should be minimized) -----
public void setLightBarController(LightBarController lightBarController) {
mLightBarController = lightBarController;
@@ -584,7 +583,7 @@
}
public void checkNavBarModes() {
- mPhoneStatusBar.checkBarMode(mNavigationBarMode,
+ mStatusBar.checkBarMode(mNavigationBarMode,
mNavigationBarWindowState, mNavigationBarView.getBarTransitions());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 319f124..5e988fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -28,7 +28,6 @@
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
@@ -53,7 +52,6 @@
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.statusbar.phone.NavGesture;
import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
-import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.policy.DeadZone;
import com.android.systemui.statusbar.policy.KeyButtonDrawable;
@@ -553,8 +551,8 @@
}
@Override
- public void onDockedStackMinimizedChanged(boolean minimized, long animDuration)
- throws RemoteException {
+ public void onDockedStackMinimizedChanged(boolean minimized, long animDuration,
+ boolean isHomeStackResizable) throws RemoteException {
}
@Override
@@ -784,7 +782,7 @@
final Point size = new Point();
mDisplay.getRealSize(size);
- pw.println(String.format(" this: " + PhoneStatusBar.viewInfo(this)
+ pw.println(String.format(" this: " + StatusBar.viewInfo(this)
+ " " + visibilityToString(getVisibility())));
getWindowVisibleDisplayFrame(r);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 32b9969..0386398 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -33,7 +33,7 @@
private int mIconHPadding;
private int mIconTint = Color.WHITE;
- private PhoneStatusBar mPhoneStatusBar;
+ private StatusBar mStatusBar;
protected View mNotificationIconArea;
private NotificationIconContainer mNotificationIcons;
private NotificationIconContainer mShelfIcons;
@@ -41,8 +41,8 @@
private NotificationStackScrollLayout mNotificationScrollLayout;
private Context mContext;
- public NotificationIconAreaController(Context context, PhoneStatusBar phoneStatusBar) {
- mPhoneStatusBar = phoneStatusBar;
+ public NotificationIconAreaController(Context context, StatusBar statusBar) {
+ mStatusBar = statusBar;
mNotificationColorUtil = NotificationColorUtil.getInstance(context);
mContext = context;
@@ -64,11 +64,11 @@
mNotificationIcons = (NotificationIconContainer) mNotificationIconArea.findViewById(
R.id.notificationIcons);
- NotificationShelf shelf = mPhoneStatusBar.getNotificationShelf();
+ NotificationShelf shelf = mStatusBar.getNotificationShelf();
mShelfIcons = shelf.getShelfIcons();
shelf.setCollapsedIcons(mNotificationIcons);
- mNotificationScrollLayout = mPhoneStatusBar.getNotificationScrollLayout();
+ mNotificationScrollLayout = mStatusBar.getNotificationScrollLayout();
}
public void onDensityOrFontScaleChanged(Context context) {
@@ -124,7 +124,7 @@
}
protected int getHeight() {
- return mPhoneStatusBar.getStatusBarHeight();
+ return mStatusBar.getStatusBarHeight();
}
protected boolean shouldShowNotificationIcon(NotificationData.Entry entry,
@@ -133,7 +133,7 @@
&& !NotificationData.showNotificationEvenIfUnprovisioned(entry.notification)) {
return false;
}
- if (!PhoneStatusBar.isTopLevelChild(entry)) {
+ if (!StatusBar.isTopLevelChild(entry)) {
return false;
}
if (entry.row.getVisibility() == View.GONE) {
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 3bdd5e5..c527296 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -92,7 +92,7 @@
public static final long DOZE_ANIMATION_DURATION = 700;
- private KeyguardAffordanceHelper mAfforanceHelper;
+ private KeyguardAffordanceHelper mAffordanceHelper;
private KeyguardUserSwitcher mKeyguardUserSwitcher;
private KeyguardStatusBarView mKeyguardStatusBar;
private QS mQs;
@@ -219,7 +219,7 @@
mFalsingManager = FalsingManager.getInstance(context);
}
- public void setStatusBar(PhoneStatusBar bar) {
+ public void setStatusBar(StatusBar bar) {
mStatusBar = bar;
}
@@ -239,8 +239,8 @@
mNotificationStackScroller.setOnEmptySpaceClickListener(this);
mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area);
mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim);
- mAfforanceHelper = new KeyguardAffordanceHelper(this, getContext());
- mKeyguardBottomArea.setAffordanceHelper(mAfforanceHelper);
+ mAffordanceHelper = new KeyguardAffordanceHelper(this, getContext());
+ mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper);
mLastOrientation = getResources().getConfiguration().orientation;
mQsFrame = (FrameLayout) findViewById(R.id.qs_frame);
@@ -516,7 +516,7 @@
mBlockTouches = false;
mUnlockIconActive = false;
if (!mLaunchingAffordance) {
- mAfforanceHelper.reset(false);
+ mAffordanceHelper.reset(false);
mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
}
closeQs();
@@ -763,7 +763,7 @@
if ((!mIsExpanding || mHintAnimationRunning)
&& !mQsExpanded
&& mStatusBar.getBarState() != StatusBarState.SHADE) {
- mAfforanceHelper.onTouchEvent(event);
+ mAffordanceHelper.onTouchEvent(event);
}
if (mOnlyAffordanceInThisMotion) {
return true;
@@ -881,7 +881,7 @@
@Override
protected boolean shouldGestureIgnoreXTouchSlop(float x, float y) {
- return !mAfforanceHelper.isOnAffordanceIcon(x, y);
+ return !mAffordanceHelper.isOnAffordanceIcon(x, y);
}
private void onQsTouch(MotionEvent event) {
@@ -1722,7 +1722,7 @@
}
if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
|| mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
- mAfforanceHelper.animateHideLeftRightIcon();
+ mAffordanceHelper.animateHideLeftRightIcon();
}
mNotificationStackScroller.onPanelTrackingStarted();
}
@@ -1739,7 +1739,7 @@
if (expand && (mStatusBar.getBarState() == StatusBarState.KEYGUARD
|| mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED)) {
if (!mHintAnimationRunning) {
- mAfforanceHelper.reset(true);
+ mAffordanceHelper.reset(true);
}
}
if (!expand && (mStatusBar.getBarState() == StatusBarState.KEYGUARD
@@ -1785,7 +1785,7 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- mAfforanceHelper.onConfigurationChanged();
+ mAffordanceHelper.onConfigurationChanged();
if (newConfig.orientation != mLastOrientation) {
resetVerticalPanelPosition();
}
@@ -1806,7 +1806,7 @@
@Override
public void onRtlPropertiesChanged(int layoutDirection) {
if (layoutDirection != mOldLayoutDirection) {
- mAfforanceHelper.onRtlPropertiesChanged();
+ mAffordanceHelper.onRtlPropertiesChanged();
mOldLayoutDirection = layoutDirection;
}
}
@@ -1938,7 +1938,7 @@
return;
}
mHintAnimationRunning = true;
- mAfforanceHelper.startHintAnimation(rightIcon, new Runnable() {
+ mAffordanceHelper.startHintAnimation(rightIcon, new Runnable() {
@Override
public void run() {
mHintAnimationRunning = false;
@@ -2351,7 +2351,7 @@
} else {
animate = false;
}
- mAfforanceHelper.launchAffordance(animate, getLayoutDirection() == LAYOUT_DIRECTION_RTL);
+ mAffordanceHelper.launchAffordance(animate, getLayoutDirection() == LAYOUT_DIRECTION_RTL);
}
public void onAffordanceLaunchEnded() {
@@ -2397,7 +2397,7 @@
? null : resolveInfo.activityInfo.packageName;
return packageToLaunch != null &&
(keyguardIsShowing || !isForegroundApp(packageToLaunch)) &&
- !mAfforanceHelper.isSwipingInProgress();
+ !mAffordanceHelper.isSwipingInProgress();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 18e394e..5f67468 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -60,7 +60,7 @@
Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
}
- protected PhoneStatusBar mStatusBar;
+ protected StatusBar mStatusBar;
protected HeadsUpManager mHeadsUpManager;
private float mPeekHeight;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index c80b3ad..7e08812 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -30,10 +30,10 @@
public class PhoneStatusBarView extends PanelBar {
private static final String TAG = "PhoneStatusBarView";
- private static final boolean DEBUG = PhoneStatusBar.DEBUG;
+ private static final boolean DEBUG = StatusBar.DEBUG;
private static final boolean DEBUG_GESTURES = false;
- PhoneStatusBar mBar;
+ StatusBar mBar;
boolean mIsFullyOpenedPanel = false;
private final PhoneStatusBarTransitions mBarTransitions;
@@ -59,7 +59,7 @@
return mBarTransitions;
}
- public void setBar(PhoneStatusBar bar) {
+ public void setBar(StatusBar bar) {
mBar = bar;
}
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 2f76cb1..dd567e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -71,7 +71,7 @@
public static final String TILES_SETTING = Secure.QS_TILES;
private final Context mContext;
- private final PhoneStatusBar mStatusBar;
+ private final StatusBar mStatusBar;
private final LinkedHashMap<String, QSTile<?>> mTiles = new LinkedHashMap<>();
protected final ArrayList<String> mTileSpecs = new ArrayList<>();
private final TileServices mServices;
@@ -81,7 +81,7 @@
private final StatusBarIconController mIconController;
private int mCurrentUser;
- public QSTileHost(Context context, PhoneStatusBar statusBar,
+ public QSTileHost(Context context, StatusBar statusBar,
StatusBarIconController iconController) {
mIconController = iconController;
mContext = context;
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 5536209..c0e9653 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -29,6 +29,7 @@
import android.os.UserManager;
import android.support.annotation.VisibleForTesting;
import android.util.AttributeSet;
+import android.util.SparseBooleanArray;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
@@ -55,6 +56,8 @@
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener;
+import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
import com.android.systemui.statusbar.policy.UserInfoController;
@@ -63,7 +66,7 @@
public class QuickStatusBarHeader extends BaseStatusBarHeader implements
NextAlarmChangeCallback, OnClickListener, OnUserInfoChangedListener, EmergencyListener,
- BatteryStateChangeCallback {
+ BatteryStateChangeCallback, SignalCallback {
private static final String TAG = "QuickStatusBarHeader";
@@ -106,6 +109,8 @@
private boolean mShowFullAlarm;
private float mDateTimeTranslation;
private TextView mBatteryLevel;
+ private SparseBooleanArray mRoamingsBySubId = new SparseBooleanArray();
+ private boolean mIsRoaming;
public QuickStatusBarHeader(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -300,7 +305,7 @@
protected void updateVisibilities() {
updateAlarmVisibilities();
updateDateTimePosition();
- mEmergencyOnly.setVisibility(mExpanded && mShowEmergencyCallsOnly
+ mEmergencyOnly.setVisibility(mExpanded && (mShowEmergencyCallsOnly || mIsRoaming)
? View.VISIBLE : View.INVISIBLE);
mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
@@ -311,7 +316,7 @@
}
private void updateDateTimePosition() {
- mDateTimeAlarmGroup.setTranslationY(mShowEmergencyCallsOnly
+ mDateTimeAlarmGroup.setTranslationY(mShowEmergencyCallsOnly || mIsRoaming
? mExpansionAmount * mDateTimeTranslation : 0);
}
@@ -321,11 +326,13 @@
mUserInfoController.addCallback(this);
if (Dependency.get(NetworkController.class).hasVoiceCallingFeature()) {
Dependency.get(NetworkController.class).addEmergencyListener(this);
+ Dependency.get(NetworkController.class).addCallback(this);
}
} else {
mNextAlarmController.removeCallback(this);
mUserInfoController.removeCallback(this);
Dependency.get(NetworkController.class).removeEmergencyListener(this);
+ Dependency.get(NetworkController.class).removeCallback(this);
}
}
@@ -406,6 +413,28 @@
// Don't care.
}
+ public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
+ int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
+ String description, boolean isWide, int subId, boolean roaming) {
+ mRoamingsBySubId.put(subId, roaming);
+ boolean isRoaming = calculateRoaming();
+ if (mIsRoaming != isRoaming) {
+ mIsRoaming = isRoaming;
+ mEmergencyOnly.setText(mIsRoaming ? R.string.accessibility_data_connection_roaming
+ : com.android.internal.R.string.emergency_calls_only);
+ if (mExpanded) {
+ updateEverything();
+ }
+ }
+ }
+
+ private boolean calculateRoaming() {
+ for (int i = 0; i < mRoamingsBySubId.size(); i++) {
+ if (mRoamingsBySubId.valueAt(i)) return true;
+ }
+ return false;
+ }
+
@Override
public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
mMultiUserAvatar.setImageDrawable(picture);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
similarity index 65%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 7d82477..45fd7ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -72,9 +72,7 @@
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -127,8 +125,6 @@
import com.android.systemui.Interpolators;
import com.android.systemui.Prefs;
import com.android.systemui.R;
-import com.android.systemui.SysUiServiceProvider;
-import com.android.systemui.SystemUIApplication;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.classifier.FalsingLog;
@@ -142,6 +138,7 @@
import com.android.systemui.ActivityStarter;
import com.android.systemui.plugins.qs.QS.BaseStatusBarHeader;
import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.SnoozeListener;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.SnoozeOption;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.recents.ScreenPinningRequest;
@@ -152,7 +149,6 @@
import com.android.systemui.stackdivider.WindowManagerProxy;
import com.android.systemui.statusbar.ActivatableNotificationView;
import com.android.systemui.statusbar.BackDropView;
-import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DismissView;
import com.android.systemui.statusbar.DragDownHelper;
@@ -161,9 +157,13 @@
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.KeyboardShortcuts;
import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.NotificationContentView;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationData.Entry;
+import com.android.systemui.statusbar.NotificationGuts;
+import com.android.systemui.statusbar.NotificationInfo;
import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.NotificationSnooze;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.SignalClusterView;
@@ -172,7 +172,6 @@
import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
-import com.android.systemui.statusbar.policy.BatteryControllerImpl;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -184,11 +183,9 @@
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.policy.PreviewInflater;
-import com.android.systemui.statusbar.policy.SecurityController;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
@@ -198,17 +195,102 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
+import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
+import android.app.ActivityManager.StackId;
+import android.app.INotificationManager;
+import android.app.KeyguardManager;
+import android.app.NotificationChannel;
+import android.app.RemoteInput;
+import android.app.TaskStackBuilder;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.UserInfo;
+import android.os.Build;
+import android.os.Handler;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
+import android.service.notification.NotificationListenerService;
+import android.service.vr.IVrManager;
+import android.service.vr.IVrStateCallbacks;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.view.IWindowManager;
+import android.view.ViewAnimationUtils;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.RemoteViews;
+import android.widget.Toast;
+
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.systemui.DejankUtils;
+import com.android.systemui.RecentsComponent;
+import com.android.systemui.SwipeHelper;
+import com.android.systemui.SystemUI;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.MenuItem;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.SnoozeGutsContent;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.statusbar.policy.RemoteInputView;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.util.NotificationChannels;
+
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+import java.util.Stack;
+
+public class StatusBar extends SystemUI implements DemoMode,
DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
- OnHeadsUpChangedListener, VisualStabilityManager.Callback, SnoozeListener {
- static final String TAG = "PhoneStatusBar";
- public static final boolean DEBUG = BaseStatusBar.DEBUG;
+ OnHeadsUpChangedListener, VisualStabilityManager.Callback, SnoozeListener,
+ CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
+ ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
+ ExpandableNotificationRow.OnExpandClickListener {
+ public static final boolean MULTIUSER_DEBUG = false;
+
+ public static final boolean ENABLE_REMOTE_INPUT =
+ SystemProperties.getBoolean("debug.enable_remote_input", true);
+ public static final boolean ENABLE_CHILD_NOTIFICATIONS
+ = SystemProperties.getBoolean("debug.child_notifs", true);
+ public static final boolean FORCE_REMOTE_INPUT_HISTORY =
+ SystemProperties.getBoolean("debug.force_remoteinput_history", false);
+ private static boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false;
+
+ protected static final int MSG_SHOW_RECENT_APPS = 1019;
+ protected static final int MSG_HIDE_RECENT_APPS = 1020;
+ protected static final int MSG_TOGGLE_RECENTS_APPS = 1021;
+ protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
+ protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
+ protected static final int MSG_SHOW_NEXT_AFFILIATED_TASK = 1024;
+ protected static final int MSG_SHOW_PREV_AFFILIATED_TASK = 1025;
+ protected static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU = 1026;
+ protected static final int MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU = 1027;
+
+ protected static final boolean ENABLE_HEADS_UP = true;
+ protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
+
+ private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
+
+ // Should match the values in PhoneWindowManager
+ public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
+ public static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
+
+ private static final String BANNER_ACTION_CANCEL =
+ "com.android.systemui.statusbar.banner_action_cancel";
+ private static final String BANNER_ACTION_SETUP =
+ "com.android.systemui.statusbar.banner_action_setup";
+ private static final String NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION
+ = "com.android.systemui.statusbar.work_challenge_unlocked_notification_action";
+ static final String TAG = "StatusBar";
+ public static final boolean DEBUG = false;
public static final boolean SPEW = false;
public static final boolean DUMPTRUCK = true; // extra dumpsys info
public static final boolean DEBUG_GESTURES = false;
@@ -300,6 +382,14 @@
FREEFORM_WINDOW_MANAGEMENT = freeformWindowManagement;
}
+ /**
+ * The {@link StatusBarState} of the status bar.
+ */
+ protected int mState;
+ protected boolean mBouncerShowing;
+ protected boolean mShowLockscreenNotifications;
+ protected boolean mAllowLockscreenRemoteInput;
+
PhoneStatusBarPolicy mIconPolicy;
VolumeComponent mVolumeComponent;
@@ -310,7 +400,6 @@
int mNaturalBarHeight = -1;
- Display mDisplay;
Point mCurrentDisplaySize = new Point();
protected StatusBarWindowView mStatusBarWindow;
@@ -416,6 +505,7 @@
}
};
+ protected H mHandler = createHandler();
final private ContentObserver mHeadsUpObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
@@ -614,7 +704,6 @@
private NetworkController mNetworkController;
private KeyguardMonitorImpl mKeyguardMonitor;
private BatteryController mBatteryController;
- private DeviceProvisionedController mDeviceProvisionedController;
private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) {
final int N = array.size();
@@ -656,16 +745,156 @@
mAssistManager = Dependency.get(AssistManager.class);
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
- mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
- .getDefaultDisplay();
+ mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
+ mDisplay = mWindowManager.getDefaultDisplay();
updateDisplaySize();
mScrimSrcModeEnabled = mContext.getResources().getBoolean(
R.bool.config_status_bar_scrim_behind_use_src);
DateTimeView.setReceiverHandler(Dependency.get(Dependency.TIME_TICK_HANDLER));
- putComponent(PhoneStatusBar.class, this);
+ putComponent(StatusBar.class, this);
- super.start(); // calls createAndAddWindows()
+ // start old BaseStatusBar.start().
+ mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
+ mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+
+ mNotificationData = new NotificationData(this);
+
+ mAccessibilityManager = (AccessibilityManager)
+ mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+
+ mDreamManager = IDreamManager.Stub.asInterface(
+ ServiceManager.checkService(DreamService.DREAM_SERVICE));
+ mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+
+ mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
+ mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
+ mSettingsObserver);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
+ mLockscreenSettingsObserver,
+ UserHandle.USER_ALL);
+ if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT),
+ false,
+ mSettingsObserver,
+ UserHandle.USER_ALL);
+ }
+
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
+ true,
+ mLockscreenSettingsObserver,
+ UserHandle.USER_ALL);
+
+ mBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+
+ mRecents = getComponent(Recents.class);
+
+ final Configuration currentConfig = mContext.getResources().getConfiguration();
+ mLocale = currentConfig.locale;
+ mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
+ mFontScale = currentConfig.fontScale;
+ mDensity = currentConfig.densityDpi;
+
+ mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+ mLockPatternUtils = new LockPatternUtils(mContext);
+
+ // Connect in to the status bar manager service
+ mCommandQueue = getComponent(CommandQueue.class);
+ mCommandQueue.addCallbacks(this);
+
+ int[] switches = new int[9];
+ ArrayList<IBinder> binders = new ArrayList<IBinder>();
+ ArrayList<String> iconSlots = new ArrayList<>();
+ ArrayList<StatusBarIcon> icons = new ArrayList<>();
+ Rect fullscreenStackBounds = new Rect();
+ Rect dockedStackBounds = new Rect();
+ try {
+ mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
+ fullscreenStackBounds, dockedStackBounds);
+ } catch (RemoteException ex) {
+ // If the system process isn't there we're doomed anyway.
+ }
+
+ createAndAddWindows();
+
+ mSettingsObserver.onChange(false); // set up
+ disable(switches[0], switches[6], false /* animate */);
+ setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,
+ fullscreenStackBounds, dockedStackBounds);
+ topAppWindowChanged(switches[2] != 0);
+ // StatusBarManagerService has a back up of IME token and it's restored here.
+ setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
+
+ // Set up the initial icon state
+ int N = iconSlots.size();
+ int viewIndex = 0;
+ for (int i=0; i < N; i++) {
+ setIcon(iconSlots.get(i), icons.get(i));
+ }
+
+ // Set up the initial notification state.
+ try {
+ mNotificationListener.registerAsSystemService(mContext,
+ new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
+ UserHandle.USER_ALL);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to register notification listener", e);
+ }
+
+
+ if (DEBUG) {
+ Log.d(TAG, String.format(
+ "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
+ icons.size(),
+ switches[0],
+ switches[1],
+ switches[2],
+ switches[3]
+ ));
+ }
+
+ mCurrentUserId = ActivityManager.getCurrentUser();
+ setHeadsUpUser(mCurrentUserId);
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
+ filter.addAction(Intent.ACTION_USER_ADDED);
+ filter.addAction(Intent.ACTION_USER_PRESENT);
+ mContext.registerReceiver(mBaseBroadcastReceiver, filter);
+
+ IntentFilter internalFilter = new IntentFilter();
+ internalFilter.addAction(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
+ internalFilter.addAction(BANNER_ACTION_CANCEL);
+ internalFilter.addAction(BANNER_ACTION_SETUP);
+ mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null);
+
+ IntentFilter allUsersFilter = new IntentFilter();
+ allUsersFilter.addAction(
+ DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
+ allUsersFilter.addAction(Intent.ACTION_DEVICE_LOCKED_CHANGED);
+ mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter,
+ null, null);
+ updateCurrentProfilesCache();
+
+ IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService("vrmanager"));
+ try {
+ vrManager.registerListener(mVrStateCallbacks);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register VR mode state listener: " + e);
+ }
+
+ mNonBlockablePkgs = new HashSet<String>();
+ Collections.addAll(mNonBlockablePkgs, mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_nonBlockableNotificationPackages));
+ // end old BaseStatusBar.start().
mMediaSessionManager
= (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
@@ -778,7 +1007,7 @@
mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById(
R.id.notification_stack_scroller);
mStackScroller.setLongPressListener(getNotificationLongClicker());
- mStackScroller.setPhoneStatusBar(this);
+ mStackScroller.setStatusBar(this);
mStackScroller.setGroupManager(mGroupManager);
mStackScroller.setHeadsUpManager(mHeadsUpManager);
mGroupManager.setOnGroupChangeListener(mStackScroller);
@@ -862,7 +1091,7 @@
initEmergencyCryptkeeperText();
- mKeyguardBottomArea.setPhoneStatusBar(this);
+ mKeyguardBottomArea.setStatusBar(this);
mKeyguardBottomArea.setUserSetupComplete(mUserSetup);
if (UserManager.get(mContext).isUserSwitcherEnabled()) {
createUserSwitcher();
@@ -1001,9 +1230,21 @@
mNotificationShelf.setStatusBarState(mState);
}
- @Override
protected void onDensityOrFontScaleChanged() {
- super.onDensityOrFontScaleChanged();
+ // start old BaseStatusBar.onDensityOrFontScaleChanged().
+ ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
+ for (int i = 0; i < activeNotifications.size(); i++) {
+ Entry entry = activeNotifications.get(i);
+ boolean exposedGuts = mNotificationGutsExposed != null
+ && entry.row.getGuts() == mNotificationGutsExposed;
+ entry.row.reInflateViews();
+ if (exposedGuts) {
+ mNotificationGutsExposed = entry.row.getGuts();
+ bindGuts(entry.row, mGutsMenuItem);
+ }
+ inflateViews(entry, mStackScroller);
+ }
+ // end old BaseStatusBar.onDensityOrFontScaleChanged().
mScrimController.onDensityOrFontScaleChanged();
mStatusBarView.onDensityOrFontScaleChanged();
if (mBrightnessMirrorController != null) {
@@ -1016,7 +1257,7 @@
inflateEmptyShadeView();
updateEmptyShadeView();
mStatusBarKeyguardViewManager.onDensityOrFontScaleChanged();
- // TODO: Bring these out of PhoneStatusBar.
+ // TODO: Bring these out of StatusBar.
((UserInfoControllerImpl) Dependency.get(UserInfoController.class))
.onDensityOrFontScaleChanged();
Dependency.get(UserSwitcherController.class).onDensityOrFontScaleChanged();
@@ -1162,16 +1403,20 @@
}
}
- @Override
protected void setZenMode(int mode) {
- super.setZenMode(mode);
+ // start old BaseStatusBar.setZenMode().
+ if (isDeviceProvisioned()) {
+ mZenMode = mode;
+ updateNotifications();
+ }
+ // end old BaseStatusBar.setZenMode().
if (mIconPolicy != null) {
mIconPolicy.setZenMode(mode);
}
}
protected void startKeyguard() {
- Trace.beginSection("PhoneStatusBar#startKeyguard");
+ Trace.beginSection("StatusBar#startKeyguard");
KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
mFingerprintUnlockController = new FingerprintUnlockController(mContext,
mStatusBarWindowManager, mDozeScrimController, keyguardViewMediator,
@@ -1211,7 +1456,6 @@
Trace.endSection();
}
- @Override
protected View getStatusBarView() {
return mStatusBarView;
}
@@ -1233,7 +1477,6 @@
return mNaturalBarHeight;
}
- @Override
protected boolean toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction) {
if (mRecents == null) {
return false;
@@ -1281,7 +1524,6 @@
return new UserHandle(mCurrentUserId);
}
- @Override
public void addNotification(StatusBarNotification notification, RankingMap ranking,
Entry oldEntry) {
if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey());
@@ -1344,13 +1586,11 @@
}
}
- @Override
protected void updateNotificationRanking(RankingMap ranking) {
mNotificationData.updateRanking(ranking);
updateNotifications();
}
- @Override
public void removeNotification(String key, RankingMap ranking) {
boolean deferRemoval = false;
if (mHeadsUpManager.isHeadsUp(key)) {
@@ -1481,13 +1721,28 @@
}
}
- @Override
protected void performRemoveNotification(StatusBarNotification n) {
Entry entry = mNotificationData.get(n.getKey());
if (mRemoteInputController.isRemoteInputActive(entry)) {
mRemoteInputController.removeRemoteInput(entry, null);
}
- super.performRemoveNotification(n);
+ // start old BaseStatusBar.performRemoveNotification.
+ final String pkg = n.getPackageName();
+ final String tag = n.getTag();
+ final int id = n.getId();
+ final int userId = n.getUserId();
+ try {
+ mBarService.onNotificationClear(pkg, tag, id, userId);
+ if (FORCE_REMOTE_INPUT_HISTORY
+ && mKeysKeptForRemoteInput.contains(n.getKey())) {
+ mKeysKeptForRemoteInput.remove(n.getKey());
+ }
+ removeNotification(n.getKey(), null);
+
+ } catch (RemoteException ex) {
+ // system process is dead if we're here.
+ }
+ // end old BaseStatusBar.performRemoveNotification.
}
private void updateNotificationShade() {
@@ -1716,17 +1971,14 @@
}
}
- @Override
public void addQsTile(ComponentName tile) {
mQSPanel.getHost().addTile(tile);
}
- @Override
public void remQsTile(ComponentName tile) {
mQSPanel.getHost().removeTile(tile);
}
- @Override
public void clickTile(ComponentName tile) {
mQSPanel.clickTile(tile);
}
@@ -1789,7 +2041,6 @@
return entry.row.getParent() instanceof NotificationStackScrollLayout;
}
- @Override
protected void updateNotifications() {
mNotificationData.filterAndSort();
@@ -1800,7 +2051,6 @@
updateNotifications();
}
- @Override
protected void setAreThereNotifications() {
if (SPEW) {
@@ -1984,7 +2234,7 @@
* Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
*/
public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
- Trace.beginSection("PhoneStatusBar#updateMediaMetaData");
+ Trace.beginSection("StatusBar#updateMediaMetaData");
if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) {
Trace.endSection();
return;
@@ -2279,9 +2529,8 @@
disable(mDisabledUnmodified1, mDisabledUnmodified2, animate);
}
- @Override
- protected BaseStatusBar.H createHandler() {
- return new PhoneStatusBar.H();
+ protected H createHandler() {
+ return new StatusBar.H();
}
@Override
@@ -2318,7 +2567,6 @@
return getBarState() == StatusBarState.KEYGUARD;
}
- @Override
public boolean isDozing() {
return mDozing;
}
@@ -2412,7 +2660,6 @@
}
- @Override
protected void updateHeadsUp(String key, Entry entry, boolean shouldPeek,
boolean alertAgain) {
final boolean wasHeadsUp = isHeadsUp(key);
@@ -2429,7 +2676,6 @@
}
}
- @Override
protected void setHeadsUpUser(int newUserId) {
if (mHeadsUpManager != null) {
mHeadsUpManager.setUser(newUserId);
@@ -2440,7 +2686,6 @@
return mHeadsUpManager.isHeadsUp(key);
}
- @Override
protected boolean isSnoozedPackage(StatusBarNotification sbn) {
return mHeadsUpManager.isSnoozed(sbn.getPackageName());
}
@@ -2497,11 +2742,39 @@
/**
* All changes to the status bar and notifications funnel through here and are batched.
*/
- private class H extends BaseStatusBar.H {
+ protected class H extends Handler {
@Override
public void handleMessage(Message m) {
- super.handleMessage(m);
switch (m.what) {
+ // start old BaseStatusBar.H handling.
+ case MSG_SHOW_RECENT_APPS:
+ showRecents(m.arg1 > 0, m.arg2 != 0);
+ break;
+ case MSG_HIDE_RECENT_APPS:
+ hideRecents(m.arg1 > 0, m.arg2 > 0);
+ break;
+ case MSG_TOGGLE_RECENTS_APPS:
+ toggleRecents();
+ break;
+ case MSG_PRELOAD_RECENT_APPS:
+ preloadRecents();
+ break;
+ case MSG_CANCEL_PRELOAD_RECENT_APPS:
+ cancelPreloadingRecents();
+ break;
+ case MSG_SHOW_NEXT_AFFILIATED_TASK:
+ showRecentsNextAffiliatedTask();
+ break;
+ case MSG_SHOW_PREV_AFFILIATED_TASK:
+ showRecentsPreviousAffiliatedTask();
+ break;
+ case MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU:
+ toggleKeyboardShortcuts(m.arg1);
+ break;
+ case MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU:
+ dismissKeyboardShortcuts();
+ break;
+ // End old BaseStatusBar.H handling.
case MSG_OPEN_NOTIFICATION_PANEL:
animateExpandNotificationsPanel();
break;
@@ -2518,7 +2791,6 @@
}
}
- @Override
public void maybeEscalateHeadsUp() {
Collection<HeadsUpManager.HeadsUpEntry> entries = mHeadsUpManager.getAllEntries();
for (HeadsUpManager.HeadsUpEntry entry : entries) {
@@ -2618,12 +2890,10 @@
1.0f /* speedUpFactor */);
}
- @Override
public void animateCollapsePanels(int flags, boolean force) {
animateCollapsePanels(flags, force, false /* delayed */, 1.0f /* speedUpFactor */);
}
- @Override
public void animateCollapsePanels(int flags, boolean force, boolean delayed) {
animateCollapsePanels(flags, force, delayed, 1.0f /* speedUpFactor */);
}
@@ -2948,7 +3218,6 @@
}
};
- @Override
public void setInteracting(int barWindow, boolean interacting) {
final boolean changing = ((mInteractingWindows & barWindow) != 0) != interacting;
mInteractingWindows = interacting
@@ -3170,7 +3439,6 @@
pw.println(BarTransitions.modeToString(transitions.getMode()));
}
- @Override
public void createAndAddWindows() {
addStatusBarWindow();
}
@@ -3353,7 +3621,6 @@
}
}
- @Override
protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) {
dismissKeyguardThenExecute(action, null /* cancelRunnable */, afterKeyguardGone);
}
@@ -3373,7 +3640,15 @@
protected void onConfigurationChanged(Configuration newConfig) {
updateResources();
updateDisplaySize(); // populates mDisplayMetrics
- super.onConfigurationChanged(newConfig); // calls refreshLayout
+ // Begin old BaseStatusBar.onConfigurationChanged
+ final float fontScale = newConfig.fontScale;
+ final int density = newConfig.densityDpi;
+ if (density != mDensity || mFontScale != fontScale) {
+ onDensityOrFontScaleChanged();
+ mDensity = density;
+ mFontScale = fontScale;
+ }
+ // End old BaseStatusBar.onConfigurationChanged
if (DEBUG) {
Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
@@ -3383,9 +3658,10 @@
mScreenPinningRequest.onConfigurationChanged();
}
- @Override
public void userSwitched(int newUserId) {
- super.userSwitched(newUserId);
+ // Begin old BaseStatusBar.userSwitched
+ setHeadsUpUser(newUserId);
+ // End old BaseStatusBar.userSwitched
if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
animateCollapsePanels();
updatePublicMode();
@@ -3440,14 +3716,40 @@
// Visibility reporting
- @Override
protected void handleVisibleToUserChanged(boolean visibleToUser) {
if (visibleToUser) {
- super.handleVisibleToUserChanged(visibleToUser);
+ handleVisibleToUserChangedImpl(visibleToUser);
startNotificationLogging();
} else {
stopNotificationLogging();
- super.handleVisibleToUserChanged(visibleToUser);
+ handleVisibleToUserChangedImpl(visibleToUser);
+ }
+ }
+
+ /**
+ * The LEDs are turned off when the notification panel is shown, even just a little bit.
+ * See also StatusBar.setPanelExpanded for another place where we attempt to do this.
+ */
+ // Old BaseStatusBar.handleVisibileToUserChanged
+ private void handleVisibleToUserChangedImpl(boolean visibleToUser) {
+ try {
+ if (visibleToUser) {
+ boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
+ boolean clearNotificationEffects =
+ !isPanelFullyCollapsed() &&
+ (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED);
+ int notificationLoad = mNotificationData.getActiveNotifications().size();
+ if (pinnedHeadsUp && isPanelFullyCollapsed()) {
+ notificationLoad = 1;
+ } else {
+ MetricsLogger.histogram(mContext, "note_load", notificationLoad);
+ }
+ mBarService.onPanelRevealed(clearNotificationEffects, notificationLoad);
+ } else {
+ mBarService.onPanelHidden();
+ }
+ } catch (RemoteException ex) {
+ // Won't fail unless the world has ended.
}
}
@@ -3638,9 +3940,16 @@
}
}
- @Override
public void destroy() {
- super.destroy();
+ // Begin old BaseStatusBar.destroy().
+ mContext.unregisterReceiver(mBaseBroadcastReceiver);
+ try {
+ mNotificationListener.unregisterAsSystemService();
+ } catch (RemoteException e) {
+ // Ignore.
+ }
+ mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener);
+ // End old BaseStatusBar.destroy().
if (mStatusBarWindow != null) {
mWindowManager.removeViewImmediate(mStatusBarWindow);
mStatusBarWindow = null;
@@ -3745,7 +4054,6 @@
return mState;
}
- @Override
public boolean isPanelFullyCollapsed() {
return mNotificationPanel.isFullyCollapsed();
}
@@ -3794,12 +4102,10 @@
updateMediaMetaData(true /* metaDataChanged */, true);
}
- @Override
public boolean isCollapsing() {
return mNotificationPanel.isCollapsing();
}
- @Override
public void addPostCollapseAction(Runnable r) {
mPostCollapseRunnables.add(r);
}
@@ -3913,7 +4219,7 @@
* @return true if we would like to stay in the shade, false if it should go away entirely
*/
public boolean hideKeyguard() {
- Trace.beginSection("PhoneStatusBar#hideKeyguard");
+ Trace.beginSection("StatusBar#hideKeyguard");
boolean staying = mLeaveOpenOnKeyguardHide;
setBarState(StatusBarState.SHADE);
View viewToClick = null;
@@ -4045,7 +4351,7 @@
}
protected void updateKeyguardState(boolean goingToFullShade, boolean fromShadeLocked) {
- Trace.beginSection("PhoneStatusBar#updateKeyguardState");
+ Trace.beginSection("StatusBar#updateKeyguardState");
if (mState == StatusBarState.KEYGUARD) {
mKeyguardIndicationController.setVisible(true);
mNotificationPanel.resetViews();
@@ -4082,7 +4388,7 @@
}
private void updateDozingState() {
- Trace.beginSection("PhoneStatusBar#updateDozingState");
+ Trace.beginSection("StatusBar#updateDozingState");
boolean animate = !mDozing && mDozeScrimController.isPulsing();
mNotificationPanel.setDozing(mDozing, animate);
mStackScroller.setDark(mDozing, animate, mWakeUpTouchLocation);
@@ -4288,7 +4594,6 @@
}
}
- @Override
protected int getMaxKeyguardNotifications(boolean recompute) {
if (recompute) {
mMaxKeyguardNotifications = Math.max(1,
@@ -4388,29 +4693,53 @@
}
}
- @Override
public void onLockedNotificationImportanceChange(OnDismissAction dismissAction) {
mLeaveOpenOnKeyguardHide = true;
dismissKeyguardThenExecute(dismissAction, true /* afterKeyguardGone */);
}
- @Override
protected void onLockedRemoteInput(ExpandableNotificationRow row, View clicked) {
mLeaveOpenOnKeyguardHide = true;
showBouncer();
mPendingRemoteInputView = clicked;
}
- @Override
protected boolean startWorkChallengeIfNecessary(int userId, IntentSender intendSender,
String notificationKey) {
// Clear pending remote view, as we do not want to trigger pending remote input view when
// it's called by other code
mPendingWorkRemoteInputView = null;
- return super.startWorkChallengeIfNecessary(userId, intendSender, notificationKey);
+ // Begin old BaseStatusBar.startWorkChallengeIfNecessary.
+ final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null,
+ null, userId);
+ if (newIntent == null) {
+ return false;
+ }
+ final Intent callBackIntent = new Intent(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
+ callBackIntent.putExtra(Intent.EXTRA_INTENT, intendSender);
+ callBackIntent.putExtra(Intent.EXTRA_INDEX, notificationKey);
+ callBackIntent.setPackage(mContext.getPackageName());
+
+ PendingIntent callBackPendingIntent = PendingIntent.getBroadcast(
+ mContext,
+ 0,
+ callBackIntent,
+ PendingIntent.FLAG_CANCEL_CURRENT |
+ PendingIntent.FLAG_ONE_SHOT |
+ PendingIntent.FLAG_IMMUTABLE);
+ newIntent.putExtra(
+ Intent.EXTRA_INTENT,
+ callBackPendingIntent.getIntentSender());
+ try {
+ ActivityManager.getService().startConfirmDeviceCredentialIntent(newIntent,
+ null /*options*/);
+ } catch (RemoteException ex) {
+ // ignore
+ }
+ return true;
+ // End old BaseStatusBar.startWorkChallengeIfNecessary.
}
- @Override
protected void onLockedWorkRemoteInput(int userId, ExpandableNotificationRow row,
View clicked) {
// Collapse notification and show work challenge
@@ -4430,7 +4759,6 @@
return false;
}
- @Override
protected void onWorkChallengeChanged() {
updatePublicMode();
updateNotifications();
@@ -4527,9 +4855,8 @@
return mKeyguardFadingAwayDuration;
}
- @Override
public void setBouncerShowing(boolean bouncerShowing) {
- super.setBouncerShowing(bouncerShowing);
+ mBouncerShowing = bouncerShowing;
mStatusBarView.setBouncerShowing(bouncerShowing);
recomputeDisableFlags(true /* animate */);
}
@@ -4609,7 +4936,6 @@
return !mNotificationData.getActiveNotifications().isEmpty();
}
- @Override
public void wakeUpIfDozing(long time, PointF where) {
if (mDozing && mDozeScrimController.isPulsing()) {
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -4703,7 +5029,7 @@
}
private void updateDozing() {
- Trace.beginSection("PhoneStatusBar#updateDozing");
+ Trace.beginSection("StatusBar#updateDozing");
// When in wake-and-unlock while pulsing, keep dozing state until fully unlocked.
mDozing = mDozingRequested && mState == StatusBarState.KEYGUARD
|| mFingerprintUnlockController.getMode()
@@ -4855,18 +5181,2022 @@
@Override
public void startPendingIntentDismissingKeyguard(PendingIntent intent) {
- PhoneStatusBar.this.startPendingIntentDismissingKeyguard(intent);
+ StatusBar.this.startPendingIntentDismissingKeyguard(intent);
}
}
- @Override
public SnoozeListener getSnoozeListener() {
return this;
}
@Override
- public void snoozeNotification(StatusBarNotification sbn, long snoozeUntil) {
- setNotificationSnoozed(sbn, snoozeUntil);
+ public void snoozeNotification(StatusBarNotification sbn, SnoozeOption snoozeOption) {
+ setNotificationSnoozed(sbn, snoozeOption);
}
+
+ // Begin Extra BaseStatusBar methods.
+
+ protected CommandQueue mCommandQueue;
+ protected IStatusBarService mBarService;
+
+ // all notifications
+ protected NotificationData mNotificationData;
+ protected NotificationStackScrollLayout mStackScroller;
+
+ protected NotificationGroupManager mGroupManager = new NotificationGroupManager();
+
+ protected RemoteInputController mRemoteInputController;
+
+ // for heads up notifications
+ protected HeadsUpManager mHeadsUpManager;
+
+ // handling reordering
+ protected VisualStabilityManager mVisualStabilityManager = new VisualStabilityManager();
+
+ protected int mCurrentUserId = 0;
+ final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
+
+ protected int mLayoutDirection = -1; // invalid
+ protected AccessibilityManager mAccessibilityManager;
+
+ protected boolean mDeviceInteractive;
+
+ protected boolean mVisible;
+ protected ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>();
+ protected ArraySet<Entry> mRemoteInputEntriesToRemoveOnCollapse = new ArraySet<>();
+
+ /**
+ * Notifications with keys in this set are not actually around anymore. We kept them around
+ * when they were canceled in response to a remote input interaction. This allows us to show
+ * what you replied and allows you to continue typing into it.
+ */
+ protected ArraySet<String> mKeysKeptForRemoteInput = new ArraySet<>();
+
+ // mScreenOnFromKeyguard && mVisible.
+ private boolean mVisibleToUser;
+
+ private Locale mLocale;
+ private float mFontScale;
+
+ protected boolean mUseHeadsUp = false;
+ protected boolean mHeadsUpTicker = false;
+ protected boolean mDisableNotificationAlerts = false;
+
+ protected DevicePolicyManager mDevicePolicyManager;
+ protected IDreamManager mDreamManager;
+ protected PowerManager mPowerManager;
+ protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+
+ // public mode, private notifications, etc
+ private final SparseBooleanArray mLockscreenPublicMode = new SparseBooleanArray();
+ private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
+ private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray();
+
+ private UserManager mUserManager;
+ private int mDensity;
+
+ protected KeyguardManager mKeyguardManager;
+ private LockPatternUtils mLockPatternUtils;
+ private DeviceProvisionedController mDeviceProvisionedController;
+
+ // UI-specific methods
+
+ protected WindowManager mWindowManager;
+ protected IWindowManager mWindowManagerService;
+
+ protected Display mDisplay;
+
+ protected RecentsComponent mRecents;
+
+ protected int mZenMode;
+
+ // which notification is currently being longpress-examined by the user
+ private NotificationGuts mNotificationGutsExposed;
+ private MenuItem mGutsMenuItem;
+
+ private KeyboardShortcuts mKeyboardShortcuts;
+
+ protected NotificationShelf mNotificationShelf;
+ protected DismissView mDismissView;
+ protected EmptyShadeView mEmptyShadeView;
+
+ private NotificationClicker mNotificationClicker = new NotificationClicker();
+
+ protected AssistManager mAssistManager;
+
+ protected boolean mVrMode;
+
+ private Set<String> mNonBlockablePkgs;
+
+ @Override // NotificationData.Environment
+ public boolean isDeviceProvisioned() {
+ return mDeviceProvisionedController.isDeviceProvisioned();
+ }
+
+ private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
+ @Override
+ public void onVrStateChanged(boolean enabled) {
+ mVrMode = enabled;
+ }
+ };
+
+ public boolean isDeviceInVrMode() {
+ return mVrMode;
+ }
+
+ private final DeviceProvisionedListener mDeviceProvisionedListener =
+ new DeviceProvisionedListener() {
+ @Override
+ public void onDeviceProvisionedChanged() {
+ updateNotifications();
+ }
+ };
+
+ protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ final int mode = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
+ setZenMode(mode);
+
+ updateLockscreenNotificationSetting();
+ }
+ };
+
+ private final ContentObserver mLockscreenSettingsObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or
+ // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ...
+ mUsersAllowingPrivateNotifications.clear();
+ mUsersAllowingNotifications.clear();
+ // ... and refresh all the notifications
+ updateLockscreenNotificationSetting();
+ updateNotifications();
+ }
+ };
+
+ private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
+ private final int[] mTmpInt2 = new int[2];
+
+ @Override
+ public boolean onClickHandler(
+ final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
+ view.getLocationInWindow(mTmpInt2);
+ wakeUpIfDozing(SystemClock.uptimeMillis(), new PointF(
+ mTmpInt2[0] + view.getWidth() / 2, mTmpInt2[1] + view.getHeight() / 2));
+
+
+ if (handleRemoteInput(view, pendingIntent, fillInIntent)) {
+ return true;
+ }
+
+ if (DEBUG) {
+ Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
+ }
+ logActionClick(view);
+ // The intent we are sending is for the application, which
+ // won't have permission to immediately start an activity after
+ // the user switches to home. We know it is safe to do at this
+ // point, so make sure new activity switches are now allowed.
+ try {
+ ActivityManager.getService().resumeAppSwitches();
+ } catch (RemoteException e) {
+ }
+ final boolean isActivity = pendingIntent.isActivity();
+ if (isActivity) {
+ final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
+ final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
+ mContext, pendingIntent.getIntent(), mCurrentUserId);
+ dismissKeyguardThenExecute(new OnDismissAction() {
+ @Override
+ public boolean onDismiss() {
+ try {
+ ActivityManager.getService().resumeAppSwitches();
+ } catch (RemoteException e) {
+ }
+
+ boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
+
+ // close the shade if it was open
+ if (handled) {
+ animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
+ true /* force */);
+ visibilityChanged(false);
+ mAssistManager.hideAssist();
+ }
+
+ // Wait for activity start.
+ return handled;
+ }
+ }, afterKeyguardGone);
+ return true;
+ } else {
+ return superOnClickHandler(view, pendingIntent, fillInIntent);
+ }
+ }
+
+ private void logActionClick(View view) {
+ ViewParent parent = view.getParent();
+ String key = getNotificationKeyForParent(parent);
+ if (key == null) {
+ Log.w(TAG, "Couldn't determine notification for click.");
+ return;
+ }
+ int index = -1;
+ // If this is a default template, determine the index of the button.
+ if (view.getId() == com.android.internal.R.id.action0 &&
+ parent != null && parent instanceof ViewGroup) {
+ ViewGroup actionGroup = (ViewGroup) parent;
+ index = actionGroup.indexOfChild(view);
+ }
+ try {
+ mBarService.onNotificationActionClick(key, index);
+ } catch (RemoteException e) {
+ // Ignore
+ }
+ }
+
+ private String getNotificationKeyForParent(ViewParent parent) {
+ while (parent != null) {
+ if (parent instanceof ExpandableNotificationRow) {
+ return ((ExpandableNotificationRow) parent).getStatusBarNotification().getKey();
+ }
+ parent = parent.getParent();
+ }
+ return null;
+ }
+
+ private boolean superOnClickHandler(View view, PendingIntent pendingIntent,
+ Intent fillInIntent) {
+ return super.onClickHandler(view, pendingIntent, fillInIntent,
+ StackId.FULLSCREEN_WORKSPACE_STACK_ID);
+ }
+
+ private boolean handleRemoteInput(View view, PendingIntent pendingIntent, Intent fillInIntent) {
+ Object tag = view.getTag(com.android.internal.R.id.remote_input_tag);
+ RemoteInput[] inputs = null;
+ if (tag instanceof RemoteInput[]) {
+ inputs = (RemoteInput[]) tag;
+ }
+
+ if (inputs == null) {
+ return false;
+ }
+
+ RemoteInput input = null;
+
+ for (RemoteInput i : inputs) {
+ if (i.getAllowFreeFormInput()) {
+ input = i;
+ }
+ }
+
+ if (input == null) {
+ return false;
+ }
+
+ ViewParent p = view.getParent();
+ RemoteInputView riv = null;
+ while (p != null) {
+ if (p instanceof View) {
+ View pv = (View) p;
+ if (pv.isRootNamespace()) {
+ riv = (RemoteInputView) pv.findViewWithTag(RemoteInputView.VIEW_TAG);
+ break;
+ }
+ }
+ p = p.getParent();
+ }
+ ExpandableNotificationRow row = null;
+ while (p != null) {
+ if (p instanceof ExpandableNotificationRow) {
+ row = (ExpandableNotificationRow) p;
+ break;
+ }
+ p = p.getParent();
+ }
+
+ if (riv == null || row == null) {
+ return false;
+ }
+
+ row.setUserExpanded(true);
+
+ if (!mAllowLockscreenRemoteInput) {
+ final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
+ if (isLockscreenPublicMode(userId)) {
+ onLockedRemoteInput(row, view);
+ return true;
+ }
+ if (mUserManager.getUserInfo(userId).isManagedProfile()
+ && mKeyguardManager.isDeviceLocked(userId)) {
+ onLockedWorkRemoteInput(userId, row, view);
+ return true;
+ }
+ }
+
+ int width = view.getWidth();
+ if (view instanceof TextView) {
+ // Center the reveal on the text which might be off-center from the TextView
+ TextView tv = (TextView) view;
+ if (tv.getLayout() != null) {
+ int innerWidth = (int) tv.getLayout().getLineWidth(0);
+ innerWidth += tv.getCompoundPaddingLeft() + tv.getCompoundPaddingRight();
+ width = Math.min(width, innerWidth);
+ }
+ }
+ int cx = view.getLeft() + width / 2;
+ int cy = view.getTop() + view.getHeight() / 2;
+ int w = riv.getWidth();
+ int h = riv.getHeight();
+ int r = Math.max(
+ Math.max(cx + cy, cx + (h - cy)),
+ Math.max((w - cx) + cy, (w - cx) + (h - cy)));
+
+ riv.setRevealParameters(cx, cy, r);
+ riv.setPendingIntent(pendingIntent);
+ riv.setRemoteInput(inputs, input);
+ riv.focusAnimated();
+
+ return true;
+ }
+
+ };
+
+ private final BroadcastReceiver mBaseBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ updateCurrentProfilesCache();
+ if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
+
+ updateLockscreenNotificationSetting();
+
+ userSwitched(mCurrentUserId);
+ } else if (Intent.ACTION_USER_ADDED.equals(action)) {
+ updateCurrentProfilesCache();
+ } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
+ List<ActivityManager.RecentTaskInfo> recentTask = null;
+ try {
+ recentTask = ActivityManager.getService().getRecentTasks(1,
+ ActivityManager.RECENT_WITH_EXCLUDED
+ | ActivityManager.RECENT_INCLUDE_PROFILES,
+ mCurrentUserId).getList();
+ } catch (RemoteException e) {
+ // Abandon hope activity manager not running.
+ }
+ if (recentTask != null && recentTask.size() > 0) {
+ UserInfo user = mUserManager.getUserInfo(recentTask.get(0).userId);
+ if (user != null && user.isManagedProfile()) {
+ Toast toast = Toast.makeText(mContext,
+ R.string.managed_profile_foreground_toast,
+ Toast.LENGTH_SHORT);
+ TextView text = (TextView) toast.getView().findViewById(
+ android.R.id.message);
+ text.setCompoundDrawablesRelativeWithIntrinsicBounds(
+ R.drawable.stat_sys_managed_profile_status, 0, 0, 0);
+ int paddingPx = mContext.getResources().getDimensionPixelSize(
+ R.dimen.managed_profile_toast_padding);
+ text.setCompoundDrawablePadding(paddingPx);
+ toast.show();
+ }
+ }
+ } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) {
+ NotificationManager noMan = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ noMan.cancel(SystemMessage.NOTE_HIDDEN_NOTIFICATIONS);
+
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
+ if (BANNER_ACTION_SETUP.equals(action)) {
+ animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
+ true /* force */);
+ mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+
+ );
+ }
+ } else if (NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION.equals(action)) {
+ final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT);
+ final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
+ if (intentSender != null) {
+ try {
+ mContext.startIntentSender(intentSender, null, 0, 0, 0);
+ } catch (IntentSender.SendIntentException e) {
+ /* ignore */
+ }
+ }
+ if (notificationKey != null) {
+ try {
+ mBarService.onNotificationClick(notificationKey);
+ } catch (RemoteException e) {
+ /* ignore */
+ }
+ }
+ }
+ }
+ };
+
+ private final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+
+ if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) &&
+ isCurrentProfile(getSendingUserId())) {
+ mUsersAllowingPrivateNotifications.clear();
+ updateLockscreenNotificationSetting();
+ updateNotifications();
+ } else if (Intent.ACTION_DEVICE_LOCKED_CHANGED.equals(action)) {
+ if (userId != mCurrentUserId && isCurrentProfile(userId)) {
+ onWorkChallengeChanged();
+ }
+ }
+ }
+ };
+
+ private final NotificationListenerService mNotificationListener =
+ new NotificationListenerService() {
+ @Override
+ public void onListenerConnected() {
+ if (DEBUG) Log.d(TAG, "onListenerConnected");
+ final StatusBarNotification[] notifications = getActiveNotifications();
+ if (notifications == null) {
+ Log.w(TAG, "onListenerConnected unable to get active notifications.");
+ return;
+ }
+ final RankingMap currentRanking = getCurrentRanking();
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ for (StatusBarNotification sbn : notifications) {
+ addNotification(sbn, currentRanking, null /* oldEntry */);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onNotificationPosted(final StatusBarNotification sbn,
+ final RankingMap rankingMap) {
+ if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
+ if (sbn != null) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ processForRemoteInput(sbn.getNotification());
+ String key = sbn.getKey();
+ mKeysKeptForRemoteInput.remove(key);
+ boolean isUpdate = mNotificationData.get(key) != null;
+ // In case we don't allow child notifications, we ignore children of
+ // notifications that have a summary, since we're not going to show them
+ // anyway. This is true also when the summary is canceled,
+ // because children are automatically canceled by NoMan in that case.
+ if (!ENABLE_CHILD_NOTIFICATIONS
+ && mGroupManager.isChildInGroupWithSummary(sbn)) {
+ if (DEBUG) {
+ Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
+ }
+
+ // Remove existing notification to avoid stale data.
+ if (isUpdate) {
+ removeNotification(key, rankingMap);
+ } else {
+ mNotificationData.updateRanking(rankingMap);
+ }
+ return;
+ }
+ if (isUpdate) {
+ updateNotification(sbn, rankingMap);
+ } else {
+ addNotification(sbn, rankingMap, null /* oldEntry */);
+ }
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onNotificationRemoved(StatusBarNotification sbn,
+ final RankingMap rankingMap) {
+ if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
+ if (sbn != null) {
+ final String key = sbn.getKey();
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ removeNotification(key, rankingMap);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onNotificationRankingUpdate(final RankingMap rankingMap) {
+ if (DEBUG) Log.d(TAG, "onRankingUpdate");
+ if (rankingMap != null) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ updateNotificationRanking(rankingMap);
+ }
+ });
+ } }
+
+ };
+
+ private void updateCurrentProfilesCache() {
+ synchronized (mCurrentProfiles) {
+ mCurrentProfiles.clear();
+ if (mUserManager != null) {
+ for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
+ mCurrentProfiles.put(user.id, user);
+ }
+ }
+ }
+ }
+
+ protected void notifyUserAboutHiddenNotifications() {
+ if (0 != Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) {
+ Log.d(TAG, "user hasn't seen notification about hidden notifications");
+ if (!mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
+ Log.d(TAG, "insecure lockscreen, skipping notification");
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
+ return;
+ }
+ Log.d(TAG, "disabling lockecreen notifications and alerting the user");
+ // disable lockscreen notifications until user acts on the banner.
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
+
+ final String packageName = mContext.getPackageName();
+ PendingIntent cancelIntent = PendingIntent.getBroadcast(mContext, 0,
+ new Intent(BANNER_ACTION_CANCEL).setPackage(packageName),
+ PendingIntent.FLAG_CANCEL_CURRENT);
+ PendingIntent setupIntent = PendingIntent.getBroadcast(mContext, 0,
+ new Intent(BANNER_ACTION_SETUP).setPackage(packageName),
+ PendingIntent.FLAG_CANCEL_CURRENT);
+
+ final int colorRes = com.android.internal.R.color.system_notification_accent_color;
+ Notification.Builder note = new Notification.Builder(mContext)
+ .setSmallIcon(R.drawable.ic_android)
+ .setContentTitle(mContext.getString(R.string.hidden_notifications_title))
+ .setContentText(mContext.getString(R.string.hidden_notifications_text))
+ .setChannel(NotificationChannels.SECURITY)
+ .setOngoing(true)
+ .setColor(mContext.getColor(colorRes))
+ .setContentIntent(setupIntent)
+ .addAction(R.drawable.ic_close,
+ mContext.getString(R.string.hidden_notifications_cancel),
+ cancelIntent)
+ .addAction(R.drawable.ic_settings,
+ mContext.getString(R.string.hidden_notifications_setup),
+ setupIntent);
+ overrideNotificationAppName(mContext, note);
+
+ NotificationManager noMan =
+ (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ noMan.notify(SystemMessage.NOTE_HIDDEN_NOTIFICATIONS, note.build());
+ }
+ }
+
+ @Override // NotificationData.Environment
+ public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
+ final int thisUserId = mCurrentUserId;
+ final int notificationUserId = n.getUserId();
+ if (DEBUG && MULTIUSER_DEBUG) {
+ Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
+ n, thisUserId, notificationUserId));
+ }
+ return isCurrentProfile(notificationUserId);
+ }
+
+ protected void setNotificationShown(StatusBarNotification n) {
+ setNotificationsShown(new String[]{n.getKey()});
+ }
+
+ protected void setNotificationsShown(String[] keys) {
+ try {
+ mNotificationListener.setNotificationsShown(keys);
+ } catch (RuntimeException e) {
+ Log.d(TAG, "failed setNotificationsShown: ", e);
+ }
+ }
+
+ protected boolean isCurrentProfile(int userId) {
+ synchronized (mCurrentProfiles) {
+ return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
+ }
+ }
+
+ @Override
+ public NotificationGroupManager getGroupManager() {
+ return mGroupManager;
+ }
+
+ protected void bindDismissRunnable(final ExpandableNotificationRow row) {
+ row.setOnDismissRunnable(() -> performRemoveNotification(row.getStatusBarNotification()));
+ }
+
+ protected void applyColorsAndBackgrounds(StatusBarNotification sbn,
+ NotificationData.Entry entry) {
+
+ if (entry.getContentView().getId()
+ != com.android.internal.R.id.status_bar_latest_event_content) {
+ // Using custom RemoteViews
+ if (entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
+ && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP) {
+ entry.row.setShowingLegacyBackground(true);
+ entry.legacy = true;
+ }
+ }
+
+ entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
+ }
+
+ public boolean isMediaNotification(NotificationData.Entry entry) {
+ // TODO: confirm that there's a valid media key
+ return entry.getExpandedContentView() != null &&
+ entry.getExpandedContentView()
+ .findViewById(com.android.internal.R.id.media_actions) != null;
+ }
+
+ // The (i) button in the guts that links to the system notification settings for that app
+ private void startAppNotificationSettingsActivity(String packageName, final int appUid) {
+ final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
+ intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
+ intent.putExtra(Settings.EXTRA_APP_UID, appUid);
+ startNotificationGutsIntent(intent, appUid);
+ }
+
+ private void startNotificationGutsIntent(final Intent intent, final int appUid) {
+ dismissKeyguardThenExecute(new OnDismissAction() {
+ @Override
+ public boolean onDismiss() {
+ AsyncTask.execute(new Runnable() {
+ @Override
+ public void run() {
+ TaskStackBuilder.create(mContext)
+ .addNextIntentWithParentStack(intent)
+ .startActivities(getActivityOptions(),
+ new UserHandle(UserHandle.getUserId(appUid)));
+ }
+ });
+ animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
+ return true;
+ }
+ }, false /* afterKeyguardGone */);
+ }
+
+ protected void setNotificationSnoozed(StatusBarNotification sbn, SnoozeOption snoozeOption) {
+ if (snoozeOption.criterion != null) {
+ mNotificationListener.snoozeNotification(sbn.getKey(), snoozeOption.criterion.getId());
+ } else {
+ GregorianCalendar snoozeUntil = new GregorianCalendar();
+ snoozeUntil.add(Calendar.MINUTE, snoozeOption.snoozeForMinutes);
+ mNotificationListener.snoozeNotification(sbn.getKey(), snoozeUntil.getTimeInMillis());
+ }
+ }
+
+ private void bindGuts(final ExpandableNotificationRow row, MenuItem item) {
+ row.inflateGuts();
+ row.setGutsView(item);
+ final StatusBarNotification sbn = row.getStatusBarNotification();
+ row.setTag(sbn.getPackageName());
+ final NotificationGuts guts = row.getGuts();
+ guts.setClosedListener((NotificationGuts g) -> {
+ if (!row.isRemoved()) {
+ mStackScroller.onHeightChanged(row, !isPanelFullyCollapsed() /* needsAnimation */);
+ }
+ mNotificationGutsExposed = null;
+ mGutsMenuItem = null;
+ });
+
+ if (item.gutsContent instanceof SnoozeGutsContent) {
+ ((SnoozeGutsContent) item.gutsContent).setSnoozeListener(getSnoozeListener());
+ ((SnoozeGutsContent) item.gutsContent).setStatusBarNotification(sbn);
+ ((NotificationSnooze) item.gutsContent).setSnoozeOptions(row.getEntry().snoozeCriteria);
+ }
+
+ if (item.gutsContent instanceof NotificationInfo) {
+ final NotificationChannel channel = row.getEntry().channel;
+ PackageManager pmUser = getPackageManagerForUser(mContext,
+ sbn.getUser().getIdentifier());
+ final INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ final String pkg = sbn.getPackageName();
+ NotificationInfo info = (NotificationInfo) item.gutsContent;
+ final NotificationInfo.OnSettingsClickListener onSettingsClick = (View v,
+ int appUid) -> {
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_INFO);
+ guts.resetFalsingCheck();
+ startAppNotificationSettingsActivity(pkg, appUid);
+ };
+ final View.OnClickListener onDoneClick = (View v) -> {
+ // If the user has security enabled, show challenge if the setting is changed.
+ if (info.hasImportanceChanged()
+ && isLockscreenPublicMode(sbn.getUser().getIdentifier())
+ && (mState == StatusBarState.KEYGUARD
+ || mState == StatusBarState.SHADE_LOCKED)) {
+ OnDismissAction dismissAction = new OnDismissAction() {
+ @Override
+ public boolean onDismiss() {
+ saveAndCloseNotificationMenu(info, row, guts, v);
+ return true;
+ }
+ };
+ onLockedNotificationImportanceChange(dismissAction);
+ } else {
+ saveAndCloseNotificationMenu(info, row, guts, v);
+ }
+ };
+ info.bindNotification(pmUser, iNotificationManager, sbn, channel, onSettingsClick,
+ onDoneClick,
+ mNonBlockablePkgs);
+ }
+ }
+
+ private void saveAndCloseNotificationMenu(NotificationInfo info,
+ ExpandableNotificationRow row, NotificationGuts guts, View done) {
+ guts.resetFalsingCheck();
+ info.saveImportance();
+ int[] rowLocation = new int[2];
+ int[] doneLocation = new int[2];
+ row.getLocationOnScreen(rowLocation);
+ done.getLocationOnScreen(doneLocation);
+
+ final int centerX = done.getWidth() / 2;
+ final int centerY = done.getHeight() / 2;
+ final int x = doneLocation[0] - rowLocation[0] + centerX;
+ final int y = doneLocation[1] - rowLocation[1] + centerY;
+ dismissPopups(x, y);
+ }
+
+ protected SwipeHelper.LongPressListener getNotificationLongClicker() {
+ return new SwipeHelper.LongPressListener() {
+ @Override
+ public boolean onLongPress(View v, final int x, final int y,
+ MenuItem item) {
+ if (!(v instanceof ExpandableNotificationRow)) {
+ return false;
+ }
+ if (v.getWindowToken() == null) {
+ Log.e(TAG, "Trying to show notification guts, but not attached to window");
+ return false;
+ }
+
+ final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ bindGuts(row, item);
+ NotificationGuts guts = row.getGuts();
+
+ // Assume we are a status_bar_notification_row
+ if (guts == null) {
+ // This view has no guts. Examples are the more card or the dismiss all view
+ return false;
+ }
+
+ // Already showing?
+ if (guts.getVisibility() == View.VISIBLE) {
+ dismissPopups(x, y);
+ return false;
+ }
+
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_CONTROLS);
+
+ // ensure that it's laid but not visible until actually laid out
+ guts.setVisibility(View.INVISIBLE);
+ // Post to ensure the the guts are properly laid out.
+ guts.post(new Runnable() {
+ @Override
+ public void run() {
+ if (row.getWindowToken() == null) {
+ Log.e(TAG, "Trying to show notification guts, but not attached to "
+ + "window");
+ return;
+ }
+ dismissPopups(-1 /* x */, -1 /* y */, false /* resetGear */,
+ false /* animate */);
+ guts.setVisibility(View.VISIBLE);
+ final double horz = Math.max(guts.getWidth() - x, x);
+ final double vert = Math.max(guts.getHeight() - y, y);
+ final float r = (float) Math.hypot(horz, vert);
+ final Animator a
+ = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
+ a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ a.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ // Move the notification view back over the gear
+ row.resetTranslation();
+ }
+ });
+ a.start();
+ guts.setExposed(true /* exposed */,
+ mState == StatusBarState.KEYGUARD /* needsFalsingProtection */);
+ row.closeRemoteInput();
+ mStackScroller.onHeightChanged(row, true /* needsAnimation */);
+ mNotificationGutsExposed = guts;
+ mGutsMenuItem = item;
+ }
+ });
+ return true;
+ }
+ };
+ }
+
+ /**
+ * Returns the exposed NotificationGuts or null if none are exposed.
+ */
+ public NotificationGuts getExposedGuts() {
+ return mNotificationGutsExposed;
+ }
+
+ public void dismissPopups() {
+ dismissPopups(-1 /* x */, -1 /* y */, true /* resetGear */, false /* animate */);
+ }
+
+ private void dismissPopups(int x, int y) {
+ dismissPopups(x, y, true /* resetGear */, false /* animate */);
+ }
+
+ public void dismissPopups(int x, int y, boolean resetGear, boolean animate) {
+ if (mNotificationGutsExposed != null) {
+ mNotificationGutsExposed.closeControls(x, y, true /* save */);
+ }
+ if (resetGear) {
+ mStackScroller.resetExposedGearView(animate, true /* force */);
+ }
+ }
+
+ @Override
+ public void showRecentApps(boolean triggeredFromAltTab, boolean fromHome) {
+ int msg = MSG_SHOW_RECENT_APPS;
+ mHandler.removeMessages(msg);
+ mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0, fromHome ? 1 : 0).sendToTarget();
+ }
+
+ @Override
+ public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
+ int msg = MSG_HIDE_RECENT_APPS;
+ mHandler.removeMessages(msg);
+ mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0,
+ triggeredFromHomeKey ? 1 : 0).sendToTarget();
+ }
+
+ @Override
+ public void toggleRecentApps() {
+ toggleRecents();
+ }
+
+ @Override
+ public void toggleSplitScreen() {
+ toggleSplitScreenMode(-1 /* metricsDockAction */, -1 /* metricsUndockAction */);
+ }
+
+ @Override
+ public void preloadRecentApps() {
+ int msg = MSG_PRELOAD_RECENT_APPS;
+ mHandler.removeMessages(msg);
+ mHandler.sendEmptyMessage(msg);
+ }
+
+ @Override
+ public void cancelPreloadRecentApps() {
+ int msg = MSG_CANCEL_PRELOAD_RECENT_APPS;
+ mHandler.removeMessages(msg);
+ mHandler.sendEmptyMessage(msg);
+ }
+
+ @Override
+ public void dismissKeyboardShortcutsMenu() {
+ int msg = MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU;
+ mHandler.removeMessages(msg);
+ mHandler.sendEmptyMessage(msg);
+ }
+
+ @Override
+ public void toggleKeyboardShortcutsMenu(int deviceId) {
+ int msg = MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU;
+ mHandler.removeMessages(msg);
+ mHandler.obtainMessage(msg, deviceId, 0).sendToTarget();
+ }
+
+ /** Jumps to the next affiliated task in the group. */
+ public void showNextAffiliatedTask() {
+ int msg = MSG_SHOW_NEXT_AFFILIATED_TASK;
+ mHandler.removeMessages(msg);
+ mHandler.sendEmptyMessage(msg);
+ }
+
+ /** Jumps to the previous affiliated task in the group. */
+ public void showPreviousAffiliatedTask() {
+ int msg = MSG_SHOW_PREV_AFFILIATED_TASK;
+ mHandler.removeMessages(msg);
+ mHandler.sendEmptyMessage(msg);
+ }
+
+ protected void sendCloseSystemWindows(String reason) {
+ try {
+ ActivityManager.getService().closeSystemDialogs(reason);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /** Proxy for RecentsComponent */
+
+ protected void showRecents(boolean triggeredFromAltTab, boolean fromHome) {
+ if (mRecents != null) {
+ sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
+ mRecents.showRecents(triggeredFromAltTab, fromHome);
+ }
+ }
+
+ protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
+ if (mRecents != null) {
+ mRecents.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
+ }
+ }
+
+ protected void toggleRecents() {
+ if (mRecents != null) {
+ mRecents.toggleRecents(mDisplay);
+ }
+ }
+
+ protected void preloadRecents() {
+ if (mRecents != null) {
+ mRecents.preloadRecents();
+ }
+ }
+
+ protected void toggleKeyboardShortcuts(int deviceId) {
+ KeyboardShortcuts.toggle(mContext, deviceId);
+ }
+
+ protected void dismissKeyboardShortcuts() {
+ KeyboardShortcuts.dismiss();
+ }
+
+ protected void cancelPreloadingRecents() {
+ if (mRecents != null) {
+ mRecents.cancelPreloadingRecents();
+ }
+ }
+
+ protected void showRecentsNextAffiliatedTask() {
+ if (mRecents != null) {
+ mRecents.showNextAffiliatedTask();
+ }
+ }
+
+ protected void showRecentsPreviousAffiliatedTask() {
+ if (mRecents != null) {
+ mRecents.showPrevAffiliatedTask();
+ }
+ }
+
+ /**
+ * Save the current "public" (locked and secure) state of the lockscreen.
+ */
+ public void setLockscreenPublicMode(boolean publicMode, int userId) {
+ mLockscreenPublicMode.put(userId, publicMode);
+ }
+
+ public boolean isLockscreenPublicMode(int userId) {
+ return mLockscreenPublicMode.get(userId, false);
+ }
+
+ /**
+ * Has the given user chosen to allow notifications to be shown even when the lockscreen is in
+ * "public" (secure & locked) mode?
+ */
+ public boolean userAllowsNotificationsInPublic(int userHandle) {
+ if (userHandle == UserHandle.USER_ALL) {
+ return true;
+ }
+
+ if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
+ final boolean allowed = 0 != Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle);
+ mUsersAllowingNotifications.append(userHandle, allowed);
+ return allowed;
+ }
+
+ return mUsersAllowingNotifications.get(userHandle);
+ }
+
+ /**
+ * Has the given user chosen to allow their private (full) notifications to be shown even
+ * when the lockscreen is in "public" (secure & locked) mode?
+ */
+ public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
+ if (userHandle == UserHandle.USER_ALL) {
+ return true;
+ }
+
+ if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
+ final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
+ final boolean allowedByDpm = adminAllowsUnredactedNotifications(userHandle);
+ final boolean allowed = allowedByUser && allowedByDpm;
+ mUsersAllowingPrivateNotifications.append(userHandle, allowed);
+ return allowed;
+ }
+
+ return mUsersAllowingPrivateNotifications.get(userHandle);
+ }
+
+ private boolean adminAllowsUnredactedNotifications(int userHandle) {
+ if (userHandle == UserHandle.USER_ALL) {
+ return true;
+ }
+ final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */,
+ userHandle);
+ return (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0;
+ }
+
+ /**
+ * Returns true if we're on a secure lockscreen and the user wants to hide notification data.
+ * If so, notifications should be hidden.
+ */
+ @Override // NotificationData.Environment
+ public boolean shouldHideNotifications(int userId) {
+ return isLockscreenPublicMode(userId) && !userAllowsNotificationsInPublic(userId)
+ || (userId != mCurrentUserId && shouldHideNotifications(mCurrentUserId));
+ }
+
+ /**
+ * Returns true if we're on a secure lockscreen and the user wants to hide notifications via
+ * package-specific override.
+ */
+ @Override // NotificationDate.Environment
+ public boolean shouldHideNotifications(String key) {
+ return isLockscreenPublicMode(mCurrentUserId)
+ && mNotificationData.getVisibilityOverride(key) == Notification.VISIBILITY_SECRET;
+ }
+
+ /**
+ * Returns true if we're on a secure lockscreen.
+ */
+ @Override // NotificationData.Environment
+ public boolean isSecurelyLocked(int userId) {
+ return isLockscreenPublicMode(userId);
+ }
+
+ public void onNotificationClear(StatusBarNotification notification) {
+ try {
+ mBarService.onNotificationClear(
+ notification.getPackageName(),
+ notification.getTag(),
+ notification.getId(),
+ notification.getUserId());
+ } catch (android.os.RemoteException ex) {
+ // oh well
+ }
+ }
+
+ /**
+ * Called when the notification panel layouts
+ */
+ public void onPanelLaidOut() {
+ if (mState == StatusBarState.KEYGUARD) {
+ // Since the number of notifications is determined based on the height of the view, we
+ // need to update them.
+ int maxBefore = getMaxKeyguardNotifications(false /* recompute */);
+ int maxNotifications = getMaxKeyguardNotifications(true /* recompute */);
+ if (maxBefore != maxNotifications) {
+ updateRowStates();
+ }
+ }
+ }
+
+ protected boolean inflateViews(Entry entry, ViewGroup parent) {
+ PackageManager pmUser = getPackageManagerForUser(mContext,
+ entry.notification.getUser().getIdentifier());
+
+ final StatusBarNotification sbn = entry.notification;
+ boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
+ try {
+ entry.cacheContentViews(mContext, null, isLowPriority);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Unable to get notification remote views", e);
+ return false;
+ }
+
+ final RemoteViews contentView = entry.cachedContentView;
+ final RemoteViews bigContentView = entry.cachedBigContentView;
+ final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView;
+ final RemoteViews publicContentView = entry.cachedPublicContentView;
+ final RemoteViews ambientContentView = entry.cachedAmbientContentView;
+
+ if (contentView == null) {
+ Log.v(TAG, "no contentView for: " + sbn.getNotification());
+ return false;
+ }
+
+ if (DEBUG) {
+ Log.v(TAG, "publicContentView: " + publicContentView);
+ }
+
+ ExpandableNotificationRow row;
+
+ // Stash away previous user expansion state so we can restore it at
+ // the end.
+ boolean hasUserChangedExpansion = false;
+ boolean userExpanded = false;
+ boolean userLocked = false;
+
+ if (entry.row != null) {
+ row = entry.row;
+ hasUserChangedExpansion = row.hasUserChangedExpansion();
+ userExpanded = row.isUserExpanded();
+ userLocked = row.isUserLocked();
+ entry.reset();
+ if (hasUserChangedExpansion) {
+ row.setUserExpanded(userExpanded);
+ }
+ } else {
+ // create the row view
+ LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row,
+ parent, false);
+ row.setExpansionLogger(this, entry.notification.getKey());
+ row.setGroupManager(mGroupManager);
+ row.setHeadsUpManager(mHeadsUpManager);
+ row.setRemoteInputController(mRemoteInputController);
+ row.setOnExpandClickListener(this);
+
+ // Get the app name.
+ // Note that Notification.Builder#bindHeaderAppName has similar logic
+ // but since this field is used in the guts, it must be accurate.
+ // Therefore we will only show the application label, or, failing that, the
+ // package name. No substitutions.
+ final String pkg = sbn.getPackageName();
+ String appname = pkg;
+ try {
+ final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS);
+ if (info != null) {
+ appname = String.valueOf(pmUser.getApplicationLabel(info));
+ }
+ } catch (NameNotFoundException e) {
+ // Do nothing
+ }
+ row.setAppName(appname);
+ }
+
+ bindDismissRunnable(row);
+ row.setIsLowPriority(isLowPriority);
+
+ // NB: the large icon is now handled entirely by the template
+
+ // bind the click event to the content area
+ NotificationContentView contentContainer = row.getPrivateLayout();
+ NotificationContentView contentContainerPublic = row.getPublicLayout();
+
+ row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+ if (ENABLE_REMOTE_INPUT) {
+ row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
+ }
+
+ mNotificationClicker.register(row, sbn);
+
+ // set up the adaptive layout
+ View contentViewLocal = null;
+ View bigContentViewLocal = null;
+ View headsUpContentViewLocal = null;
+ View publicViewLocal = null;
+ View ambientViewLocal = null;
+ try {
+ contentViewLocal = contentView.apply(
+ sbn.getPackageContext(mContext),
+ contentContainer,
+ mOnClickHandler);
+ if (bigContentView != null) {
+ bigContentViewLocal = bigContentView.apply(
+ sbn.getPackageContext(mContext),
+ contentContainer,
+ mOnClickHandler);
+ }
+ if (headsUpContentView != null) {
+ headsUpContentViewLocal = headsUpContentView.apply(
+ sbn.getPackageContext(mContext),
+ contentContainer,
+ mOnClickHandler);
+ }
+ if (publicContentView != null) {
+ publicViewLocal = publicContentView.apply(
+ sbn.getPackageContext(mContext),
+ contentContainerPublic, mOnClickHandler);
+ }
+ if (ambientContentView != null) {
+ ambientViewLocal = ambientContentView.apply(
+ sbn.getPackageContext(mContext),
+ contentContainer, mOnClickHandler);
+ }
+
+ if (contentViewLocal != null) {
+ contentViewLocal.setIsRootNamespace(true);
+ contentContainer.setContractedChild(contentViewLocal);
+ }
+ if (bigContentViewLocal != null) {
+ bigContentViewLocal.setIsRootNamespace(true);
+ contentContainer.setExpandedChild(bigContentViewLocal);
+ }
+ if (headsUpContentViewLocal != null) {
+ headsUpContentViewLocal.setIsRootNamespace(true);
+ contentContainer.setHeadsUpChild(headsUpContentViewLocal);
+ }
+ if (publicViewLocal != null) {
+ publicViewLocal.setIsRootNamespace(true);
+ contentContainerPublic.setContractedChild(publicViewLocal);
+ }
+
+ if (ambientViewLocal != null) {
+ ambientViewLocal.setIsRootNamespace(true);
+ contentContainer.setAmbientChild(ambientViewLocal);
+ }
+ }
+ catch (RuntimeException e) {
+ final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
+ Log.e(TAG, "couldn't inflate view for notification " + ident, e);
+ return false;
+ }
+
+ // Extract target SDK version.
+ try {
+ ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
+ entry.targetSdk = info.targetSdkVersion;
+ } catch (NameNotFoundException ex) {
+ Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
+ }
+ entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
+
+ entry.row = row;
+ entry.row.setOnActivatedListener(this);
+ entry.row.setExpandable(bigContentViewLocal != null);
+
+ applyColorsAndBackgrounds(sbn, entry);
+
+ // Restore previous flags.
+ if (hasUserChangedExpansion) {
+ // Note: setUserExpanded() conveniently ignores calls with
+ // userExpanded=true if !isExpandable().
+ row.setUserExpanded(userExpanded);
+ }
+ row.setUserLocked(userLocked);
+ row.onNotificationUpdated(entry);
+ return true;
+ }
+
+ /**
+ * Adds RemoteInput actions from the WearableExtender; to be removed once more apps support this
+ * via first-class API.
+ *
+ * TODO: Remove once enough apps specify remote inputs on their own.
+ */
+ private void processForRemoteInput(Notification n) {
+ if (!ENABLE_REMOTE_INPUT) return;
+
+ if (n.extras != null && n.extras.containsKey("android.wearable.EXTENSIONS") &&
+ (n.actions == null || n.actions.length == 0)) {
+ Notification.Action viableAction = null;
+ Notification.WearableExtender we = new Notification.WearableExtender(n);
+
+ List<Notification.Action> actions = we.getActions();
+ final int numActions = actions.size();
+
+ for (int i = 0; i < numActions; i++) {
+ Notification.Action action = actions.get(i);
+ if (action == null) {
+ continue;
+ }
+ RemoteInput[] remoteInputs = action.getRemoteInputs();
+ if (remoteInputs == null) {
+ continue;
+ }
+ for (RemoteInput ri : remoteInputs) {
+ if (ri.getAllowFreeFormInput()) {
+ viableAction = action;
+ break;
+ }
+ }
+ if (viableAction != null) {
+ break;
+ }
+ }
+
+ if (viableAction != null) {
+ Notification.Builder rebuilder = Notification.Builder.recoverBuilder(mContext, n);
+ rebuilder.setActions(viableAction);
+ rebuilder.build(); // will rewrite n
+ }
+ }
+ }
+
+ public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
+ if (!isDeviceProvisioned()) return;
+
+ final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
+ final boolean afterKeyguardGone = intent.isActivity()
+ && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
+ mCurrentUserId);
+ dismissKeyguardThenExecute(new OnDismissAction() {
+ @Override
+ public boolean onDismiss() {
+ new Thread() {
+ @Override
+ public void run() {
+ try {
+ // The intent we are sending is for the application, which
+ // won't have permission to immediately start an activity after
+ // the user switches to home. We know it is safe to do at this
+ // point, so make sure new activity switches are now allowed.
+ ActivityManager.getService().resumeAppSwitches();
+ } catch (RemoteException e) {
+ }
+ try {
+ intent.send(null, 0, null, null, null, null, getActivityOptions());
+ } catch (PendingIntent.CanceledException e) {
+ // the stack trace isn't very helpful here.
+ // Just log the exception message.
+ Log.w(TAG, "Sending intent failed: " + e);
+
+ // TODO: Dismiss Keyguard.
+ }
+ if (intent.isActivity()) {
+ mAssistManager.hideAssist();
+ }
+ }
+ }.start();
+
+ // close the shade if it was open
+ animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
+ true /* force */, true /* delayed */);
+ visibilityChanged(false);
+
+ return true;
+ }
+ }, afterKeyguardGone);
+ }
+
+
+ private final class NotificationClicker implements View.OnClickListener {
+ private final int[] mTmpInt2 = new int[2];
+
+ @Override
+ public void onClick(final View v) {
+ if (!(v instanceof ExpandableNotificationRow)) {
+ Log.e(TAG, "NotificationClicker called on a view that is not a notification row.");
+ return;
+ }
+
+ v.getLocationInWindow(mTmpInt2);
+ wakeUpIfDozing(SystemClock.uptimeMillis(),
+ new PointF(mTmpInt2[0] + v.getWidth() / 2, mTmpInt2[1] + v.getHeight() / 2));
+
+ final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ final StatusBarNotification sbn = row.getStatusBarNotification();
+ if (sbn == null) {
+ Log.e(TAG, "NotificationClicker called on an unclickable notification,");
+ return;
+ }
+
+ // Check if the notification is displaying the gear, if so slide notification back
+ if (row.getSettingsRow() != null && row.getSettingsRow().isVisible()) {
+ row.animateTranslateNotification(0);
+ return;
+ }
+
+ Notification notification = sbn.getNotification();
+ final PendingIntent intent = notification.contentIntent != null
+ ? notification.contentIntent
+ : notification.fullScreenIntent;
+ final String notificationKey = sbn.getKey();
+
+ // Mark notification for one frame.
+ row.setJustClicked(true);
+ DejankUtils.postAfterTraversal(new Runnable() {
+ @Override
+ public void run() {
+ row.setJustClicked(false);
+ }
+ });
+
+ final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
+ final boolean afterKeyguardGone = intent.isActivity()
+ && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
+ mCurrentUserId);
+ dismissKeyguardThenExecute(new OnDismissAction() {
+ @Override
+ public boolean onDismiss() {
+ if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) {
+ // Release the HUN notification to the shade.
+
+ if (isPanelFullyCollapsed()) {
+ HeadsUpManager.setIsClickedNotification(row, true);
+ }
+ //
+ // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
+ // become canceled shortly by NoMan, but we can't assume that.
+ mHeadsUpManager.releaseImmediately(notificationKey);
+ }
+ StatusBarNotification parentToCancel = null;
+ if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
+ StatusBarNotification summarySbn = mGroupManager.getLogicalGroupSummary(sbn)
+ .getStatusBarNotification();
+ if (shouldAutoCancel(summarySbn)) {
+ parentToCancel = summarySbn;
+ }
+ }
+ final StatusBarNotification parentToCancelFinal = parentToCancel;
+ new Thread() {
+ @Override
+ public void run() {
+ try {
+ // The intent we are sending is for the application, which
+ // won't have permission to immediately start an activity after
+ // the user switches to home. We know it is safe to do at this
+ // point, so make sure new activity switches are now allowed.
+ ActivityManager.getService().resumeAppSwitches();
+ } catch (RemoteException e) {
+ }
+ if (intent != null) {
+ // If we are launching a work activity and require to launch
+ // separate work challenge, we defer the activity action and cancel
+ // notification until work challenge is unlocked.
+ if (intent.isActivity()) {
+ final int userId = intent.getCreatorUserHandle()
+ .getIdentifier();
+ if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
+ && mKeyguardManager.isDeviceLocked(userId)) {
+ boolean canBypass = false;
+ try {
+ canBypass = ActivityManager.getService()
+ .canBypassWorkChallenge(intent);
+ } catch (RemoteException e) {
+ }
+ // For direct-boot aware activities, they can be shown when
+ // the device is still locked without triggering the work
+ // challenge.
+ if ((!canBypass) && startWorkChallengeIfNecessary(userId,
+ intent.getIntentSender(), notificationKey)) {
+ // Show work challenge, do not run PendingIntent and
+ // remove notification
+ return;
+ }
+ }
+ }
+ try {
+ intent.send(null, 0, null, null, null, null,
+ getActivityOptions());
+ } catch (PendingIntent.CanceledException e) {
+ // the stack trace isn't very helpful here.
+ // Just log the exception message.
+ Log.w(TAG, "Sending contentIntent failed: " + e);
+
+ // TODO: Dismiss Keyguard.
+ }
+ if (intent.isActivity()) {
+ mAssistManager.hideAssist();
+ }
+ }
+
+ try {
+ mBarService.onNotificationClick(notificationKey);
+ } catch (RemoteException ex) {
+ // system process is dead if we're here.
+ }
+ if (parentToCancelFinal != null) {
+ // We have to post it to the UI thread for synchronization
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ Runnable removeRunnable = new Runnable() {
+ @Override
+ public void run() {
+ performRemoveNotification(parentToCancelFinal);
+ }
+ };
+ if (isCollapsing()) {
+ // To avoid lags we're only performing the remove
+ // after the shade was collapsed
+ addPostCollapseAction(removeRunnable);
+ } else {
+ removeRunnable.run();
+ }
+ }
+ });
+ }
+ }
+ }.start();
+
+ // close the shade if it was open
+ animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
+ true /* force */, true /* delayed */);
+ visibilityChanged(false);
+
+ return true;
+ }
+ }, afterKeyguardGone);
+ }
+
+ private boolean shouldAutoCancel(StatusBarNotification sbn) {
+ int flags = sbn.getNotification().flags;
+ if ((flags & Notification.FLAG_AUTO_CANCEL) != Notification.FLAG_AUTO_CANCEL) {
+ return false;
+ }
+ if ((flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+ return false;
+ }
+ return true;
+ }
+
+ public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
+ Notification notification = sbn.getNotification();
+ if (notification.contentIntent != null || notification.fullScreenIntent != null) {
+ row.setOnClickListener(this);
+ } else {
+ row.setOnClickListener(null);
+ }
+ }
+ }
+
+ protected Bundle getActivityOptions() {
+ // Anything launched from the notification shade should always go into the
+ // fullscreen stack.
+ ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchStackId(StackId.FULLSCREEN_WORKSPACE_STACK_ID);
+ return options.toBundle();
+ }
+
+ protected void visibilityChanged(boolean visible) {
+ if (mVisible != visible) {
+ mVisible = visible;
+ if (!visible) {
+ dismissPopups();
+ }
+ }
+ updateVisibleToUser();
+ }
+
+ protected void updateVisibleToUser() {
+ boolean oldVisibleToUser = mVisibleToUser;
+ mVisibleToUser = mVisible && mDeviceInteractive;
+
+ if (oldVisibleToUser != mVisibleToUser) {
+ handleVisibleToUserChanged(mVisibleToUser);
+ }
+ }
+
+ /**
+ * Clear Buzz/Beep/Blink.
+ */
+ public void clearNotificationEffects() {
+ try {
+ mBarService.clearNotificationEffects();
+ } catch (RemoteException e) {
+ // Won't fail unless the world has ended.
+ }
+ }
+
+ /**
+ * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
+ * about the failure.
+ *
+ * WARNING: this will call back into us. Don't hold any locks.
+ */
+ void handleNotificationError(StatusBarNotification n, String message) {
+ removeNotification(n.getKey(), null);
+ try {
+ mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
+ n.getInitialPid(), message, n.getUserId());
+ } catch (RemoteException ex) {
+ // The end is nigh.
+ }
+ }
+
+ protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) {
+ NotificationData.Entry entry = mNotificationData.remove(key, ranking);
+ if (entry == null) {
+ Log.w(TAG, "removeNotification for unknown key: " + key);
+ return null;
+ }
+ updateNotifications();
+ return entry.notification;
+ }
+
+ protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) {
+ if (DEBUG) {
+ Log.d(TAG, "createNotificationViews(notification=" + sbn);
+ }
+ NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ try {
+ entry.createIcons(mContext, sbn);
+ } catch (NotificationData.IconException exception) {
+ handleNotificationError(sbn, exception.getMessage());
+ }
+
+ // Construct the expanded view.
+ if (!inflateViews(entry, mStackScroller)) {
+ handleNotificationError(sbn, "Couldn't expand RemoteViews for: " + sbn);
+ return null;
+ }
+ return entry;
+ }
+
+ protected void addNotificationViews(Entry entry, RankingMap ranking) {
+ if (entry == null) {
+ return;
+ }
+ // Add the expanded view and icon.
+ mNotificationData.add(entry, ranking);
+ updateNotifications();
+ }
+
+ /**
+ * Updates expanded, dimmed and locked states of notification rows.
+ */
+ protected void updateRowStates() {
+ final int N = mStackScroller.getChildCount();
+
+ int visibleNotifications = 0;
+ boolean onKeyguard = mState == StatusBarState.KEYGUARD;
+ int maxNotifications = -1;
+ if (onKeyguard) {
+ maxNotifications = getMaxKeyguardNotifications(true /* recompute */);
+ }
+ mStackScroller.setMaxDisplayedNotifications(maxNotifications);
+ Stack<ExpandableNotificationRow> stack = new Stack<>();
+ for (int i = N - 1; i >= 0; i--) {
+ View child = mStackScroller.getChildAt(i);
+ if (!(child instanceof ExpandableNotificationRow)) {
+ continue;
+ }
+ stack.push((ExpandableNotificationRow) child);
+ }
+ while(!stack.isEmpty()) {
+ ExpandableNotificationRow row = stack.pop();
+ NotificationData.Entry entry = row.getEntry();
+ boolean childNotification = mGroupManager.isChildInGroupWithSummary(entry.notification);
+ if (onKeyguard) {
+ row.setOnKeyguard(true);
+ } else {
+ row.setOnKeyguard(false);
+ row.setSystemExpanded(visibleNotifications == 0 && !childNotification);
+ }
+ entry.row.setShowAmbient(isDozing());
+ int userId = entry.notification.getUserId();
+ boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
+ entry.notification) && !entry.row.isRemoved();
+ boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
+ if (suppressedSummary
+ || (isLockscreenPublicMode(userId) && !mShowLockscreenNotifications)
+ || (onKeyguard && !showOnKeyguard)) {
+ entry.row.setVisibility(View.GONE);
+ } else {
+ boolean wasGone = entry.row.getVisibility() == View.GONE;
+ if (wasGone) {
+ entry.row.setVisibility(View.VISIBLE);
+ }
+ if (!childNotification && !entry.row.isRemoved()) {
+ if (wasGone) {
+ // notify the scroller of a child addition
+ mStackScroller.generateAddAnimation(entry.row,
+ !showOnKeyguard /* fromMoreCard */);
+ }
+ visibleNotifications++;
+ }
+ }
+ if (row.isSummaryWithChildren()) {
+ List<ExpandableNotificationRow> notificationChildren =
+ row.getNotificationChildren();
+ int size = notificationChildren.size();
+ for (int i = size - 1; i >= 0; i--) {
+ stack.push(notificationChildren.get(i));
+ }
+ }
+ }
+
+ mStackScroller.changeViewPosition(mDismissView, mStackScroller.getChildCount() - 1);
+ mStackScroller.changeViewPosition(mEmptyShadeView, mStackScroller.getChildCount() - 2);
+ mStackScroller.changeViewPosition(mNotificationShelf, mStackScroller.getChildCount() - 3);
+ }
+
+ public boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
+ return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey());
+ }
+
+ // extended in StatusBar
+ protected void setShowLockscreenNotifications(boolean show) {
+ mShowLockscreenNotifications = show;
+ }
+
+ protected void setLockScreenAllowRemoteInput(boolean allowLockscreenRemoteInput) {
+ mAllowLockscreenRemoteInput = allowLockscreenRemoteInput;
+ }
+
+ private void updateLockscreenNotificationSetting() {
+ final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+ 1,
+ mCurrentUserId) != 0;
+ final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
+ null /* admin */, mCurrentUserId);
+ final boolean allowedByDpm = (dpmFlags
+ & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
+
+ setShowLockscreenNotifications(show && allowedByDpm);
+
+ if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
+ final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
+ 0,
+ mCurrentUserId) != 0;
+ final boolean remoteInputDpm =
+ (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT) == 0;
+
+ setLockScreenAllowRemoteInput(remoteInput && remoteInputDpm);
+ } else {
+ setLockScreenAllowRemoteInput(false);
+ }
+ }
+
+ public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
+ if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
+
+ final String key = notification.getKey();
+ Entry entry = mNotificationData.get(key);
+ if (entry == null) {
+ return;
+ } else {
+ mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
+ mRemoteInputEntriesToRemoveOnCollapse.remove(entry);
+ }
+
+ Notification n = notification.getNotification();
+ mNotificationData.updateRanking(ranking);
+
+ boolean applyInPlace;
+ try {
+ applyInPlace = entry.cacheContentViews(mContext, notification.getNotification(),
+ mNotificationData.isAmbient(key));
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Unable to get notification remote views", e);
+ applyInPlace = false;
+ }
+ boolean shouldPeek = shouldPeek(entry, notification);
+ boolean alertAgain = alertAgain(entry, n);
+ if (DEBUG) {
+ Log.d(TAG, "applyInPlace=" + applyInPlace
+ + " shouldPeek=" + shouldPeek
+ + " alertAgain=" + alertAgain);
+ }
+
+ final StatusBarNotification oldNotification = entry.notification;
+ entry.notification = notification;
+ mGroupManager.onEntryUpdated(entry, oldNotification);
+
+ boolean updateSuccessful = false;
+ try {
+ if (applyInPlace) {
+ if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
+ try {
+ entry.updateIcons(mContext, n);
+ updateNotificationViews(entry, notification);
+ updateSuccessful = true;
+ } catch (RuntimeException e) {
+ // It failed to apply cleanly.
+ Log.w(TAG, "Couldn't reapply views for package " +
+ notification.getPackageName(), e);
+ }
+ }
+ if (!updateSuccessful) {
+ entry.updateIcons(mContext, n);
+ if (!inflateViews(entry, mStackScroller)) {
+ handleNotificationError(notification, "Couldn't update remote views for: "
+ + notification);
+ }
+ }
+ } catch (NotificationData.IconException e) {
+ handleNotificationError(notification, e.getMessage());
+ }
+ updateHeadsUp(key, entry, shouldPeek, alertAgain);
+ updateNotifications();
+
+ if (!notification.isClearable()) {
+ // The user may have performed a dismiss action on the notification, since it's
+ // not clearable we should snap it back.
+ mStackScroller.snapViewIfNeeded(entry.row);
+ }
+
+ if (DEBUG) {
+ // Is this for you?
+ boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);
+ Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
+ }
+
+ setAreThereNotifications();
+ }
+
+ private void updateNotificationViews(Entry entry, StatusBarNotification sbn) {
+ final RemoteViews contentView = entry.cachedContentView;
+ final RemoteViews bigContentView = entry.cachedBigContentView;
+ final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView;
+ final RemoteViews publicContentView = entry.cachedPublicContentView;
+
+ // Reapply the RemoteViews
+ contentView.reapply(mContext, entry.getContentView(), mOnClickHandler);
+ if (bigContentView != null && entry.getExpandedContentView() != null) {
+ bigContentView.reapply(sbn.getPackageContext(mContext),
+ entry.getExpandedContentView(),
+ mOnClickHandler);
+ }
+ View headsUpChild = entry.getHeadsUpContentView();
+ if (headsUpContentView != null && headsUpChild != null) {
+ headsUpContentView.reapply(sbn.getPackageContext(mContext),
+ headsUpChild, mOnClickHandler);
+ }
+ if (publicContentView != null && entry.getPublicContentView() != null) {
+ publicContentView.reapply(sbn.getPackageContext(mContext),
+ entry.getPublicContentView(), mOnClickHandler);
+ }
+ // update the contentIntent
+ mNotificationClicker.register(entry.row, sbn);
+
+ entry.row.onNotificationUpdated(entry);
+ entry.row.resetHeight();
+ }
+
+ protected void updatePublicContentView(Entry entry,
+ StatusBarNotification sbn) {
+ final RemoteViews publicContentView = entry.cachedPublicContentView;
+ View inflatedView = entry.getPublicContentView();
+ if (entry.autoRedacted && publicContentView != null && inflatedView != null) {
+ final boolean disabledByPolicy =
+ !adminAllowsUnredactedNotifications(entry.notification.getUserId());
+ String notificationHiddenText = mContext.getString(disabledByPolicy
+ ? com.android.internal.R.string.notification_hidden_by_policy_text
+ : com.android.internal.R.string.notification_hidden_text);
+ TextView titleView = (TextView) inflatedView.findViewById(android.R.id.title);
+ if (titleView != null
+ && !titleView.getText().toString().equals(notificationHiddenText)) {
+ publicContentView.setTextViewText(android.R.id.title, notificationHiddenText);
+ publicContentView.reapply(sbn.getPackageContext(mContext),
+ inflatedView, mOnClickHandler);
+ entry.row.onNotificationUpdated(entry);
+ }
+ }
+ }
+
+ protected void notifyHeadsUpScreenOff() {
+ maybeEscalateHeadsUp();
+ }
+
+ private boolean alertAgain(Entry oldEntry, Notification newNotification) {
+ return oldEntry == null || !oldEntry.hasInterrupted()
+ || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
+ }
+
+ protected boolean shouldPeek(Entry entry) {
+ return shouldPeek(entry, entry.notification);
+ }
+
+ protected boolean shouldPeek(Entry entry, StatusBarNotification sbn) {
+ if (!mUseHeadsUp || isDeviceInVrMode()) {
+ return false;
+ }
+
+ if (mNotificationData.shouldFilterOut(sbn)) {
+ if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey());
+ return false;
+ }
+
+ boolean inUse = mPowerManager.isScreenOn();
+ try {
+ inUse = inUse && !mDreamManager.isDreaming();
+ } catch (RemoteException e) {
+ Log.d(TAG, "failed to query dream manager", e);
+ }
+
+ if (!inUse && !isDozing()) {
+ if (DEBUG) {
+ Log.d(TAG, "No peeking: not in use: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (mNotificationData.shouldSuppressScreenOn(sbn.getKey())) {
+ if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
+ return false;
+ }
+
+ if (entry.hasJustLaunchedFullScreenIntent()) {
+ if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey());
+ return false;
+ }
+
+ if (isSnoozedPackage(sbn)) {
+ if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
+ return false;
+ }
+
+ if (mNotificationData.getImportance(sbn.getKey()) < NotificationManager.IMPORTANCE_HIGH) {
+ if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());
+ return false;
+ }
+
+ if (sbn.getNotification().fullScreenIntent != null) {
+ if (mAccessibilityManager.isTouchExplorationEnabled()) {
+ if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
+ return false;
+ } else {
+ // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent
+ return !mStatusBarKeyguardViewManager.isShowing()
+ || mStatusBarKeyguardViewManager.isOccluded();
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @return Whether the security bouncer from Keyguard is showing.
+ */
+ public boolean isBouncerShowing() {
+ return mBouncerShowing;
+ }
+
+ /**
+ * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then
+ * return PackageManager for mContext
+ */
+ public static PackageManager getPackageManagerForUser(Context context, int userId) {
+ Context contextForUser = context;
+ // UserHandle defines special userId as negative values, e.g. USER_ALL
+ if (userId >= 0) {
+ try {
+ // Create a context for the correct user so if a package isn't installed
+ // for user 0 we can still load information about the package.
+ contextForUser =
+ context.createPackageContextAsUser(context.getPackageName(),
+ Context.CONTEXT_RESTRICTED,
+ new UserHandle(userId));
+ } catch (NameNotFoundException e) {
+ // Shouldn't fail to find the package name for system ui.
+ }
+ }
+ return contextForUser.getPackageManager();
+ }
+
+ @Override
+ public void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
+ try {
+ mBarService.onNotificationExpansionChanged(key, userAction, expanded);
+ } catch (RemoteException e) {
+ // Ignore.
+ }
+ }
+
+ public boolean isKeyguardSecure() {
+ if (mStatusBarKeyguardViewManager == null) {
+ // startKeyguard() hasn't been called yet, so we don't know.
+ // Make sure anything that needs to know isKeyguardSecure() checks and re-checks this
+ // value onVisibilityChanged().
+ Slog.w(TAG, "isKeyguardSecure() called before startKeyguard(), returning false",
+ new Throwable());
+ return false;
+ }
+ return mStatusBarKeyguardViewManager.isSecure();
+ }
+
+ @Override
+ public void showAssistDisclosure() {
+ if (mAssistManager != null) {
+ mAssistManager.showDisclosure();
+ }
+ }
+
+ @Override
+ public void startAssist(Bundle args) {
+ if (mAssistManager != null) {
+ mAssistManager.startAssist(args);
+ }
+ }
+ // End Extra BaseStatusBarMethods.
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index a0425e6..46ca3c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.phone;
import android.animation.ArgbEvaluator;
-import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -25,8 +24,6 @@
import android.graphics.Rect;
import android.graphics.drawable.Icon;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -64,7 +61,7 @@
public static final int DEFAULT_ICON_TINT = Color.WHITE;
private Context mContext;
- private PhoneStatusBar mPhoneStatusBar;
+ private StatusBar mStatusBar;
private DemoStatusIcons mDemoStatusIcons;
private LinearLayout mSystemIconArea;
@@ -100,11 +97,11 @@
private final ArraySet<String> mIconBlacklist = new ArraySet<>();
public StatusBarIconController(Context context, View statusBar, View keyguardStatusBar,
- PhoneStatusBar phoneStatusBar) {
+ StatusBar phoneStatusBar) {
super(context.getResources().getStringArray(
com.android.internal.R.array.config_statusBarIcons));
mContext = context;
- mPhoneStatusBar = phoneStatusBar;
+ mStatusBar = phoneStatusBar;
mSystemIconArea = (LinearLayout) statusBar.findViewById(R.id.system_icon_area);
mStatusIcons = (LinearLayout) statusBar.findViewById(R.id.statusIcons);
mSignalCluster = (SignalClusterView) statusBar.findViewById(R.id.signal_cluster);
@@ -405,11 +402,11 @@
.withEndAction(null);
// Synchronize the motion with the Keyguard fading if necessary.
- if (mPhoneStatusBar.isKeyguardFadingAway()) {
+ if (mStatusBar.isKeyguardFadingAway()) {
v.animate()
- .setDuration(mPhoneStatusBar.getKeyguardFadingAwayDuration())
+ .setDuration(mStatusBar.getKeyguardFadingAwayDuration())
.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
- .setStartDelay(mPhoneStatusBar.getKeyguardFadingAwayDelay())
+ .setStartDelay(mStatusBar.getKeyguardFadingAwayDelay())
.start();
}
}
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 7e5a7da..8d5d890 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -70,7 +70,7 @@
protected LockPatternUtils mLockPatternUtils;
protected ViewMediatorCallback mViewMediatorCallback;
- protected PhoneStatusBar mPhoneStatusBar;
+ protected StatusBar mStatusBar;
private ScrimController mScrimController;
private FingerprintUnlockController mFingerprintUnlockController;
@@ -103,12 +103,12 @@
mLockPatternUtils = lockPatternUtils;
}
- public void registerStatusBar(PhoneStatusBar phoneStatusBar,
+ public void registerStatusBar(StatusBar statusBar,
ViewGroup container, StatusBarWindowManager statusBarWindowManager,
ScrimController scrimController,
FingerprintUnlockController fingerprintUnlockController,
DismissCallbackRegistry dismissCallbackRegistry) {
- mPhoneStatusBar = phoneStatusBar;
+ mStatusBar = statusBar;
mContainer = container;
mStatusBarWindowManager = statusBarWindowManager;
mScrimController = scrimController;
@@ -136,10 +136,10 @@
if (mBouncer.needsFullscreenBouncer()) {
// The keyguard might be showing (already). So we need to hide it.
- mPhoneStatusBar.hideKeyguard();
+ mStatusBar.hideKeyguard();
mBouncer.show(true /* resetSecuritySelection */);
} else {
- mPhoneStatusBar.showKeyguard();
+ mStatusBar.showKeyguard();
if (hideBouncerWhenShowing) {
mBouncer.hide(false /* destroyView */);
mBouncer.prepare();
@@ -180,8 +180,8 @@
public void reset(boolean hideBouncerWhenShowing) {
if (mShowing) {
if (mOccluded) {
- mPhoneStatusBar.hideKeyguard();
- mPhoneStatusBar.stopWaitingForKeyguardExit();
+ mStatusBar.hideKeyguard();
+ mStatusBar.stopWaitingForKeyguardExit();
mBouncer.hide(false /* destroyView */);
} else {
showBouncerOrKeyguard(hideBouncerWhenShowing);
@@ -192,12 +192,12 @@
}
public void onStartedGoingToSleep() {
- mPhoneStatusBar.onStartedGoingToSleep();
+ mStatusBar.onStartedGoingToSleep();
}
public void onFinishedGoingToSleep() {
mDeviceInteractive = false;
- mPhoneStatusBar.onFinishedGoingToSleep();
+ mStatusBar.onFinishedGoingToSleep();
mBouncer.onScreenTurnedOff();
}
@@ -205,13 +205,13 @@
Trace.beginSection("StatusBarKeyguardViewManager#onStartedWakingUp");
mDeviceInteractive = true;
mDeviceWillWakeUp = false;
- mPhoneStatusBar.onStartedWakingUp();
+ mStatusBar.onStartedWakingUp();
Trace.endSection();
}
public void onScreenTurningOn() {
Trace.beginSection("StatusBarKeyguardViewManager#onScreenTurningOn");
- mPhoneStatusBar.onScreenTurningOn();
+ mStatusBar.onScreenTurningOn();
Trace.endSection();
}
@@ -228,7 +228,7 @@
true /* skipFirstFrame */);
updateStates();
}
- mPhoneStatusBar.onScreenTurnedOn();
+ mStatusBar.onScreenTurnedOn();
Trace.endSection();
}
@@ -240,7 +240,7 @@
public void onScreenTurnedOff() {
mScreenTurnedOn = false;
- mPhoneStatusBar.onScreenTurnedOff();
+ mStatusBar.onScreenTurnedOff();
}
public void notifyDeviceWakeUpRequested() {
@@ -257,12 +257,12 @@
public void setOccluded(boolean occluded, boolean animate) {
if (occluded != mOccluded) {
- mPhoneStatusBar.onKeyguardOccludedChanged(occluded);
+ mStatusBar.onKeyguardOccludedChanged(occluded);
}
if (occluded && !mOccluded && mShowing) {
- if (mPhoneStatusBar.isInLaunchTransition()) {
+ if (mStatusBar.isInLaunchTransition()) {
mOccluded = true;
- mPhoneStatusBar.fadeKeyguardAfterLaunchTransition(null /* beforeFading */,
+ mStatusBar.fadeKeyguardAfterLaunchTransition(null /* beforeFading */,
new Runnable() {
@Override
public void run() {
@@ -275,7 +275,7 @@
}
mOccluded = occluded;
if (mShowing) {
- mPhoneStatusBar.updateMediaMetaData(false, animate && !occluded);
+ mStatusBar.updateMediaMetaData(false, animate && !occluded);
}
mStatusBarWindowManager.setKeyguardOccluded(occluded);
@@ -283,7 +283,7 @@
// a FLAG_DISMISS_KEYGUARD_ACTIVITY.
reset(false /* hideBouncerWhenShowing*/);
if (animate && !occluded && mShowing) {
- mPhoneStatusBar.animateKeyguardUnoccluding();
+ mStatusBar.animateKeyguardUnoccluding();
}
}
@@ -318,8 +318,8 @@
long uptimeMillis = SystemClock.uptimeMillis();
long delay = Math.max(0, startTime + HIDE_TIMING_CORRECTION_MS - uptimeMillis);
- if (mPhoneStatusBar.isInLaunchTransition() ) {
- mPhoneStatusBar.fadeKeyguardAfterLaunchTransition(new Runnable() {
+ if (mStatusBar.isInLaunchTransition() ) {
+ mStatusBar.fadeKeyguardAfterLaunchTransition(new Runnable() {
@Override
public void run() {
mStatusBarWindowManager.setKeyguardShowing(false);
@@ -327,14 +327,14 @@
mBouncer.hide(true /* destroyView */);
updateStates();
mScrimController.animateKeyguardFadingOut(
- PhoneStatusBar.FADE_KEYGUARD_START_DELAY,
- PhoneStatusBar.FADE_KEYGUARD_DURATION, null,
+ StatusBar.FADE_KEYGUARD_START_DELAY,
+ StatusBar.FADE_KEYGUARD_DURATION, null,
false /* skipFirstFrame */);
}
}, new Runnable() {
@Override
public void run() {
- mPhoneStatusBar.hideKeyguard();
+ mStatusBar.hideKeyguard();
mStatusBarWindowManager.setKeyguardFadingAway(false);
mViewMediatorCallback.keyguardGone();
executeAfterKeyguardGoneAction();
@@ -348,19 +348,19 @@
delay = 0;
fadeoutDuration = 240;
}
- mPhoneStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
+ mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
mFingerprintUnlockController.startKeyguardFadingAway();
mBouncer.hide(true /* destroyView */);
updateStates();
if (wakeUnlockPulsing) {
mStatusBarWindowManager.setKeyguardFadingAway(true);
- mPhoneStatusBar.fadeKeyguardWhilePulsing();
+ mStatusBar.fadeKeyguardWhilePulsing();
animateScrimControllerKeyguardFadingOut(delay, fadeoutDuration,
- mPhoneStatusBar::hideKeyguard, false /* skipFirstFrame */);
+ mStatusBar::hideKeyguard, false /* skipFirstFrame */);
} else {
mFingerprintUnlockController.startKeyguardFadingAway();
- mPhoneStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
- boolean staying = mPhoneStatusBar.hideKeyguard();
+ mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
+ boolean staying = mStatusBar.hideKeyguard();
if (!staying) {
mStatusBarWindowManager.setKeyguardFadingAway(true);
if (mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK) {
@@ -379,7 +379,7 @@
}
} else {
mScrimController.animateGoingToFullShade(delay, fadeoutDuration);
- mPhoneStatusBar.finishKeyguardFadingAway();
+ mStatusBar.finishKeyguardFadingAway();
}
}
mStatusBarWindowManager.setKeyguardShowing(false);
@@ -408,7 +408,7 @@
}
mContainer.postDelayed(() -> mStatusBarWindowManager.setKeyguardFadingAway(false),
100);
- mPhoneStatusBar.finishKeyguardFadingAway();
+ mStatusBar.finishKeyguardFadingAway();
mFingerprintUnlockController.finishKeyguardFadingAway();
WindowManagerGlobal.getInstance().trimMemory(
ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
@@ -438,7 +438,7 @@
* Dismisses the keyguard by going to the next screen or making it gone.
*/
public void dismissAndCollapse() {
- mPhoneStatusBar.executeRunnableDismissingKeyguard(null, null, true, false, true);
+ mStatusBar.executeRunnableDismissingKeyguard(null, null, true, false, true);
}
public void dismiss() {
@@ -466,7 +466,7 @@
*/
public boolean onBackPressed() {
if (mBouncer.isShowing()) {
- mPhoneStatusBar.endAffordanceLaunch();
+ mStatusBar.endAffordanceLaunch();
reset(true /* hideBouncerWhenShowing */);
return true;
}
@@ -478,8 +478,8 @@
}
private long getNavBarShowDelay() {
- if (mPhoneStatusBar.isKeyguardFadingAway()) {
- return mPhoneStatusBar.getKeyguardFadingAwayDelay();
+ if (mStatusBar.isKeyguardFadingAway()) {
+ return mStatusBar.getKeyguardFadingAwayDelay();
} else {
// Keyguard is not going away, thus we are showing the navigation bar because the
@@ -491,7 +491,7 @@
private Runnable mMakeNavigationBarVisibleRunnable = new Runnable() {
@Override
public void run() {
- mPhoneStatusBar.getNavigationBarView().getRootView().setVisibility(View.VISIBLE);
+ mStatusBar.getNavigationBarView().getRootView().setVisibility(View.VISIBLE);
}
};
@@ -516,7 +516,7 @@
boolean navBarVisible = isNavBarVisible();
boolean lastNavBarVisible = getLastNavBarVisible();
if (navBarVisible != lastNavBarVisible || mFirstUpdate) {
- if (mPhoneStatusBar.getNavigationBarView() != null) {
+ if (mStatusBar.getNavigationBarView() != null) {
if (navBarVisible) {
long delay = getNavBarShowDelay();
if (delay == 0) {
@@ -527,14 +527,14 @@
}
} else {
mContainer.removeCallbacks(mMakeNavigationBarVisibleRunnable);
- mPhoneStatusBar.getNavigationBarView().getRootView().setVisibility(View.GONE);
+ mStatusBar.getNavigationBarView().getRootView().setVisibility(View.GONE);
}
}
}
if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
mStatusBarWindowManager.setBouncerShowing(bouncerShowing);
- mPhoneStatusBar.setBouncerShowing(bouncerShowing);
+ mStatusBar.setBouncerShowing(bouncerShowing);
mScrimController.setBouncerShowing(bouncerShowing);
}
@@ -553,7 +553,7 @@
mLastBouncerDismissible = bouncerDismissible;
mLastRemoteInputActive = remoteInputActive;
- mPhoneStatusBar.onKeyguardViewManagerStatesUpdated();
+ mStatusBar.onKeyguardViewManagerStatesUpdated();
}
/**
@@ -583,11 +583,11 @@
}
public boolean shouldDisableWindowAnimationsForUnlock() {
- return mPhoneStatusBar.isInLaunchTransition();
+ return mStatusBar.isInLaunchTransition();
}
public boolean isGoingToNotificationShade() {
- return mPhoneStatusBar.isGoingToNotificationShade();
+ return mStatusBar.isGoingToNotificationShade();
}
public boolean isSecure(int userId) {
@@ -595,11 +595,11 @@
}
public void keyguardGoingAway() {
- mPhoneStatusBar.keyguardGoingAway();
+ mStatusBar.keyguardGoingAway();
}
public void animateCollapsePanels(float speedUpFactor) {
- mPhoneStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */,
+ mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */,
false /* delayed */, speedUpFactor);
}
@@ -616,6 +616,6 @@
}
public ViewRootImpl getViewRootImpl() {
- return mPhoneStatusBar.getStatusBarView().getViewRootImpl();
+ return mStatusBar.getStatusBarView().getViewRootImpl();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index 0660054..16999b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -25,7 +25,6 @@
import android.os.Binder;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.os.Trace;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
@@ -34,7 +33,6 @@
import com.android.keyguard.R;
import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
@@ -140,7 +138,7 @@
private void applyFocusableFlag(State state) {
boolean panelFocusable = state.statusBarFocusable && state.panelExpanded;
if (state.bouncerShowing && (state.keyguardOccluded || state.keyguardNeedsInput)
- || BaseStatusBar.ENABLE_REMOTE_INPUT && state.remoteInputActive) {
+ || StatusBar.ENABLE_REMOTE_INPUT && state.remoteInputActive) {
mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
} else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) {
@@ -385,7 +383,7 @@
boolean backdropShowing;
/**
- * The {@link BaseStatusBar} state from the status bar.
+ * The {@link StatusBar} state from the status bar.
*/
int statusBarState;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index ccd6357..7c42d00 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -34,7 +34,6 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.Trace;
import android.util.AttributeSet;
import android.view.ActionMode;
import android.view.InputQueue;
@@ -56,7 +55,6 @@
import com.android.internal.widget.FloatingToolbar;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -64,7 +62,7 @@
public class StatusBarWindowView extends FrameLayout {
public static final String TAG = "StatusBarWindowView";
- public static final boolean DEBUG = BaseStatusBar.DEBUG;
+ public static final boolean DEBUG = StatusBar.DEBUG;
private DragDownHelper mDragDownHelper;
private NotificationStackScrollLayout mStackScrollLayout;
@@ -74,7 +72,7 @@
private int mRightInset = 0;
private int mLeftInset = 0;
- private PhoneStatusBar mService;
+ private StatusBar mService;
private final Paint mTransparentSrcPaint = new Paint();
private FalsingManager mFalsingManager;
@@ -173,7 +171,7 @@
}
}
- public void setService(PhoneStatusBar service) {
+ public void setService(StatusBar service) {
mService = service;
mDragDownHelper = new DragDownHelper(getContext(), this, mStackScrollLayout, mService);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
index e7056a6..5ab99e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
@@ -125,14 +125,14 @@
public void setMobileDataIndicators(final IconState statusIcon, final IconState qsIcon,
final int statusType, final int qsType,final boolean activityIn,
final boolean activityOut, final String typeContentDescription,
- final String description, final boolean isWide, final int subId) {
+ final String description, final boolean isWide, final int subId, boolean roaming) {
post(new Runnable() {
@Override
public void run() {
for (SignalCallback signalCluster : mSignalCallbacks) {
signalCluster.setMobileDataIndicators(statusIcon, qsIcon, statusType, qsType,
activityIn, activityOut, typeContentDescription, description, isWide,
- subId);
+ subId, roaming);
}
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index a8d8f60..8c805fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -37,7 +37,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.phone.StatusBar;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -90,7 +90,7 @@
private final int mStatusBarHeight;
private final Context mContext;
private final NotificationGroupManager mGroupManager;
- private PhoneStatusBar mBar;
+ private StatusBar mBar;
private int mSnoozeLengthMs;
private ContentObserver mSettingsObserver;
private HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>();
@@ -162,7 +162,7 @@
mIsObserving = shouldObserve;
}
- public void setBar(PhoneStatusBar bar) {
+ public void setBar(StatusBar bar) {
mBar = bar;
}
@@ -170,7 +170,7 @@
mListeners.add(listener);
}
- public PhoneStatusBar getBar() {
+ public StatusBar getBar() {
return mBar;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 83463e2..03c46e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -226,10 +226,8 @@
final boolean dataDisabled = mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED
&& mCurrentState.userSetup;
- // Show icon in QS when we are connected or need to show roaming or data is disabled.
- boolean showDataIcon = mCurrentState.dataConnected
- || mCurrentState.iconGroup == TelephonyIcons.ROAMING
- || dataDisabled;
+ // Show icon in QS when we are connected or data is disabled.
+ boolean showDataIcon = mCurrentState.dataConnected || dataDisabled;
IconState statusIcon = new IconState(mCurrentState.enabled && !mCurrentState.airplaneMode,
getCurrentIconId(), contentDescription);
@@ -249,13 +247,11 @@
boolean activityOut = mCurrentState.dataConnected
&& !mCurrentState.carrierNetworkChangeMode
&& mCurrentState.activityOut;
- showDataIcon &= mCurrentState.isDefault
- || mCurrentState.iconGroup == TelephonyIcons.ROAMING
- || dataDisabled;
+ showDataIcon &= mCurrentState.isDefault || dataDisabled;
int typeIcon = showDataIcon ? icons.mDataType : 0;
callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
activityIn, activityOut, dataContentDescription, description, icons.mIsWide,
- mSubscriptionInfo.getSubscriptionId());
+ mSubscriptionInfo.getSubscriptionId(), mCurrentState.roaming);
}
@Override
@@ -405,10 +401,9 @@
mCurrentState.dataConnected = mCurrentState.connected
&& mDataState == TelephonyManager.DATA_CONNECTED;
+ mCurrentState.roaming = isRoaming();
if (isCarrierNetworkChangeActive()) {
mCurrentState.iconGroup = TelephonyIcons.CARRIER_NETWORK_CHANGE;
- } else if (isRoaming()) {
- mCurrentState.iconGroup = TelephonyIcons.ROAMING;
} else if (isDataDisabled()) {
mCurrentState.iconGroup = TelephonyIcons.DATA_DISABLED;
}
@@ -541,6 +536,7 @@
boolean carrierNetworkChangeMode;
boolean isDefault;
boolean userSetup;
+ boolean roaming;
@Override
public void copyFrom(State s) {
@@ -555,6 +551,7 @@
airplaneMode = state.airplaneMode;
carrierNetworkChangeMode = state.carrierNetworkChangeMode;
userSetup = state.userSetup;
+ roaming = state.roaming;
}
@Override
@@ -565,6 +562,7 @@
builder.append("networkName=").append(networkName).append(',');
builder.append("networkNameData=").append(networkNameData).append(',');
builder.append("dataConnected=").append(dataConnected).append(',');
+ builder.append("roaming=").append(roaming).append(',');
builder.append("isDefault=").append(isDefault).append(',');
builder.append("isEmergency=").append(isEmergency).append(',');
builder.append("airplaneMode=").append(airplaneMode).append(',');
@@ -584,7 +582,8 @@
&& ((MobileState) o).airplaneMode == airplaneMode
&& ((MobileState) o).carrierNetworkChangeMode == carrierNetworkChangeMode
&& ((MobileState) o).userSetup == userSetup
- && ((MobileState) o).isDefault == isDefault;
+ && ((MobileState) o).isDefault == isDefault
+ && ((MobileState) o).roaming == roaming;
}
}
}
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 a3a9d71..eb47a3c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -53,7 +53,7 @@
default void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
- String description, boolean isWide, int subId) {}
+ String description, boolean isWide, int subId, boolean roaming) {}
default void setSubs(List<SubscriptionInfo> subs) {}
default void setNoSims(boolean show) {}
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 5e13f59..d7c919d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -822,10 +822,12 @@
datatype.equals("h") ? TelephonyIcons.H :
datatype.equals("lte") ? TelephonyIcons.LTE :
datatype.equals("lte+") ? TelephonyIcons.LTE_PLUS :
- datatype.equals("roam") ? TelephonyIcons.ROAMING :
datatype.equals("dis") ? TelephonyIcons.DATA_DISABLED :
TelephonyIcons.UNKNOWN;
}
+ if (args.containsKey("roam")) {
+ controller.getState().roaming = "show".equals(args.getString("roam"));
+ }
int[][] icons = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH;
String level = args.getString("level");
if (level != null) {
@@ -833,6 +835,10 @@
: Math.min(Integer.parseInt(level), icons[0].length - 1);
controller.getState().connected = controller.getState().level >= 0;
}
+ String activity = args.getString("activity");
+ if (activity != null) {
+ controller.setActivity(Integer.parseInt(activity));
+ }
controller.getState().enabled = show;
controller.notifyListeners();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalCallbackAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalCallbackAdapter.java
deleted file mode 100644
index dce889f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalCallbackAdapter.java
+++ /dev/null
@@ -1,63 +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 com.android.systemui.statusbar.policy;
-
-import android.telephony.SubscriptionInfo;
-
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
-
-import java.util.List;
-
-
-/**
- * Provides empty implementations of SignalCallback for those that only want some of
- * the callbacks.
- */
-public class SignalCallbackAdapter implements SignalCallback {
-
- @Override
- public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
- boolean activityIn, boolean activityOut, String description) {
- }
-
- @Override
- public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
- int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
- String description, boolean isWide, int subId) {
- }
-
- @Override
- public void setSubs(List<SubscriptionInfo> subs) {
- }
-
- @Override
- public void setNoSims(boolean show) {
- }
-
- @Override
- public void setEthernetIndicators(IconState icon) {
- }
-
- @Override
- public void setIsAirplaneMode(IconState icon) {
- }
-
- @Override
- public void setMobileDataEnabled(boolean enabled) {
- }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
index ed8c7ff..6b2361e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
@@ -95,8 +95,6 @@
R.drawable.ic_qs_signal_carrier_network_change_animation }
};
- static final int QS_DATA_R = R.drawable.ic_qs_signal_r;
-
//***** Data connection icons
//GSM/UMTS
@@ -211,7 +209,7 @@
static final int QS_DATA_LTE_PLUS = R.drawable.ic_qs_signal_lte_plus;
static final int FLIGHT_MODE_ICON = R.drawable.stat_sys_airplane_mode;
- static final int ROAMING_ICON = R.drawable.stat_sys_data_fully_connected_roam;
+ static final int ROAMING_ICON = R.drawable.stat_sys_roaming;
static final int ICON_LTE = R.drawable.stat_sys_data_fully_connected_lte;
static final int ICON_LTE_PLUS = R.drawable.stat_sys_data_fully_connected_lte_plus;
static final int ICON_G = R.drawable.stat_sys_data_fully_connected_g;
@@ -410,21 +408,6 @@
TelephonyIcons.QS_DATA_LTE_PLUS
);
- static final MobileIconGroup ROAMING = new MobileIconGroup(
- "Roaming",
- TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING,
- TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
- TelephonyIcons.TELEPHONY_NO_NETWORK,
- TelephonyIcons.QS_TELEPHONY_NO_NETWORK,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
- R.string.accessibility_data_connection_roaming,
- TelephonyIcons.ROAMING_ICON,
- false,
- TelephonyIcons.QS_DATA_R
- );
-
static final MobileIconGroup DATA_DISABLED = new MobileIconGroup(
"DataDisabled",
TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 42c20ff..886b8be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -56,7 +56,7 @@
private final WifiNetworkScoreCache mScoreCache;
private final WifiStatusTracker mWifiTracker;
- private boolean mScoringEnabled = false;
+ private boolean mScoringUiEnabled = false;
public WifiSignalController(Context context, boolean hasMobileData,
CallbackHandler callbackHandler, NetworkControllerImpl networkController,
@@ -95,25 +95,26 @@
// Setup scoring
mNetworkScoreManager = networkScoreManager;
+ configureScoringGating();
+ registerScoreCache();
+ }
+
+ private void configureScoringGating() {
ContentObserver observer = new ContentObserver(new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean selfChange) {
- mScoringEnabled =
+ mScoringUiEnabled =
Settings.Global.getInt(
mContext.getContentResolver(),
- Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 0) == 1;
- if (!mScoringEnabled) {
- mScoreCache.clearScores();
- }
+ Settings.Global.NETWORK_SCORING_UI_ENABLED, 0) == 1;
}
};
- ContentResolver cr = mContext.getContentResolver();
- cr.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED),
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.NETWORK_SCORING_UI_ENABLED),
false /* notifyForDescendants */,
observer);
- observer.onChange(false /* selfChange */);
- registerScoreCache();
+
+ observer.onChange(false /* selfChange */); // Set the initial values
}
private void registerScoreCache() {
@@ -199,7 +200,7 @@
* <p>{@link #updateScoreCacheIfNecessary} should be called prior to this method.
*/
private int getWifiBadgeEnum() {
- if (!mScoringEnabled || mWifiTracker.networkKey == null) {
+ if (!mScoringUiEnabled || mWifiTracker.networkKey == null) {
return ScoredNetwork.BADGING_NONE;
}
ScoredNetwork score = mScoreCache.getScoredNetwork(mWifiTracker.networkKey);
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 5d48df9..85b1c32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -79,7 +79,7 @@
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ScrollAdapter;
@@ -251,7 +251,7 @@
return true;
}
};
- private PhoneStatusBar mPhoneStatusBar;
+ private StatusBar mStatusBar;
private int[] mTempInt2 = new int[2];
private boolean mGenerateChildOrderChangedEvent;
private HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>();
@@ -877,7 +877,7 @@
mFalsingManager.onNotificationDismissed();
if (mFalsingManager.shouldEnforceBouncer()) {
- mPhoneStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
+ mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
}
}
@@ -968,7 +968,7 @@
@Override
public float getFalsingThresholdFactor() {
- return mPhoneStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
+ return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
}
@Override
@@ -3222,7 +3222,7 @@
mAmbientState.setExpansionChanging(false);
if (!mIsExpanded) {
setOwnScrollY(0);
- mPhoneStatusBar.resetUserExpandedStates();
+ mStatusBar.resetUserExpandedStates();
// lets make sure nothing is in the overlay / transient anymore
clearTemporaryViews(this);
@@ -3757,8 +3757,8 @@
return max + getStackTranslation();
}
- public void setPhoneStatusBar(PhoneStatusBar phoneStatusBar) {
- this.mPhoneStatusBar = phoneStatusBar;
+ public void setStatusBar(StatusBar statusBar) {
+ this.mStatusBar = statusBar;
}
public void setGroupManager(NotificationGroupManager groupManager) {
@@ -3829,7 +3829,7 @@
@Override
public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
- mPhoneStatusBar.requestNotificationUpdate();
+ mStatusBar.requestNotificationUpdate();
}
/** @hide */
@@ -3897,7 +3897,7 @@
@Override
public void onGroupsChanged() {
- mPhoneStatusBar.requestNotificationUpdate();
+ mStatusBar.requestNotificationUpdate();
}
public void generateChildOrderChangedEvent() {
@@ -4367,7 +4367,7 @@
}
public void closeControlsIfOutsideTouch(MotionEvent ev) {
- NotificationGuts guts = mPhoneStatusBar.getExposedGuts();
+ NotificationGuts guts = mStatusBar.getExposedGuts();
View view = null;
int height = 0;
if (guts != null) {
@@ -4390,7 +4390,7 @@
Rect rect = new Rect(x, y, x + view.getWidth(), y + height);
if (!rect.contains(rx, ry)) {
// Touch was outside visible guts / gear notification, close what's visible
- mPhoneStatusBar.dismissPopups(-1, -1, true /* resetGear */, true /* animate */);
+ mStatusBar.dismissPopups(-1, -1, true /* resetGear */, true /* animate */);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 9b8183b..ab562d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -17,23 +17,29 @@
package com.android.systemui.statusbar.tv;
import android.content.ComponentName;
+import android.content.Context;
import android.graphics.Rect;
import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.service.notification.NotificationListenerService.RankingMap;
-import android.service.notification.StatusBarNotification;
-import android.view.View;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.systemui.statusbar.ActivatableNotificationView;
-import com.android.systemui.statusbar.BaseStatusBar;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.SystemUI;
import com.android.systemui.pip.tv.PipManager;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.CommandQueue.Callbacks;
+
+import java.util.ArrayList;
/**
* Status bar implementation for "large screen" products that mostly present no on-screen nav
*/
-public class TvStatusBar extends BaseStatusBar {
+public class TvStatusBar extends SystemUI implements Callbacks {
+
+ private IStatusBarService mBarService;
@Override
public void setIcon(String slot, StatusBarIcon icon) {
@@ -43,16 +49,6 @@
public void removeIcon(String slot) {
}
- @Override
- public void addNotification(StatusBarNotification notification, RankingMap ranking,
- NotificationData.Entry entry) {
- }
-
- @Override
- protected void updateNotificationRanking(RankingMap ranking) {
- }
-
- @Override
public void removeNotification(String key, RankingMap ranking) {
}
@@ -99,53 +95,10 @@
}
@Override
- protected void setAreThereNotifications() {
- }
-
- @Override
- protected void updateNotifications() {
- }
-
- public View getStatusBarView() {
- return null;
- }
-
- @Override
- protected boolean toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction) {
- return false;
- }
-
- @Override
- public void maybeEscalateHeadsUp() {
- }
-
- @Override
- public boolean isPanelFullyCollapsed() {
- return false;
- }
-
- @Override
- protected int getMaxKeyguardNotifications(boolean recompute) {
- return 0;
- }
-
- @Override
public void animateExpandSettingsPanel(String subPanel) {
}
@Override
- protected void createAndAddWindows() {
- }
-
- @Override
- public void onActivated(ActivatableNotificationView view) {
- }
-
- @Override
- public void onActivationReset(ActivatableNotificationView view) {
- }
-
- @Override
public void showScreenPinningRequest(int taskId) {
}
@@ -175,19 +128,6 @@
}
@Override
- protected void updateHeadsUp(String key, NotificationData.Entry entry, boolean shouldPeek,
- boolean alertAgain) {
- }
-
- @Override
- protected void setHeadsUpUser(int newUserId) {
- }
-
- protected boolean isSnoozedPackage(StatusBarNotification sbn) {
- return false;
- }
-
- @Override
public void addQsTile(ComponentName tile) {
}
@@ -201,8 +141,23 @@
@Override
public void start() {
- super.start();
putComponent(TvStatusBar.class, this);
+ CommandQueue commandQueue = getComponent(CommandQueue.class);
+ commandQueue.addCallbacks(this);
+ int[] switches = new int[9];
+ ArrayList<IBinder> binders = new ArrayList<>();
+ ArrayList<String> iconSlots = new ArrayList<>();
+ ArrayList<StatusBarIcon> icons = new ArrayList<>();
+ Rect fullscreenStackBounds = new Rect();
+ Rect dockedStackBounds = new Rect();
+ mBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ try {
+ mBarService.registerStatusBar(commandQueue, iconSlots, icons, switches, binders,
+ fullscreenStackBounds, dockedStackBounds);
+ } catch (RemoteException ex) {
+ // If the system process isn't there we're doomed anyway.
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index 137a12f..78145fe 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -31,7 +31,6 @@
import com.android.systemui.SystemUIFactory;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.qs.tiles.DndTile;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.tuner.TunerService;
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index 90e9321..408e8f3 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -33,6 +33,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
<uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
+ <uses-permission android:name="android.permission.REQUEST_NETWORK_SCORES" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
index 525a361..e28d077 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
@@ -20,7 +20,6 @@
import android.view.WindowManager;
import com.android.systemui.FragmentTestCase;
-import com.android.systemui.assist.AssistManager;
import com.android.systemui.recents.Recents;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
@@ -37,7 +36,7 @@
@Before
public void setup() {
mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
- mContext.putComponent(PhoneStatusBar.class, mock(PhoneStatusBar.class));
+ mContext.putComponent(StatusBar.class, mock(StatusBar.class));
mContext.putComponent(Recents.class, mock(Recents.class));
mContext.putComponent(Divider.class, mock(Divider.class));
mContext.addMockSystemService(Context.WINDOW_SERVICE, mock(WindowManager.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
similarity index 80%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index d82566f..309559b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -27,7 +27,6 @@
import com.android.keyguard.KeyguardHostView.OnDismissAction;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.BaseStatusBar;
import org.junit.Before;
import org.junit.Test;
@@ -35,15 +34,15 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class PhoneStatusBarTest extends SysuiTestCase {
+public class StatusBarTest extends SysuiTestCase {
StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- PhoneStatusBar mPhoneStatusBar;
+ StatusBar mStatusBar;
@Before
public void setup() {
mStatusBarKeyguardViewManager = mock(StatusBarKeyguardViewManager.class);
- mPhoneStatusBar = new TestablePhoneStatusBar(mStatusBarKeyguardViewManager);
+ mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager);
doAnswer(invocation -> {
OnDismissAction onDismissAction = (OnDismissAction) invocation.getArguments()[0];
@@ -63,7 +62,7 @@
when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(true);
- mPhoneStatusBar.executeRunnableDismissingKeyguard(null, null, false, false, false);
+ mStatusBar.executeRunnableDismissingKeyguard(null, null, false, false, false);
}
@Test
@@ -71,7 +70,7 @@
when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
- mPhoneStatusBar.executeRunnableDismissingKeyguard(null, null, false, false, false);
+ mStatusBar.executeRunnableDismissingKeyguard(null, null, false, false, false);
}
@Test
@@ -79,16 +78,16 @@
when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
- mPhoneStatusBar.executeRunnableDismissingKeyguard(null, null, false, false, false);
+ mStatusBar.executeRunnableDismissingKeyguard(null, null, false, false, false);
}
- static class TestablePhoneStatusBar extends PhoneStatusBar {
- public TestablePhoneStatusBar(StatusBarKeyguardViewManager man) {
+ static class TestableStatusBar extends StatusBar {
+ public TestableStatusBar(StatusBarKeyguardViewManager man) {
mStatusBarKeyguardViewManager = man;
}
@Override
- protected BaseStatusBar.H createHandler() {
+ protected H createHandler() {
return null;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
index c969cc2..b544d9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
@@ -36,6 +36,8 @@
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.assertEquals;
+import static org.mockito.Matchers.eq;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class CallbackHandlerTest {
@@ -109,8 +111,9 @@
int qsType = R.drawable.ic_qs_signal_1x;
boolean wide = true;
int subId = 5;
+ boolean roaming = true;
mHandler.setMobileDataIndicators(status, qs, type, qsType, in, out, typeDescription,
- description, wide, subId);
+ description, wide, subId, roaming);
waitForCallbacks();
ArgumentCaptor<IconState> statusArg = ArgumentCaptor.forClass(IconState.class);
@@ -126,7 +129,7 @@
Mockito.verify(mSignalCallback).setMobileDataIndicators(statusArg.capture(),
qsArg.capture(), typeIconArg.capture(), qsTypeIconArg.capture(), inArg.capture(),
outArg.capture(), typeContentArg.capture(), descArg.capture(), wideArg.capture(),
- subIdArg.capture());
+ subIdArg.capture(), eq(roaming));
assertEquals(status, statusArg.getValue());
assertEquals(qs, qsArg.getValue());
assertEquals(type, (int) typeIconArg.getValue());
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 6aa021e..0e5f513 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
@@ -55,6 +55,7 @@
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -297,7 +298,7 @@
iconArg.capture(),
anyInt(),
typeIconArg.capture(), dataInArg.capture(), dataOutArg.capture(),
- anyString(), anyString(), anyBoolean(), anyInt());
+ anyString(), anyString(), anyBoolean(), anyInt(), anyBoolean());
IconState iconState = iconArg.getValue();
assertEquals("Visibility in, quick settings", visible, iconState.visible);
assertEquals("Signal icon in, quick settings", icon, iconState.icon);
@@ -309,6 +310,11 @@
}
protected void verifyLastMobileDataIndicators(boolean visible, int icon, int typeIcon) {
+ verifyLastMobileDataIndicators(visible, icon, typeIcon, false);
+ }
+
+ protected void verifyLastMobileDataIndicators(boolean visible, int icon, int typeIcon,
+ boolean roaming) {
ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class);
ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class);
@@ -318,9 +324,10 @@
any(),
typeIconArg.capture(),
anyInt(), anyBoolean(), anyBoolean(), anyString(), anyString(), anyBoolean(),
- anyInt());
+ anyInt(), eq(roaming));
IconState iconState = iconArg.getValue();
+ assertEquals("Signal icon in status bar", icon, iconState.icon);
assertEquals("Data icon in status bar", typeIcon, (int) typeIconArg.getValue());
assertEquals("Visibility in status bar", visible, iconState.visible);
}
@@ -341,7 +348,7 @@
qsTypeIconArg.capture(),
dataInArg.capture(),
dataOutArg.capture(),
- anyString(), anyString(), anyBoolean(), anyInt());
+ anyString(), anyString(), anyBoolean(), anyInt(), anyBoolean());
IconState iconState = iconArg.getValue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 1ec0418..d7f961c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -25,19 +25,6 @@
}
@Test
- public void testRoamingDataIcon() {
- setupDefaultSignal();
- setGsmRoaming(true);
-
- verifyLastMobileDataIndicators(true,
- TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[1][DEFAULT_LEVEL],
- TelephonyIcons.ROAMING_ICON,
- true,
- TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH[1][DEFAULT_LEVEL],
- TelephonyIcons.QS_DATA_R, false, false);
- }
-
- @Test
public void test2gDataIcon() {
setupDefaultSignal();
updateDataConnectionState(TelephonyManager.DATA_CONNECTED,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index 00e5926..2c0f9c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -158,13 +158,12 @@
for (int testStrength = SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
testStrength <= SignalStrength.SIGNAL_STRENGTH_GREAT; testStrength++) {
setupDefaultSignal();
- setConnectivity(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
setGsmRoaming(true);
setLevel(testStrength);
verifyLastMobileDataIndicators(true,
TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[1][testStrength],
- TelephonyIcons.ROAMING_ICON);
+ DEFAULT_ICON, true);
}
}
@@ -179,7 +178,7 @@
verifyLastMobileDataIndicators(true,
TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[1][testStrength],
- TelephonyIcons.ROAMING_ICON);
+ TelephonyIcons.DATA_1X[1][0 /* No direction */], true);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index 06a5122..9b382f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -1,7 +1,6 @@
package com.android.systemui.statusbar.policy;
import android.content.Intent;
-import android.graphics.drawable.Drawable;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkKey;
@@ -30,7 +29,6 @@
import org.mockito.stubbing.Answer;
import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
@@ -60,8 +58,6 @@
private final List<NetworkKey> mRequestedKeys = new ArrayList<>();
private CountDownLatch mRequestScoresLatch;
- private SettingOverrider mSettingsOverrider;
-
@Test
public void testWifiIcon() {
String testSsid = "Test SSID";
@@ -83,6 +79,7 @@
@Test
public void testBadgedWifiIcon() throws Exception {
+ // TODO(sghuman): Refactor this setup code when creating a test for the badged QsIcon.
int testLevel = 1;
RssiCurve mockBadgeCurve = mock(RssiCurve.class);
Bundle attr = new Bundle();
@@ -94,12 +91,16 @@
false /* meteredHint */,
attr);
- // Enable scoring
- mSettingsOverrider = mContext.getSettingsProvider().acquireOverridesBuilder(this)
- .addSetting("global", Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, "1")
- .build();
-
+ // Must set the Settings value before instantiating the NetworkControllerImpl due to bugs in
+ // FakeSettingsProvider.
+ SettingOverrider settingsOverrider =
+ mContext.getSettingsProvider().acquireOverridesBuilder(this)
+ .addSetting("global", Settings.Global.NETWORK_SCORING_UI_ENABLED, "1")
+ .build();
+ super.setUp(); // re-instantiate NetworkControllImpl now that setting has been updated
setupNetworkScoreManager();
+
+ // Test Requesting Scores
mRequestScoresLatch = new CountDownLatch(1);
setWifiEnabled(true);
setWifiState(true, TEST_SSID, TEST_BSSID);
@@ -115,23 +116,23 @@
Matchers.anyInt());
scoreCacheCaptor.getValue().updateScores(Arrays.asList(score));
+ // Test badge is set
setWifiLevel(testLevel);
- NetworkController.SignalCallback mockCallback =
- mock(NetworkController.SignalCallback.class);
- mNetworkController.addCallback(mockCallback);
- ArgumentCaptor<IconState> iconState = ArgumentCaptor.forClass(IconState.class);
- Mockito.verify(mockCallback).setWifiIndicators(
- anyBoolean(), iconState.capture(), any(), anyBoolean(), anyBoolean(), any());
+ ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class);
+ Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators(
+ anyBoolean(), iconArg.capture(), any(), anyBoolean(), anyBoolean(),
+ any());
+ IconState iconState = iconArg.getValue();
assertEquals("Badged Wifi Resource is set",
Utils.WIFI_PIE_FOR_BADGING[testLevel],
- iconState.getValue().icon);
+ iconState.icon);
assertEquals("SD Badge is set",
Utils.getWifiBadgeResource(ScoredNetwork.BADGING_SD),
- iconState.getValue().iconOverlay);
+ iconState.iconOverlay);
- mSettingsOverrider.release();
+ settingsOverrider.release();
}
private void setupNetworkScoreManager() {
@@ -215,9 +216,10 @@
setGsmRoaming(true);
// Still be on wifi though.
setConnectivity(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+ setConnectivity(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
verifyLastMobileDataIndicators(true,
TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[1][DEFAULT_LEVEL],
- TelephonyIcons.ROAMING_ICON);
+ 0, true);
}
protected void setWifiActivity(int activity) {
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 952f851..e0089c2 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -3346,6 +3346,11 @@
// OS: N
SUW_ACCESSIBILITY_TOGGLE_SELECT_TO_SPEAK = 817;
+ // OPEN: Settings > System > Backup
+ // CATEGORY: SETTINGS
+ // OS: O
+ BACKUP_SETTINGS = 818;
+
// ---- End O Constants, all O constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 3523706..ce50b30 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -42,6 +42,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
+import android.content.pm.LauncherApps;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -1547,6 +1548,13 @@
}
@Override
+ public boolean isRequestPinAppWidgetSupported() {
+ return LocalServices.getService(ShortcutServiceInternal.class)
+ .isRequestPinItemSupported(UserHandle.getCallingUserId(),
+ LauncherApps.PinItemRequest.REQUEST_TYPE_APPWIDGET);
+ }
+
+ @Override
public boolean requestPinAppWidget(String callingPackage, ComponentName componentName,
IntentSender resultSender) {
final int callingUid = Binder.getCallingUid();
diff --git a/services/autofill/java/com/android/server/autofill/AnchoredWindow.java b/services/autofill/java/com/android/server/autofill/AnchoredWindow.java
new file mode 100644
index 0000000..e674309
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/AnchoredWindow.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2017 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 com.android.server.autofill.Helper.DEBUG;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.Slog;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+import android.widget.FrameLayout;
+
+/**
+ * A window above the application that is smartly anchored to a rectangular region.
+ */
+final class AnchoredWindow {
+ private static final String TAG = "AutoFill";
+
+ private final WindowManager mWm;
+ private final View mRootView;
+ private final View mView;
+ private final int mWidth;
+ private final int mHeight;
+ private boolean mIsShowing = false;
+
+ /**
+ * Constructor.
+ *
+ * @param wm window manager that draws the view on a window
+ * @param view singleton view in the window
+ * @param width requested width of the view
+ * @param height requested height of the view
+ */
+ AnchoredWindow(WindowManager wm, View view, int width, int height) {
+ mWm = wm;
+ mRootView = wrapView(view, width, height);
+ mView = view;
+ mWidth = width;
+ mHeight = height;
+ }
+
+ /**
+ * Shows the window.
+ *
+ * @param bounds the rectangular region this window should be anchored to
+ */
+ void show(Rect bounds) {
+ LayoutParams params = createBaseLayoutParams();
+ params.x = bounds.left;
+ params.y = bounds.bottom;
+
+ if (!mIsShowing) {
+ if (DEBUG) Slog.d(TAG, "adding view " + mView);
+ mWm.addView(mRootView, params);
+ } else {
+ if (DEBUG) Slog.d(TAG, "updating view " + mView);
+ mWm.updateViewLayout(mRootView, params);
+ }
+ mIsShowing = true;
+ }
+
+ /**
+ * Hides the window.
+ */
+ void hide() {
+ if (DEBUG) Slog.d(TAG, "removing view " + mView);
+ if (mIsShowing) {
+ mWm.removeView(mRootView);
+ }
+ mIsShowing = false;
+ }
+
+ /**
+ * Wraps a view with a SelfRemovingView and sets its requested width and height.
+ */
+ private View wrapView(View view, int width, int height) {
+ ViewGroup viewGroup = new SelfRemovingView(view.getContext());
+ viewGroup.addView(view, new ViewGroup.LayoutParams(width, height));
+ return viewGroup;
+ }
+
+ private static LayoutParams createBaseLayoutParams() {
+ LayoutParams params = new LayoutParams();
+ // TODO(b/33197203): LayoutParams.TYPE_AUTOFILL
+ params.type = LayoutParams.TYPE_SYSTEM_ALERT;
+ params.flags =
+ LayoutParams.SOFT_INPUT_STATE_UNCHANGED
+ | LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ | LayoutParams.FLAG_NOT_FOCUSABLE
+ | LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+ params.gravity = Gravity.TOP | Gravity.LEFT;
+ params.width = LayoutParams.WRAP_CONTENT;
+ params.height = LayoutParams.WRAP_CONTENT;
+ return params;
+ }
+
+ /** FrameLayout that listens for touch events removes itself if the touch event is outside. */
+ private final class SelfRemovingView extends FrameLayout {
+ public SelfRemovingView(Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ hide();
+ return true;
+ } else {
+ return super.onTouchEvent(event);
+ }
+ }
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
index 47ac1ce..637656c 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
@@ -21,6 +21,7 @@
import static android.view.View.AUTO_FILL_FLAG_TYPE_FILL;
import android.Manifest;
+import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -31,7 +32,6 @@
import android.graphics.Rect;
import android.net.Uri;
import android.os.Binder;
-import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -39,16 +39,15 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.autofill.IAutoFillManagerService;
import android.text.TextUtils;
import android.text.format.DateUtils;
+import android.util.LocalLog;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.TimeUtils;
import android.view.autofill.AutoFillId;
import com.android.internal.annotations.GuardedBy;
@@ -56,10 +55,12 @@
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
import com.android.server.FgThread;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.List;
/**
* Entry point service for auto-fill management.
@@ -76,7 +77,8 @@
private static final long SERVICE_BINDING_LIFETIME_MS = 5 * DateUtils.MINUTE_IN_MILLIS;
protected static final int MSG_UNBIND = 1;
- protected static final int MSG_SHOW_AUTO_FILL = 2;
+ protected static final int MSG_REQUEST_AUTO_FILL_FOR_USER = 2;
+ protected static final int MSG_REQUEST_AUTO_FILL = 3;
private final AutoFillManagerServiceStub mServiceStub;
private final AutoFillUI mUi;
@@ -91,11 +93,23 @@
public void executeMessage(Message msg) {
switch (msg.what) {
case MSG_UNBIND: {
- removeStaleServiceForUser(msg.arg1);
+ synchronized (mLock) {
+ removeCachedServiceLocked(msg.arg1);
+ }
return;
- } case MSG_SHOW_AUTO_FILL: {
+ } case MSG_REQUEST_AUTO_FILL_FOR_USER: {
+ final int userId = msg.arg1;
+ final int flags = msg.arg2;
+ handleAutoFillForUser(userId, flags);
+ return;
+ } case MSG_REQUEST_AUTO_FILL: {
final SomeArgs args = (SomeArgs) msg.obj;
- showAutoFillInput(msg.arg1, (AutoFillId) args.arg1, (Rect) args.arg2);
+ final int userId = msg.arg1;
+ final int flags = msg.arg2;
+ final IBinder activityToken = (IBinder) args.arg1;
+ final AutoFillId autoFillId = (AutoFillId) args.arg2;
+ final Rect bounds = (Rect) args.arg3;
+ handleAutoFill(activityToken, userId, autoFillId, bounds, flags);
return;
} default: {
Slog.w(TAG, "Invalid message: " + msg);
@@ -123,6 +137,9 @@
@GuardedBy("mLock")
private SparseArray<AutoFillManagerServiceImpl> mServicesCache = new SparseArray<>();
+ // TODO(b/33197203): set a different max (or disable it) on low-memory devices.
+ private final LocalLog mRequestsHistory = new LocalLog(100);
+
public AutoFillManagerService(Context context) {
super(context);
@@ -166,11 +183,11 @@
if (DEBUG) Slog.d(TAG, "getServiceComponentForUser(" + userId + "): component="
+ serviceComponent + ", info: " + serviceInfo);
if (serviceInfo == null) {
- Slog.w(TAG, "no service info for " + serviceComponent);
+ if (DEBUG) Slog.d(TAG, "no service info for " + serviceComponent);
return null;
}
- return new AutoFillManagerServiceImpl(this, mUi, mContext, mLock, FgThread.getHandler(),
- userId, serviceInfo.applicationInfo.uid, serviceComponent,
+ return new AutoFillManagerServiceImpl(this, mUi, mContext, mLock, mRequestsHistory,
+ FgThread.getHandler(), userId, serviceInfo.applicationInfo.uid, serviceComponent,
SERVICE_BINDING_LIFETIME_MS);
}
@@ -198,87 +215,92 @@
}
// Keep service connection alive for a while, in case user needs to interact with it
// (for example, to save the data that was inputted in)
+ if (mHandlerCaller.hasMessages(MSG_UNBIND)) {
+ mHandlerCaller.removeMessages(MSG_UNBIND);
+ }
mHandlerCaller.sendMessageDelayed(mHandlerCaller.obtainMessageI(MSG_UNBIND, userId),
SERVICE_BINDING_LIFETIME_MS);
return service;
}
/**
- * Removes a cached service, but respecting its TTL.
+ * Removes a cached service for a given user.
*/
- private void removeStaleServiceForUser(int userId) {
- synchronized (mLock) {
- removeCachedService(userId, false);
- }
- }
-
- /**
- * Removes a cached service, even if it has TTL.
- */
- void removeCachedServiceForUserLocked(int userId) {
- removeCachedService(userId, true);
- }
-
- private void removeCachedService(int userId, boolean force) {
+ void removeCachedServiceLocked(int userId) {
if (DEBUG) Log.d(TAG, "removing cached service for userId " + userId);
final AutoFillManagerServiceImpl service = mServicesCache.get(userId);
if (service == null) {
- Log.w(TAG, "removeCachedServiceForUser(): no cached service for userId " + userId);
- return;
- }
- if (!force) {
- // Check TTL first.
- final long now = SystemClock.uptimeMillis();
- if (service.mEstimateTimeOfDeath > now) {
- if (DEBUG) {
- final StringBuilder msg = new StringBuilder("service has some TTL left: ");
- TimeUtils.formatDuration(service.mEstimateTimeOfDeath - now, msg);
- Log.d(TAG, msg.toString());
- }
- return;
+ if (DEBUG) {
+ Log.d(TAG, "removeCachedServiceForUser(): no cached service for userId " + userId);
}
+ return;
}
mServicesCache.delete(userId);
service.stopLocked();
-
}
-
- private void requestAutoFillLocked(IBinder activityToken, int userId, Bundle extras,
- int flags) {
- final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
- if (service != null) {
- service.requestAutoFill(activityToken, extras, flags);
+ private void handleAutoFill(IBinder activityToken, int userId, AutoFillId autoFillId,
+ Rect bounds, int flags) {
+ synchronized (mLock) {
+ final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
+ if (service != null) {
+ // TODO(b/33197203): must pass AUTO_FILL_FLAG_TYPE_FILL because AM is expecting
+ // either that flag or AUTO_FILL_FLAG_TYPE_SAVE; should go away once save is
+ // refactored
+ flags |= AUTO_FILL_FLAG_TYPE_FILL;
+ service.requestAutoFillLocked(activityToken, autoFillId, bounds, flags);
+ }
}
}
- private void showAutoFillInput(int userId, AutoFillId id, Rect rect) {
- if (DEBUG) Slog.d(TAG, "handler.showAutoFillInput(): id=" + id + ", rect=" + rect);
-
+ private void handleAutoFillForUser(int userId, int flags) {
+ if (DEBUG) {
+ Slog.d(TAG, "handler.requestAutoFillForUser(): id=" + userId + ", flags=" + flags);
+ }
+ 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;
+ }
+ final IBinder activityToken = topActivities.get(0);
synchronized (mLock) {
- requestAutoFillLocked(null, userId, null, AUTO_FILL_FLAG_TYPE_FILL);
+ final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
+ if (service == null) {
+ Slog.w(TAG, "no service for user " + userId);
+ return;
+ }
+ service.requestAutoFillLocked(activityToken, null, null, flags);
}
}
final class AutoFillManagerServiceStub extends IAutoFillManagerService.Stub {
@Override
- public void showAutoFillInput(AutoFillId id, Rect boundaries) {
- if (DEBUG) Slog.d(TAG, "showAutoFillInput(): id=" + id + ", boundaries=" + boundaries);
+ public void requestAutoFill(AutoFillId id, Rect bounds, int flags) {
+ if (DEBUG) Slog.d(TAG, "requestAutoFill: flags=" + flags + ", autoFillId=" + id
+ + ", bounds=" + bounds);
- // TODO(b/33197203): fail if it's not called by same uid as the top activity
- mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO(MSG_SHOW_AUTO_FILL,
- UserHandle.getCallingUserId(), id, boundaries));
+ // Make sure its called by the top activity.
+ final int uid = Binder.getCallingUid();
+ final IBinder activityToken = LocalServices.getService(ActivityManagerInternal.class)
+ .getTopVisibleActivity(uid);
+ if (activityToken == null) {
+ throw new SecurityException("uid " + uid + " does not own the top activity");
+ }
+
+ mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIIOOO(MSG_REQUEST_AUTO_FILL,
+ UserHandle.getCallingUserId(), flags, activityToken, id, bounds));
}
@Override
- public void requestAutoFill(IBinder activityToken, int userId, Bundle extras, int flags) {
- if (DEBUG) Slog.d(TAG, "requestAutoFill: flags=" + flags + ", userId=" + userId);
+ public void requestAutoFillForUser(int userId, int flags) {
mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
- synchronized (mLock) {
- requestAutoFillLocked(activityToken, userId, extras, flags);
- }
+ mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageII(
+ MSG_REQUEST_AUTO_FILL_FOR_USER, userId, flags));
}
@Override
@@ -304,6 +326,8 @@
}
}
}
+ pw.println("Requests history:");
+ mRequestsHistory.reverseDump(fd, pw, args);
}
@Override
@@ -326,7 +350,7 @@
public void onChange(boolean selfChange, Uri uri, int userId) {
if (DEBUG) Slog.d(TAG, "settings (" + uri + " changed for " + userId);
synchronized (mLock) {
- removeCachedServiceForUserLocked(userId);
+ removeCachedServiceLocked(userId);
}
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
index 77e7b31..0dd891c 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
@@ -16,16 +16,18 @@
package com.android.server.autofill;
-import static com.android.server.autofill.Helper.DEBUG;
-import static com.android.server.autofill.Helper.bundleToString;
import static android.service.autofill.AutoFillService.FLAG_AUTHENTICATION_ERROR;
import static android.service.autofill.AutoFillService.FLAG_AUTHENTICATION_REQUESTED;
import static android.service.autofill.AutoFillService.FLAG_AUTHENTICATION_SUCCESS;
+import static android.view.View.AUTO_FILL_FLAG_TYPE_SAVE;
+import static android.view.autofill.AutoFillManager.FLAG_UPDATE_UI_HIDE;
+
+import static com.android.server.autofill.Helper.DEBUG;
+import static com.android.server.autofill.Helper.bundleToString;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
-import android.app.ActivityManagerInternal;
import android.app.IActivityManager;
import android.app.assist.AssistStructure;
import android.content.BroadcastReceiver;
@@ -35,6 +37,7 @@
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
+import android.graphics.Rect;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.IFingerprintService;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -53,6 +56,7 @@
import android.service.autofill.IAutoFillServerCallback;
import android.service.autofill.IAutoFillService;
import android.service.voice.VoiceInteractionSession;
+import android.util.LocalLog;
import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.Slog;
@@ -64,9 +68,9 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
-import com.android.server.LocalServices;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
@@ -86,6 +90,7 @@
private final int mUserId;
private final int mUid;
private final ComponentName mComponent;
+ private final String mComponentName;
private final Context mContext;
private final IActivityManager mAm;
private final Object mLock;
@@ -93,14 +98,15 @@
private final AutoFillManagerService mManagerService;
private final AutoFillUI mUi;
- // TODO(b/33197203): improve its usage
- // - set maximum number of entries
- // - disable on low-memory devices.
- private final List<String> mRequestHistory = new LinkedList<>();
+ // Token used for fingerprint authentication
+ // TODO(b/33197203): create on demand?
+ private final IBinder mAuthToken = new Binder();
@GuardedBy("mLock")
private final List<QueuedRequest> mQueuedRequests = new LinkedList<>();
+ private final LocalLog mRequestsHistory;
+
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -140,9 +146,10 @@
if (DEBUG) Slog.d(TAG, "queued requests:" + mQueuedRequests.size());
}
for (final QueuedRequest request: mQueuedRequests) {
- requestAutoFillLocked(request.activityToken, request.extras, request.flags,
- false);
+ requestAutoFillLocked(request.activityToken, request.autoFillId,
+ request.bounds, request.flags, false);
}
+ mQueuedRequests.clear();
}
}
@@ -151,7 +158,7 @@
if (DEBUG) Slog.d(TAG, name + " disconnected");
synchronized (mLock) {
mService = null;
- mManagerService.removeCachedServiceForUserLocked(mUserId);
+ mManagerService.removeCachedServiceLocked(mUserId);
}
}
};
@@ -181,9 +188,9 @@
Slog.w(TAG, "no server callback for id " + resultCode);
return;
}
- session.mAppCallback = IAutoFillAppCallback.Stub.asInterface(appBinder);
+ session.setAppCallback(appBinder);
}
- mService.autoFill(structure, session.mServerCallback, session.mExtras, flags);
+ mService.autoFill(structure, session.mServerCallback, flags);
}
};
@@ -198,15 +205,17 @@
long mEstimateTimeOfDeath;
AutoFillManagerServiceImpl(AutoFillManagerService managerService, AutoFillUI ui,
- Context context, Object lock, Handler handler, int userId, int uid,
- ComponentName component, long ttl) {
+ Context context, Object lock, LocalLog requestsHistory, Handler handler, int userId,
+ int uid, ComponentName component, long ttl) {
mManagerService = managerService;
mUi = ui;
mContext = context;
mLock = lock;
+ mRequestsHistory = requestsHistory;
mUserId = userId;
mUid = uid;
mComponent = component;
+ mComponentName = mComponent.flattenToShortString();
mAm = ActivityManager.getService();
setLifeExpectancy(ttl);
@@ -254,60 +263,86 @@
/**
* Asks service to auto-fill an activity.
*
- * @param activityToken activity token
- * @param extras bundle to be passed to the {@link AutoFillService} method.
+ * @param activityToken activity token.
+ * @param autoFillId id of the view that requested auto-fill.
* @param flags optional flags.
*/
- void requestAutoFill(@Nullable IBinder activityToken, @Nullable Bundle extras, int flags) {
- synchronized (mLock) {
- if (!mBound) {
- Slog.w(TAG, "requestAutoFill() failed because it's not bound to service");
- return;
- }
+ void requestAutoFillLocked(IBinder activityToken, @Nullable AutoFillId autoFillId,
+ @Nullable Rect bounds, int flags) {
+ if (!mBound) {
+ Slog.w(TAG, "requestAutoFill() failed because it's not bound to service");
+ return;
}
- // TODO(b/33197203): 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 request'
- if (activityToken == null) {
- // Let's get top activities from all visible stacks.
-
- // TODO(b/33197203): 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;
- }
- activityToken = topActivities.get(0);
- }
-
- final String historyItem = TimeUtils.formatForLogging(System.currentTimeMillis())
- + " - " + activityToken;
- synchronized (mLock) {
- mRequestHistory.add(historyItem);
- requestAutoFillLocked(activityToken, extras, flags, true);
- }
+ requestAutoFillLocked(activityToken, autoFillId, bounds, flags, true);
}
- private void requestAutoFillLocked(IBinder activityToken, @Nullable Bundle extras, int flags,
- boolean queueIfNecessary) {
+ private void requestAutoFillLocked(IBinder activityToken, AutoFillId autoFillId, Rect bounds,
+ int flags, boolean queueIfNecessary) {
if (mService == null) {
if (!queueIfNecessary) {
Slog.w(TAG, "requestAutoFillLocked(): service is null");
return;
}
- if (DEBUG) Slog.d(TAG, "requestAutoFill(): service not set yet, queuing it");
- mQueuedRequests.add(new QueuedRequest(activityToken, extras, flags));
+ if (DEBUG) Slog.d(TAG, "requestAutoFillLocked(): service not set yet, queuing it");
+ mQueuedRequests.add(new QueuedRequest(activityToken, autoFillId, bounds, flags));
+ return;
+ }
+ if (activityToken == null) {
+ // Sanity check
+ Slog.wtf(TAG, "requestAutoFillLocked(): null activityToken");
+ return;
+ }
+
+ final Session session = getSessionByTokenLocked(activityToken);
+
+ if (session != null) {
+ // Session already exist, update UI instead...
+ /*
+ * TODO(b/33197203): currently, it's always reusing the session, regardless of the
+ * requested autoFillId, but it should start a new session for views that
+ * were not part of the initial auto-fill dataset returned by the service. For example:
+ *
+ * 1.Activity has 4 fields, `first_name`, `last_name`, and `address`.
+ * 2.User taps `first_name`.
+ * 3.Service returns a dataset with ids for `first_name` and `last_name`.
+ * 4.When user taps `first_name` (again) or `last_name`, session should be reused, but
+ * when user taps `address`, it should start a new session (since that field was
+ * not part of the initial dataset).
+ *
+ * Similarly, once the activity is auto-filled, the flag logic should be reset (so if
+ * the user taps the view again, a new auto-fill request is made)
+ */
+ if (DEBUG) {
+ Slog.d(TAG, "requestAutoFillLocked(): reusing session for token "
+ + activityToken + ", id " + autoFillId + " and flags " + flags);
+ }
+
+ if ((flags & FLAG_UPDATE_UI_HIDE) != 0) {
+ // TODO(b/33197203): handle it?
+ if (DEBUG) Slog.d(TAG, "ignoring FLAG_UPDATE_UI_HIDE request for " + autoFillId);
+
+ return;
+ }
+
+ session.mCurrentAutoFillId = autoFillId;
+ session.mCurrentBounds = bounds;
+ mUi.showResponse(mUserId, session.mId, autoFillId, bounds, session.mCurrentResponse);
return;
}
final int sessionId = ++sSessionIdCounter;
- final Session session = new Session(sessionId, extras);
- mSessions.put(sessionId, session);
+ if (DEBUG) {
+ Slog.d(TAG, "requestAutoFillLocked(): new session (id=" + sessionId + " for token "
+ + activityToken + " and autoFillId " + autoFillId);
+ }
+
+ final Session newSession = new Session(sessionId, activityToken, autoFillId, bounds);
+ mSessions.put(sessionId, newSession);
+
+ final String historyItem = "s=" + mComponentName + " u=" + mUserId + " f=" + flags
+ + " a=" + activityToken + " i=" + autoFillId + " b=" + bounds;
+ mRequestsHistory.log(historyItem);
/*
* TODO(b/33197203): apply security checks below:
@@ -324,10 +359,79 @@
Slog.w(TAG, "failed to request auto-fill data for " + activityToken);
}
} catch (RemoteException e) {
- // Should happen, it's a local call.
+ // Should not happen, it's a local call.
}
}
+ /**
+ * Called by UI to trigger a save request to the service.
+ */
+ void requestSaveLocked(int sessionId) {
+ // TODO(b/33197203): add MetricsLogger call
+ // TODO(b/33197203): use handler?
+ // TODO(b/33197203): show error on UI on Slog.w situations below???
+
+ if (mService == null) {
+ Slog.w(TAG, "requestSave(): service is null");
+ return;
+ }
+ final Session session = mSessions.get(sessionId);
+ if (session == null) {
+ Slog.w(TAG, "requestSave(): no session with id " + sessionId);
+ return;
+ }
+ final IBinder activityToken = session.mActivityToken.get();
+ if (activityToken == null) {
+ Slog.w(TAG, "activity token for session " + sessionId + " already GCed");
+ return;
+ }
+
+ /*
+ * TODO(b/33197203): apply security checks below:
+ * - checks if disabled by secure settings / device policy
+ * - log operation using noteOp()
+ * - check flags
+ * - display disclosure if needed
+ */
+ try {
+ /* TODO(b/33197203): refactor save logic so it uses a cached AssistStructure, and get
+ the extras to be sent to the service based on the response / dataset in the session.
+ Something like:
+ final Bundle extras = (responseExtras == null && datasetExtras == null)
+ ? null : new Bundle();
+ if (responseExtras != null) {
+ if (DEBUG) Slog.d(TAG, "response extras on save notification: " +
+ bundleToString(responseExtras));
+ extras.putBundle(AutoFillService.EXTRA_RESPONSE_EXTRAS, responseExtras);
+ }
+ if (datasetExtras != null) {
+ if (DEBUG) Slog.d(TAG, "dataset extras on save notification: " +
+ bundleToString(datasetExtras));
+ extras.putBundle(AutoFillService.EXTRA_DATASET_EXTRAS, datasetExtras);
+ }
+
+ */
+
+ if (!mAm.requestAutoFillData(mAssistReceiver, null, sessionId, activityToken,
+ AUTO_FILL_FLAG_TYPE_SAVE)) {
+ Slog.w(TAG, "failed to save for " + activityToken);
+ }
+ } catch (RemoteException e) {
+ // Should not happen, it's a local call.
+ }
+ }
+
+ private Session getSessionByTokenLocked(IBinder activityToken) {
+ final int size = mSessions.size();
+ for (int i = 0; i < size; i++) {
+ final Session session = mSessions.valueAt(i);
+ if (activityToken.equals(session.mActivityToken.get())) {
+ return session;
+ }
+ }
+ return null;
+ }
+
void stopLocked() {
if (DEBUG) Slog.d(TAG, "stopLocked()");
@@ -443,12 +547,13 @@
pw.print(prefix); pw.print("mUserId="); pw.println(mUserId);
pw.print(prefix); pw.print("mUid="); pw.println(mUid);
- pw.print(prefix); pw.print("mComponent="); pw.println(mComponent.flattenToShortString());
+ pw.print(prefix); pw.print("mComponent="); pw.println(mComponentName);
pw.print(prefix); pw.print("mService: "); pw.println(mService);
pw.print(prefix); pw.print("mBound="); pw.println(mBound);
pw.print(prefix); pw.print("mEstimateTimeOfDeath=");
TimeUtils.formatDuration(mEstimateTimeOfDeath, SystemClock.uptimeMillis(), pw);
- pw.println();
+ pw.println();
+ pw.print(prefix); pw.print("mAuthToken: "); pw.println(mAuthToken);
if (DEBUG) {
// ServiceInfo dump is too noisy and redundant (it can be obtained through other dumps)
@@ -456,14 +561,6 @@
mInfo.getServiceInfo().dump(new PrintWriterPrinter(pw), prefix + prefix);
}
- if (mRequestHistory.isEmpty()) {
- pw.print(prefix); pw.println("No history");
- } else {
- pw.print(prefix); pw.println("History:");
- for (int i = 0; i < mRequestHistory.size(); i++) {
- pw.print(prefix2); pw.print(i); pw.print(": "); pw.println(mRequestHistory.get(i));
- }
- }
if (mQueuedRequests.isEmpty()) {
pw.print(prefix); pw.println("No queued requests");
} else {
@@ -480,38 +577,37 @@
} else {
pw.print(prefix); pw.print(size); pw.println(" sessions:");
for (int i = 0; i < size; i++) {
- pw.print(prefix2); pw.print(mSessions.keyAt(i));
- final Session session = mSessions.valueAt(i);
- if (session.mAppCallback == null) {
- pw.println("(no appCallback)");
- } else {
- pw.print(" (app callback: "); pw.print(session.mAppCallback) ; pw.println(")");
- }
+ pw.print(prefix); pw.print("#"); pw.println(i + 1);
+ mSessions.valueAt(i).dumpLocked(prefix2, pw);
}
- pw.println();
}
}
@Override
public String toString() {
return "AutoFillManagerServiceImpl: [userId=" + mUserId + ", uid=" + mUid
- + ", component=" + mComponent.flattenToShortString() + "]";
+ + ", component=" + mComponentName + "]";
}
private static final class QueuedRequest {
final IBinder activityToken;
- final Bundle extras;
+ final AutoFillId autoFillId;
+ final Rect bounds;
final int flags;
- QueuedRequest(IBinder activityToken, Bundle extras, int flags) {
+ QueuedRequest(IBinder activityToken, AutoFillId autoFillId, Rect bounds, int flags) {
this.activityToken = activityToken;
- this.extras = extras;
+ this.autoFillId = autoFillId;
+ this.bounds = bounds;
this.flags = flags;
}
@Override
public String toString() {
- return "flags: " + flags + " token: " + activityToken;
+ if (!DEBUG) return super.toString();
+
+ return "QueuedRequest: [flags=" + flags + ", token=" + activityToken
+ + ", id=" + autoFillId + ", bounds=" + bounds;
}
}
@@ -532,11 +628,17 @@
private final class Session {
private final int mId;
- private final Bundle mExtras;
+ private final WeakReference<IBinder> mActivityToken;
+
private IAutoFillAppCallback mAppCallback;
- // Token used on fingerprint authentication
- private final IBinder mToken = new Binder();
+ // Current view where the auto-fill bar is displayed
+ @GuardedBy("mLock")
+ private AutoFillId mCurrentAutoFillId;
+ @GuardedBy("mLock")
+ private Rect mCurrentBounds;
+ @GuardedBy("mLock")
+ private FillResponse mCurrentResponse;
private final IFingerprintService mFingerprintService;
@@ -710,13 +812,28 @@
}
};
- private Session(int id, Bundle extras) {
+ private Session(int id, IBinder activityToken, AutoFillId autoFillId, Rect bounds) {
this.mId = id;
- this.mExtras = extras;
+ this.mActivityToken = new WeakReference<>(activityToken);
+ this.mCurrentAutoFillId = autoFillId;
+ this.mCurrentBounds = bounds;
this.mFingerprintService = IFingerprintService.Stub
.asInterface(ServiceManager.getService("fingerprint"));
}
+ void setAppCallback(IBinder appBinder) {
+ try {
+ appBinder.linkToDeath(() -> {
+ if (DEBUG) Slog.d(TAG, "app callback died");
+ // TODO(b/33197203): more cleanup here?
+ mAppCallback = null;
+ }, 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "linkToDeath() failed: " + e);
+ }
+ mAppCallback = IAutoFillAppCallback.Stub.asInterface(appBinder);
+ }
+
private void showResponseLocked(FillResponse response, boolean authRequired) {
if (DEBUG) Slog.d(TAG, "showResponse(directly=" + mAutoFillDirectly
+ ", authRequired=" + authRequired +"):" + response);
@@ -735,7 +852,8 @@
if (!authRequired) {
// TODO(b/33197203): add MetricsLogger call
- mUi.showOptions(mUserId, mId, response);
+ mCurrentResponse = response;
+ mUi.showResponse(mUserId, mId, mCurrentAutoFillId, mCurrentBounds, mCurrentResponse);
return;
}
@@ -768,8 +886,8 @@
mDatasetRequiringAuth = dataset;
final boolean requiresFingerprint = dataset.hasCryptoObject();
if (requiresFingerprint) {
- // TODO(b/33197203): check if fingerprint is available first and call error callback
- // with FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE if it's not.
+ // TODO(b/33197203): check if fingerprint is available first and call error
+ // callback with FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE if it's not.
// Start scanning for the fingerprint.
scanFingerprint(dataset.getCryptoObjectOpId());
// Displays the message asking the user to tap (or fingerprint) for AutoFill.
@@ -785,14 +903,27 @@
}
}
+ void dumpLocked(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.print("mId: "); pw.println(mId);
+ pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken.get());
+ pw.print(prefix); pw.print("mCurrentAutoFillId: "); pw.println(mCurrentAutoFillId);
+ pw.print(prefix); pw.print("mCurrentBounds: "); pw.println(mCurrentBounds);
+ pw.print(prefix); pw.print("mCurrentResponse: "); pw.println(mCurrentResponse);
+ pw.print(prefix);
+ pw.print("mResponseRequiringAuth: "); pw.println(mResponseRequiringAuth);
+ pw.print(prefix);
+ pw.print("mDatasetRequiringAuth: "); pw.println(mDatasetRequiringAuth);
+ pw.print(prefix); pw.print("mAutoFillDirectly: "); pw.println(mAutoFillDirectly);
+ }
+
private void autoFillAppLocked(Dataset dataset, boolean removeSelf) {
try {
if (DEBUG) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset);
mAppCallback.autoFill(dataset);
- // TODO(b/33197203): temporarily hack: show the save notification, since save is
- // not integrated with IME yet.
- mUi.showSaveNotification(mUserId, null, dataset);
+ // TODO(b/33197203): temporarily hack: show the save notification after autofilled,
+ // since save is not automatically detected yet.
+ mUi.showSaveNotification(mUserId, mId); removeSelf = false;
} catch (RemoteException e) {
Slog.w(TAG, "Error auto-filling activity: " + e);
@@ -812,7 +943,8 @@
final long token = Binder.clearCallingIdentity();
try {
// TODO(b/33197203): set a timeout?
- mFingerprintService.authenticate(mToken, opId, mUserId, mServiceReceiver, 0, null);
+ mFingerprintService.authenticate(mAuthToken, opId, mUserId, mServiceReceiver, 0,
+ null);
} catch (RemoteException e) {
// Local call, shouldn't happen.
} finally {
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java
index 26f2451..4998e3f 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java
@@ -74,7 +74,7 @@
private int requestAutoFill(int flags) throws RemoteException {
final int userId = getUserIdFromArgs();
- mService.requestAutoFill(null, userId, null, flags);
+ mService.requestAutoFillForUser(userId, flags);
return 0;
}
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
index ad525d4..ba0601c 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
@@ -15,11 +15,8 @@
*/
package com.android.server.autofill;
-import static android.view.View.AUTO_FILL_FLAG_TYPE_FILL;
-import static android.view.View.AUTO_FILL_FLAG_TYPE_SAVE;
import static com.android.server.autofill.Helper.DEBUG;
-import static com.android.server.autofill.Helper.bundleToString;
import android.app.Activity;
import android.app.Notification;
@@ -31,13 +28,17 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.graphics.Rect;
+import android.graphics.PixelFormat;
import android.os.Binder;
import android.os.Bundle;
-import android.service.autofill.AutoFillService;
import android.util.Slog;
import android.view.autofill.AutoFillId;
import android.view.autofill.Dataset;
import android.view.autofill.FillResponse;
+import android.view.Gravity;
+import android.view.View;
+import android.view.WindowManager;
import android.widget.Toast;
import com.android.internal.annotations.GuardedBy;
@@ -56,8 +57,16 @@
private final Context mContext;
+ private final WindowManager mWm;
+
+ /**
+ * Custom snackbar UI used for saving autofill or other informational messages.
+ */
+ private View mSnackbar;
+
AutoFillUI(Context context, AutoFillManagerService service, Object lock) {
mContext = context;
+ mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
mService = service;
mLock = lock;
@@ -87,12 +96,15 @@
/**
* Shows the options from a {@link FillResponse} so the user can pick up the proper
- * {@link Dataset} (when the response has one).
+ * {@link Dataset} (when the response has one) for a given view (identified by
+ * {@code autoFillId}).
*/
- void showOptions(int userId, int sessionId, FillResponse response) {
+ void showResponse(int userId, int sessionId, AutoFillId autoFillId, Rect bounds,
+ FillResponse response) {
+ if (DEBUG) Slog.d(TAG, "showResponse: id=" + autoFillId + ", bounds=" + bounds);
// TODO(b/33197203): proper implementation
- // TODO(b/33197203): make sure if removes the callback from cache
- showOptionsNotification(userId, sessionId, response);
+ // TODO(b/33197203): make sure if removes the session from cache
+ showOptionsNotification(userId, sessionId, autoFillId, response);
}
/**
@@ -127,6 +139,26 @@
}
/**
+ * Shows the UI asking the user to save for auto-fill.
+ */
+ void showSaveUI(int userId, int sessionId) {
+ showSnackbar(new SavePrompt(mContext, new SavePrompt.OnSaveListener() {
+ @Override
+ public void onSaveClick() {
+ hideSnackbar();
+ synchronized (mLock) {
+ final AutoFillManagerServiceImpl service = getServiceLocked(userId);
+ service.requestSaveLocked(sessionId);
+ }
+ }
+ @Override
+ public void onCancelClick() {
+ hideSnackbar();
+ }
+ }));
+ }
+
+ /**
* Called by service after the user user the fingerprint sensors to authenticate.
*/
void dismissFingerprintRequest(int userId, boolean success) {
@@ -150,27 +182,9 @@
return service;
}
- private void onSaveRequested(int userId, Bundle responseExtras, Bundle datasetExtras) {
- synchronized (mLock) {
- final AutoFillManagerServiceImpl service = getServiceLocked(userId);
- if (service == null) return;
-
- final Bundle extras = (responseExtras == null && datasetExtras == null)
- ? null : new Bundle();
-
- if (responseExtras != null) {
- if (DEBUG) Slog.d(TAG, "response extras on save notification: " +
- bundleToString(responseExtras));
- extras.putBundle(AutoFillService.EXTRA_RESPONSE_EXTRAS, responseExtras);
- }
- if (datasetExtras != null) {
- if (DEBUG) Slog.d(TAG, "dataset extras on save notificataion: " +
- bundleToString(datasetExtras));
- extras.putBundle(AutoFillService.EXTRA_DATASET_EXTRAS, datasetExtras);
- }
-
- service.requestAutoFill(null, extras, AUTO_FILL_FLAG_TYPE_SAVE);
- }
+ private void onSaveRequested(int userId, int sessionId) {
+ // TODO(b/33197203): displays the snack bar, until save notification is refactored
+ showSaveUI(userId, sessionId);
}
private void onDatasetPicked(int userId, Dataset dataset, int sessionId) {
@@ -200,6 +214,34 @@
}
}
+ //similar to a snackbar, but can be a bit custom since it is more than just text. This will
+ //allow two buttons for saving or not saving the autofill for instance as well.
+ private void showSnackbar(View snackBar) {
+ WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.FILL_PARENT,
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, // TODO(b/33197203) use TYPE_AUTO_FILL
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN,
+ PixelFormat.TRANSLUCENT);
+
+ params.gravity = Gravity.BOTTOM | Gravity.LEFT;
+
+ UiThread.getHandler().runWithScissors(() -> {
+ mSnackbar = snackBar;
+ mWm.addView(mSnackbar, params);
+ }, 0);
+ }
+
+ private void hideSnackbar() {
+ UiThread.getHandler().runWithScissors(() -> {
+ if (mSnackbar != null) {
+ mWm.removeView(mSnackbar);
+ mSnackbar = null;
+ }
+ }, 0);
+ }
+
/////////////////////////////////////////////////////////////////////////////////
// TODO(b/33197203): temporary code using a notification to request auto-fill. //
// Will be removed once UX decide the right way to present it to the user. //
@@ -224,9 +266,9 @@
private static final String TYPE_SAVE = "save";
private static final String TYPE_AUTH_RESPONSE = "auth_response";
- @GuardedBy("mLock")
+ @GuardedBy("mServiceLock")
private BroadcastReceiver mNotificationReceiver;
- @GuardedBy("mLock")
+ @GuardedBy("mServiceLock")
private final AutoFillManagerService mService;
private final Object mLock;
@@ -253,10 +295,7 @@
Slog.wtf(TAG, "No extra " + EXTRA_NOTIFICATION_TYPE + " on intent " + intent);
return;
}
- final FillResponse response = intent.getParcelableExtra(EXTRA_FILL_RESPONSE);
final Dataset dataset = intent.getParcelableExtra(EXTRA_DATASET);
- final Bundle responseExtras = response == null ? null : response.getExtras();
- final Bundle datasetExtras = dataset == null ? null : dataset.getExtras();
final int flags = intent.getIntExtra(EXTRA_FLAGS, 0);
if (DEBUG) Slog.d(TAG, "Notification received: type=" + type + ", userId=" + userId
@@ -264,7 +303,7 @@
synchronized (mLock) {
switch (type) {
case TYPE_SAVE:
- onSaveRequested(userId, responseExtras, datasetExtras);
+ onSaveRequested(userId, sessionId);
break;
case TYPE_FINISH_SESSION:
onSessionDone(userId, sessionId);
@@ -315,17 +354,18 @@
* Shows a notification with the results of an auto-fill request, using notications actions
* to emulate the auto-fill bar buttons displaying the dataset names.
*/
- private void showOptionsNotification(int userId, int sessionId, FillResponse response) {
+ private void showOptionsNotification(int userId, int callbackId, AutoFillId autoFillId,
+ FillResponse response) {
final long token = Binder.clearCallingIdentity();
try {
- showOptionsNotificationAsSystem(userId, sessionId, response);
+ showOptionsNotificationAsSystem(userId, callbackId, autoFillId, response);
} finally {
Binder.restoreCallingIdentity(token);
}
}
private void showOptionsNotificationAsSystem(int userId, int sessionId,
- FillResponse response) {
+ AutoFillId autoFillId, FillResponse response) {
// Make sure server callback is removed from cache if user cancels the notification.
final Intent deleteIntent = newNotificationIntent(userId, TYPE_FINISH_SESSION)
.putExtra(EXTRA_SESSION_ID, sessionId);
@@ -352,13 +392,13 @@
}
boolean showSave = false;
if (datasets == null ) {
- subTitle = "No options to auto-fill this activity.";
+ subTitle = "No options to auto-fill " + autoFillId;
} else if (datasets.isEmpty()) {
if (savableIds.length == 0) {
- subTitle = "No options to auto-fill this activity.";
+ subTitle = "No options to auto-fill " + autoFillId;
} else {
- subTitle = "No options to auto-fill this activity, but provider can save ids:\n"
- + Arrays.toString(savableIds);
+ subTitle = "No options to auto-fill " + autoFillId
+ + ", but provider can save ids:\n" + Arrays.toString(savableIds);
showSave = true;
}
} else {
@@ -369,7 +409,7 @@
} else {
autoCancel = false;
final int size = datasets.size();
- subTitle = "There are " + size + " option(s).\n"
+ subTitle = "There are " + size + " option(s) to fill " + autoFillId + ".\n"
+ "Use the notification action(s) to select the proper one."
+ "Actions with (F) require fingerprint unlock, and with (P) require"
+ "provider authentication to unlock";
@@ -394,36 +434,28 @@
NotificationManager.from(mContext).notify(TYPE_OPTIONS, userId, notification.build());
if (showSave) {
- showSaveNotification(userId, response, null);
+ showSaveNotification(userId, sessionId);
}
}
- void showSaveNotification(int userId, FillResponse response, Dataset dataset) {
+ void showSaveNotification(int userId, int sessionId) {
final long token = Binder.clearCallingIdentity();
try {
- showSaveNotificationAsSystem(userId, response, dataset);
+ showSaveNotificationAsSystem(userId, sessionId);
} finally {
Binder.restoreCallingIdentity(token);
}
}
- private void showSaveNotificationAsSystem(int userId, FillResponse response, Dataset dataset) {
- final Intent saveIntent = newNotificationIntent(userId, TYPE_SAVE);
- if (response != null) {
- saveIntent.putExtra(EXTRA_FILL_RESPONSE, response);
- }
- if (dataset != null) {
- saveIntent.putExtra(EXTRA_DATASET, dataset);
- }
+ private void showSaveNotificationAsSystem(int userId, int sessionId) {
+ final Intent saveIntent = newNotificationIntent(userId, TYPE_SAVE)
+ .putExtra(EXTRA_SESSION_ID, sessionId);
+
final PendingIntent savePendingIntent = PendingIntent.getBroadcast(mContext,
++sResultCode, saveIntent, PendingIntent.FLAG_ONE_SHOT);
- final String title = "AutoFill Save";
- // Response is not set after fillign an authenticated dataset...
- final String subTitle = response == null
- ? "Tap notification to ask provider to save fields."
- : "Tap notification to ask provider to save fields: \n"
- + Arrays.toString(response.getSavableIds());
+ final String title = "AutoFill Save Emulation";
+ final String subTitle = "Tap notification to launch the save snackbar.";
final Notification notification = newNotificationBuilder()
.setAutoCancel(true)
diff --git a/services/autofill/java/com/android/server/autofill/SavePrompt.java b/services/autofill/java/com/android/server/autofill/SavePrompt.java
new file mode 100644
index 0000000..022d646
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/SavePrompt.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 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.content.Context;
+import android.graphics.Color;
+import android.widget.RelativeLayout;
+import android.widget.RelativeLayout.LayoutParams;
+import android.widget.TextView;
+import android.view.View;
+
+/**
+ * Autofill Save Prompt
+ */
+final class SavePrompt extends RelativeLayout {
+ public interface OnSaveListener {
+ void onSaveClick();
+
+ void onCancelClick();
+ }
+
+ private final TextView mTextView;
+ private final TextView mNoButton;
+ private final TextView mYesButton;
+ private final OnSaveListener mListener;
+
+ SavePrompt(Context context, OnSaveListener listener) {
+ super(context);
+ mListener = listener;
+ setBackgroundColor(Color.YELLOW);
+
+ // TODO(b/33197203): move layout to XML
+ mTextView = new TextView(context);
+ final LayoutParams textParams = new LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ textParams.setMargins(50, 25, 50, 0);
+ mTextView.setLayoutParams(textParams);
+ // TODO(b/33197203): use R.string once final wording is done
+ mTextView.setText("Save for autofill?");
+ mTextView.setId(View.generateViewId());
+
+ mNoButton = new TextView(context);
+ // TODO(b/33197203): use R.string once final wording is done
+ mNoButton.setText("No thanks");
+ mNoButton.setBackgroundColor(Color.TRANSPARENT);
+ mNoButton.setAllCaps(true);
+ mNoButton.setOnClickListener((v) -> {
+ mListener.onCancelClick();
+ });
+
+ mYesButton = new TextView(context);
+ // TODO(b/33197203): use R.string once final wording is done
+ mYesButton.setText("Save");
+ mYesButton.setBackgroundColor(Color.TRANSPARENT);
+ mYesButton.setId(View.generateViewId());
+ mYesButton.setAllCaps(true);
+ mYesButton.setOnClickListener((v) -> {
+ mListener.onSaveClick();
+ });
+
+ addView(mTextView);
+ addView(mNoButton);
+ addView(mYesButton);
+
+ final LayoutParams yesLayoutParams = new LayoutParams(
+ LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+ yesLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
+ yesLayoutParams.addRule(RelativeLayout.BELOW, mTextView.getId());
+ yesLayoutParams.setMargins(25, 25, 50, 25);
+ mYesButton.setLayoutParams(yesLayoutParams);
+ final LayoutParams noLayoutParams = new LayoutParams(
+ LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+ noLayoutParams.addRule(RelativeLayout.LEFT_OF, mYesButton.getId());
+ noLayoutParams.addRule(RelativeLayout.BELOW, mTextView.getId());
+ noLayoutParams.setMargins(50, 25, 25, 25);
+ mNoButton.setLayoutParams(noLayoutParams);
+ }
+}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index f3f8da8..1f8702a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -690,11 +690,6 @@
}
private LegacyTypeTracker mLegacyTypeTracker = new LegacyTypeTracker();
- @VisibleForTesting
- protected HandlerThread createHandlerThread() {
- return new HandlerThread("ConnectivityServiceThread");
- }
-
public ConnectivityService(Context context, INetworkManagementService netManager,
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
this(context, netManager, statsService, policyManager, new IpConnectivityLog());
@@ -715,7 +710,7 @@
mDefaultMobileDataRequest = createInternetRequestForTransport(
NetworkCapabilities.TRANSPORT_CELLULAR, NetworkRequest.Type.BACKGROUND_REQUEST);
- mHandlerThread = createHandlerThread();
+ mHandlerThread = new HandlerThread("ConnectivityServiceThread");
mHandlerThread.start();
mHandler = new InternalHandler(mHandlerThread.getLooper());
mTrackerHandler = new NetworkStateTrackerHandler(mHandlerThread.getLooper());
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 6cc72de..4ab894f 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -132,6 +132,8 @@
private static final String FUSED_LOCATION_SERVICE_ACTION =
"com.android.location.service.FusedLocationProvider";
+ private static final String GMSCORE_PACKAGE = "com.android.google.gms";
+
private static final int MSG_LOCATION_CHANGED = 1;
private static final long NANOS_PER_MILLI = 1000000L;
@@ -140,7 +142,7 @@
private static final long HIGH_POWER_INTERVAL_MS = 5 * 60 * 1000;
// default background throttling interval if not overriden in settings
- private static final long DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 1000;
+ private static final long DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS = 10 * 60 * 1000;
// Location Providers may sometimes deliver location updates
// slightly faster that requested - provide grace period so
@@ -214,10 +216,13 @@
private final HashMap<String, Location> mLastLocationCoarseInterval =
new HashMap<>();
- // all providers that operate over proxy, for authorizing incoming location
+ // all providers that operate over proxy, for authorizing incoming location and whitelisting
+ // throttling
private final ArrayList<LocationProviderProxy> mProxyProviders =
new ArrayList<>();
+ private String[] mBackgroundThrottlePackageWhitelist = new String[]{};
+
// current active user on the device - other users are denied location data
private int mCurrentUserId = UserHandle.USER_SYSTEM;
private int[] mCurrentUserProfiles = new int[] { UserHandle.USER_SYSTEM };
@@ -359,6 +364,26 @@
}
}
}, UserHandle.USER_ALL);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(
+ Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST),
+ true,
+ new ContentObserver(mLocationHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ synchronized (mLock) {
+ String setting = Settings.Global.getString(
+ mContext.getContentResolver(),
+ Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
+ if (setting == null) {
+ setting = "";
+ }
+
+ mBackgroundThrottlePackageWhitelist = setting.split(",");
+ updateProvidersLocked();
+ }
+ }
+ }, UserHandle.USER_ALL);
mPackageMonitor.register(mContext, mLocationHandler.getLooper(), true);
// listen for user change
@@ -1066,19 +1091,6 @@
mProvidersByName.remove(provider.getName());
}
- private boolean isOverlayProviderPackageLocked(String packageName) {
- for (LocationProviderInterface provider : mProviders) {
- if (provider instanceof LocationProviderProxy) {
- if (packageName.equals(
- ((LocationProviderProxy) provider).getConnectedPackageName())) {
- return true;
- }
- }
- }
-
- return false;
- }
-
/**
* Returns "true" if access to the specified location provider is allowed by the current
* user's settings. Access to all location providers is forbidden to non-location-provider
@@ -1542,8 +1554,28 @@
p.setRequest(providerRequest, worksource);
}
- private boolean isThrottlingExemptLocked(Receiver recevier) {
- return isOverlayProviderPackageLocked(recevier.mPackageName);
+ private boolean isThrottlingExemptLocked(Receiver receiver) {
+ if (receiver.mUid == Process.SYSTEM_UID) {
+ return true;
+ }
+
+ if (receiver.mPackageName.equals(GMSCORE_PACKAGE)) {
+ return true;
+ }
+
+ for (LocationProviderProxy provider : mProxyProviders) {
+ if (receiver.mPackageName.equals(provider.getConnectedPackageName())) {
+ return true;
+ }
+ }
+
+ for (String whitelistedPackage : mBackgroundThrottlePackageWhitelist) {
+ if (receiver.mPackageName.equals(whitelistedPackage)) {
+ return true;
+ }
+ }
+
+ return false;
}
private class UpdateRecord {
@@ -1766,7 +1798,7 @@
if (D) Log.d(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver))
+ " " + name + " " + request + " from " + packageName + "(" + uid + " "
+ (record.mIsForegroundUid ? "foreground" : "background")
- + (isOverlayProviderPackageLocked(receiver.mPackageName) ? " [whitelisted]" : "") + ")");
+ + (isThrottlingExemptLocked(receiver) ? " [whitelisted]" : "") + ")");
UpdateRecord oldRecord = receiver.mUpdateRecords.put(name, record);
if (oldRecord != null) {
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index d51e96a..c77a407 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -19,6 +19,8 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
import android.os.Build;
import android.os.RecoverySystem;
import android.os.SystemClock;
@@ -57,15 +59,31 @@
private static final int LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS = 3;
private static final int LEVEL_FACTORY_RESET = 4;
- private static final boolean DISABLE_RESET_SETTINGS = true;
-
/** Threshold for boot loops */
private static final Threshold sBoot = new BootThreshold();
/** Threshold for app crash loops */
private static SparseArray<Threshold> sApps = new SparseArray<>();
private static boolean isDisabled() {
- return Build.IS_ENG || SystemProperties.getBoolean(PROP_DISABLE_RESCUE, false);
+ // We're disabled on all engineering devices
+ if (Build.IS_ENG) return true;
+
+ // We're disabled on userdebug devices connected over USB, since that's
+ // a decent signal that someone is actively trying to debug the device,
+ // or that it's in a lab environment.
+ if (Build.IS_USERDEBUG) {
+ try {
+ if (LocalServices.getService(BatteryManagerInternal.class)
+ .getPlugType() == BatteryManager.BATTERY_PLUGGED_USB) {
+ return true;
+ } else {
+ }
+ } catch (Throwable ignored) {
+ }
+ }
+
+ // One last-ditch check
+ return SystemProperties.getBoolean(PROP_DISABLE_RESCUE, false);
}
/**
@@ -161,8 +179,6 @@
}
private static void resetAllSettings(Context context, int mode) throws Exception {
- if (DISABLE_RESET_SETTINGS) return;
-
// Try our best to reset all settings possible, and once finished
// rethrow any exception that we encountered
Exception res = null;
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index c07add0..629da86 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -108,6 +108,8 @@
import com.android.server.NativeDaemonConnector.SensitiveArg;
import com.android.server.pm.PackageManagerService;
import com.android.server.storage.AppFuseBridge;
+import com.android.server.storage.FileCollector;
+
import libcore.io.IoUtils;
import libcore.util.EmptyArray;
@@ -817,6 +819,9 @@
}
private void handleSystemReady() {
+ // Register kernel mapping from extensions to statistics GIDs
+ FileCollector.updateKernelExtensions();
+
initIfReadyAndConnected();
resetIfReadyAndConnected();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 43afbd1..140ae9b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1422,6 +1422,7 @@
ParcelFileDescriptor mProfileFd;
int mSamplingInterval = 0;
boolean mAutoStopProfiler = false;
+ boolean mStreamingOutput = false;
int mProfileType = 0;
final ProcessMap<Pair<Long, String>> mMemWatchProcesses = new ProcessMap<>();
String mMemWatchDumpProcName;
@@ -6560,12 +6561,14 @@
ParcelFileDescriptor profileFd = null;
int samplingInterval = 0;
boolean profileAutoStop = false;
+ boolean profileStreamingOutput = false;
if (mProfileApp != null && mProfileApp.equals(processName)) {
mProfileProc = app;
profileFile = mProfileFile;
profileFd = mProfileFd;
samplingInterval = mSamplingInterval;
profileAutoStop = mAutoStopProfiler;
+ profileStreamingOutput = mStreamingOutput;
}
boolean enableTrackAllocation = false;
if (mTrackAllocationApp != null && mTrackAllocationApp.equals(processName)) {
@@ -6595,7 +6598,8 @@
profileFd = profileFd.dup();
}
ProfilerInfo profilerInfo = profileFile == null ? null
- : new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop);
+ : new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop,
+ profileStreamingOutput);
// We deprecated Build.SERIAL and only apps that target pre NMR1
// SDK can see it. Since access to the serial is now behind a
@@ -12050,12 +12054,12 @@
}
@Override
- public void startConfirmDeviceCredentialIntent(Intent intent) {
+ public void startConfirmDeviceCredentialIntent(Intent intent, Bundle options) {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "startConfirmDeviceCredentialIntent");
synchronized (this) {
final long ident = Binder.clearCallingIdentity();
try {
- mActivityStarter.startConfirmCredentialIntent(intent);
+ mActivityStarter.startConfirmCredentialIntent(intent, options);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -12194,6 +12198,7 @@
mProfileFd = profilerInfo.profileFd;
mSamplingInterval = profilerInfo.samplingInterval;
mAutoStopProfiler = profilerInfo.autoStopProfiler;
+ mStreamingOutput = profilerInfo.streamingOutput;
mProfileType = 0;
}
}
@@ -15172,7 +15177,7 @@
pw.println(" mProfileApp=" + mProfileApp + " mProfileProc=" + mProfileProc);
pw.println(" mProfileFile=" + mProfileFile + " mProfileFd=" + mProfileFd);
pw.println(" mSamplingInterval=" + mSamplingInterval + " mAutoStopProfiler="
- + mAutoStopProfiler);
+ + mAutoStopProfiler + " mStreamingOutput=" + mStreamingOutput);
pw.println(" mProfileType=" + mProfileType);
}
}
@@ -19598,10 +19603,15 @@
/** Helper method that requests bounds from WM and applies them to stack. */
private void resizeStackWithBoundsFromWindowManager(int stackId, boolean deferResume) {
- final Rect newBounds = mStackSupervisor.getStack(stackId).getBoundsForNewConfiguration();
+ final Rect newStackBounds = new Rect();
+ final Rect newTempTaskBounds = new Rect();
+ mStackSupervisor.getStack(stackId).getBoundsForNewConfiguration(newStackBounds,
+ newTempTaskBounds);
mStackSupervisor.resizeStackLocked(
- stackId, newBounds, null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
- false /* preserveWindows */, false /* allowResizeInDockedMode */, deferResume);
+ stackId, !newStackBounds.isEmpty() ? newStackBounds : null /* bounds */,
+ !newTempTaskBounds.isEmpty() ? newTempTaskBounds : null /* tempTaskBounds */,
+ null /* tempTaskInsetBounds */, false /* preserveWindows */,
+ false /* allowResizeInDockedMode */, deferResume);
}
/**
@@ -22021,6 +22031,7 @@
mProfileFile = null;
mProfileType = 0;
mAutoStopProfiler = false;
+ mStreamingOutput = false;
mSamplingInterval = 0;
}
@@ -22592,6 +22603,13 @@
}
@Override
+ public IBinder getTopVisibleActivity(int uid) {
+ synchronized (ActivityManagerService.this) {
+ return mStackSupervisor.getTopVisibleActivity(uid);
+ }
+ }
+
+ @Override
public void notifyDockedStackMinimizedChanged(boolean minimized) {
synchronized (ActivityManagerService.this) {
mStackSupervisor.setDockedStackMinimized(minimized);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index df8dd6b..8c34776 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -115,6 +115,7 @@
private String mProfileFile;
private int mSamplingInterval;
private boolean mAutoStop;
+ private boolean mStreaming; // Streaming the profiling output to a file.
private int mDisplayId;
private int mStackId;
@@ -256,6 +257,7 @@
mProfileFile = null;
mSamplingInterval = 0;
mAutoStop = false;
+ mStreaming = false;
mUserId = defUser;
mDisplayId = INVALID_DISPLAY;
mStackId = INVALID_STACK_ID;
@@ -277,6 +279,8 @@
mAutoStop = false;
} else if (opt.equals("--sampling")) {
mSamplingInterval = Integer.parseInt(getNextArgRequired());
+ } else if (opt.equals("--streaming")) {
+ mStreaming = true;
} else if (opt.equals("-R")) {
mRepeat = Integer.parseInt(getNextArgRequired());
} else if (opt.equals("-S")) {
@@ -354,7 +358,8 @@
if (fd == null) {
return 1;
}
- profilerInfo = new ProfilerInfo(mProfileFile, fd, mSamplingInterval, mAutoStop);
+ profilerInfo = new ProfilerInfo(mProfileFile, fd, mSamplingInterval, mAutoStop,
+ mStreaming);
}
pw.println("Starting: " + intent);
@@ -659,6 +664,7 @@
int userId = UserHandle.USER_CURRENT;
int profileType = 0;
mSamplingInterval = 0;
+ mStreaming = false;
String process = null;
@@ -672,6 +678,8 @@
userId = UserHandle.parseUserArg(getNextArgRequired());
} else if (opt.equals("--wall")) {
wall = true;
+ } else if (opt.equals("--streaming")) {
+ mStreaming = true;
} else if (opt.equals("--sampling")) {
mSamplingInterval = Integer.parseInt(getNextArgRequired());
} else {
@@ -716,7 +724,7 @@
if (fd == null) {
return -1;
}
- profilerInfo = new ProfilerInfo(profileFile, fd, mSamplingInterval, false);
+ profilerInfo = new ProfilerInfo(profileFile, fd, mSamplingInterval, false, mStreaming);
}
try {
@@ -2425,7 +2433,7 @@
pw.println(" help");
pw.println(" Print this help text.");
pw.println(" start-activity [-D] [-N] [-W] [-P <FILE>] [--start-profiler <FILE>]");
- pw.println(" [--sampling INTERVAL] [-R COUNT] [-S]");
+ pw.println(" [--sampling INTERVAL] [--streaming] [-R COUNT] [-S]");
pw.println(" [--track-allocation] [--user <USER_ID> | current] <INTENT>");
pw.println(" Start an Activity. Options are:");
pw.println(" -D: enable debugging");
@@ -2434,6 +2442,8 @@
pw.println(" --start-profiler <FILE>: start profiler and send results to <FILE>");
pw.println(" --sampling INTERVAL: use sample profiling with INTERVAL microseconds");
pw.println(" between samples (use with --start-profiler)");
+ pw.println(" --streaming: stream the profiling output to the specified file");
+ pw.println(" (use with --start-profiler)");
pw.println(" -P <FILE>: like above, but profiling stops when app goes idle");
pw.println(" -R: repeat the activity launch <COUNT> times. Prior to each repeat,");
pw.println(" the top activity will be finished.");
@@ -2480,11 +2490,14 @@
pw.println(" stop: stop tracing IPC transactions and dump the results to file.");
pw.println(" --dump-file <FILE>: Specify the file the trace should be dumped to.");
pw.println(" profile [start|stop] [--user <USER_ID> current] [--sampling INTERVAL]");
- pw.println(" <PROCESS> <FILE>");
+ pw.println(" [--streaming] <PROCESS> <FILE>");
pw.println(" Start and stop profiler on a process. The given <PROCESS> argument");
pw.println(" may be either a process name or pid. Options are:");
pw.println(" --user <USER_ID> | current: When supplying a process name,");
pw.println(" specify user of process to profile; uses current user if not specified.");
+ pw.println(" --sampling INTERVAL: use sample profiling with INTERVAL microseconds");
+ pw.println(" between samples");
+ pw.println(" --streaming: stream the profiling output to the specified file");
pw.println(" dumpheap [--user <USER_ID> current] [-n] <PROCESS> <FILE>");
pw.println(" Dump the heap of a process. The given <PROCESS> argument may");
pw.println(" be either a process name or pid. Options are:");
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 2a849b6..8ca77c5 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1810,7 +1810,8 @@
if (_taskDescription.getIconFilename() == null &&
(icon = _taskDescription.getIcon()) != null) {
final String iconFilename = createImageFilename(createTime, task.taskId);
- final File iconFile = new File(TaskPersister.getUserImagesDir(userId), iconFilename);
+ final File iconFile = new File(TaskPersister.getUserImagesDir(task.userId),
+ iconFilename);
final String iconFilePath = iconFile.getAbsolutePath();
service.mRecentTasks.saveImage(icon, iconFilePath);
_taskDescription.setIconFilename(iconFilePath);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 98b5835c..6d5678c 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -26,6 +26,8 @@
import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.FLAG_SHOW_WITH_INSECURE_LOCKSCREEN;
+
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_APP;
@@ -542,8 +544,10 @@
mWindowContainerController.setPictureInPictureAspectRatio(aspectRatio);
}
- void getStackDockedModeBounds(Rect outBounds, boolean ignoreVisibility) {
- mWindowContainerController.getStackDockedModeBounds(outBounds, ignoreVisibility);
+ void getStackDockedModeBounds(Rect outBounds, Rect outTempBounds, Rect outTempInsetBounds,
+ boolean ignoreVisibility) {
+ mWindowContainerController.getStackDockedModeBounds(outBounds, outTempBounds,
+ outTempInsetBounds, ignoreVisibility);
}
void prepareFreezingTaskBounds() {
@@ -562,8 +566,8 @@
outBounds.setEmpty();
}
- Rect getBoundsForNewConfiguration() {
- return mWindowContainerController.getBoundsForNewConfiguration();
+ void getBoundsForNewConfiguration(Rect outBounds, Rect outTempBounds) {
+ mWindowContainerController.getBoundsForNewConfiguration(outBounds, outTempBounds);
}
void positionChildWindowContainerAtTop(TaskRecord child) {
@@ -1882,6 +1886,12 @@
if (isTop) {
mTopActivityOccludesKeyguard |= showWhenLocked;
}
+
+ final boolean canShowWithKeyguard = isOnShowWhenLockedInsecureDisplay()
+ && mStackSupervisor.mKeyguardController.canDismissKeyguard();
+ if (canShowWithKeyguard) {
+ return true;
+ }
}
if (keyguardShowing) {
@@ -1897,6 +1907,22 @@
}
}
+ /**
+ * Check if the display to which this stack is attached has
+ * {@link Display#FLAG_SHOW_WITH_INSECURE_LOCKSCREEN} applied.
+ */
+ private boolean isOnShowWhenLockedInsecureDisplay() {
+ final ActivityStackSupervisor.ActivityDisplay activityDisplay
+ = mActivityContainer.mActivityDisplay;
+ if (activityDisplay == null) {
+ throw new IllegalStateException("Stack is not attached to any display, stackId="
+ + mStackId);
+ }
+
+ final int flags = activityDisplay.mDisplay.getFlags();
+ return (flags & FLAG_SHOW_WITH_INSECURE_LOCKSCREEN) != 0;
+ }
+
private void checkTranslucentActivityWaiting(ActivityRecord top) {
if (mTranslucentActivityWaiting != top) {
mUndrawnActivitiesBelowTopTranslucent.clear();
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 29032f8..b2b3e61 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -418,8 +418,6 @@
final ActivityMetricsLogger mActivityMetricsLogger;
- private final ResizeDockedStackTimeout mResizeDockedStackTimeout;
-
@Override
protected int getChildCount() {
return mActivityDisplays.size();
@@ -529,7 +527,6 @@
mService = service;
mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper());
mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext);
- mResizeDockedStackTimeout = new ResizeDockedStackTimeout(service, this, mHandler);
mKeyguardController = new KeyguardController(service, this);
}
@@ -1322,7 +1319,8 @@
}
profilerInfo = new ProfilerInfo(profileFile, profileFd,
- mService.mSamplingInterval, mService.mAutoStopProfiler);
+ mService.mSamplingInterval, mService.mAutoStopProfiler,
+ mService.mStreamingOutput);
}
}
}
@@ -2346,13 +2344,19 @@
// static stacks need to be adjusted so they don't overlap with the docked stack.
// We get the bounds to use from window manager which has been adjusted for any
// screen controls and is also the same for all stacks.
+ final Rect tempOtherTaskRect = new Rect();
+ final Rect tempOtherTaskInsetRect = new Rect();
for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
final ActivityStack current = getStack(i);
if (current != null && StackId.isResizeableByDockedStack(i)) {
- current.getStackDockedModeBounds(tempRect, true /* ignoreVisibility */);
- resizeStackLocked(i, tempRect, tempOtherTaskBounds,
- tempOtherTaskInsetBounds, preserveWindows,
- true /* allowResizeInDockedMode */, deferResume);
+ current.getStackDockedModeBounds(tempRect, tempOtherTaskRect,
+ tempOtherTaskInsetRect, true /* ignoreVisibility */);
+ resizeStackLocked(i, tempRect,
+ !tempOtherTaskRect.isEmpty() ? tempOtherTaskRect :
+ tempOtherTaskBounds,
+ !tempOtherTaskInsetRect.isEmpty() ? tempOtherTaskInsetRect :
+ tempOtherTaskInsetBounds,
+ preserveWindows, true /* allowResizeInDockedMode */, deferResume);
}
}
}
@@ -2364,12 +2368,6 @@
mWindowManager.continueSurfaceLayout();
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
-
- mResizeDockedStackTimeout.notifyResizing(dockedBounds,
- tempDockedTaskBounds != null
- || tempDockedTaskInsetBounds != null
- || tempOtherTaskBounds != null
- || tempOtherTaskInsetBounds != null);
}
void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
@@ -4901,4 +4899,24 @@
}
return topActivityTokens;
}
+
+ public IBinder getTopVisibleActivity(int uid) {
+ // TODO(b/33197203): get rid of DEFAULT_DISPLAY here?. Used in
+ // VoiceInteractionManagerServiceImpl#showSessionLocked.
+ final ActivityDisplay display = mActivityDisplays.get(DEFAULT_DISPLAY);
+ if (display == null) {
+ return null;
+ }
+ final ArrayList<ActivityStack> stacks = display.mStacks;
+ for (int i = stacks.size() - 1; i >= 0; i--) {
+ ActivityStack stack = stacks.get(i);
+ if (stack.getStackVisibilityLocked(null) == ActivityStack.STACK_VISIBLE) {
+ ActivityRecord top = stack.topActivity();
+ if (top != null && stack == mFocusedStack && top.app.uid == uid) {
+ return top.appToken;
+ }
+ }
+ }
+ return null;
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index b913a23..96f732e 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -648,18 +648,18 @@
null);
credential.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
// Show confirm credentials activity.
- startConfirmCredentialIntent(credential);
+ startConfirmCredentialIntent(credential, null);
}
}
- void startConfirmCredentialIntent(Intent intent) {
+ void startConfirmCredentialIntent(Intent intent, Bundle optionsBundle) {
intent.addFlags(FLAG_ACTIVITY_NEW_TASK |
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
FLAG_ACTIVITY_TASK_ON_HOME);
- final ActivityOptions options = ActivityOptions.makeBasic();
+ ActivityOptions options = (optionsBundle != null ? new ActivityOptions(optionsBundle)
+ : ActivityOptions.makeBasic());
options.setLaunchTaskId(mSupervisor.getHomeActivity().task.taskId);
- mService.mContext.startActivityAsUser(intent, options.toBundle(),
- UserHandle.CURRENT);
+ mService.mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
}
final int startActivityMayWait(IApplicationThread caller, int callingUid,
@@ -2072,6 +2072,9 @@
case RECENTS_STACK_ID:
return r.isRecentsActivity();
default:
+ if (StackId.isDynamicStack(stackId)) {
+ return true;
+ }
Slog.e(TAG, "isValidLaunchStackId: Unexpected stackId=" + stackId);
return false;
}
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index b0a4746..2bd119e 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -280,7 +280,7 @@
/**
* @return true if Keyguard can be currently dismissed without entering credentials.
*/
- private boolean canDismissKeyguard() {
+ boolean canDismissKeyguard() {
return mWindowManager.isKeyguardTrusted() || !mWindowManager.isKeyguardSecure();
}
diff --git a/services/core/java/com/android/server/am/ResizeDockedStackTimeout.java b/services/core/java/com/android/server/am/ResizeDockedStackTimeout.java
deleted file mode 100644
index ff39589..0000000
--- a/services/core/java/com/android/server/am/ResizeDockedStackTimeout.java
+++ /dev/null
@@ -1,63 +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 com.android.server.am;
-
-import android.graphics.Rect;
-import android.os.Handler;
-
-import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
-
-/**
- * When resizing the docked stack, a caller can temporarily supply task bounds that are different
- * from the stack bounds. In order to return to a sane state if the caller crashes or has a bug,
- * this class manages this cycle.
- */
-class ResizeDockedStackTimeout {
-
- private static final long TIMEOUT_MS = 10 * 1000;
- private final ActivityManagerService mService;
- private final ActivityStackSupervisor mSupervisor;
- private final Handler mHandler;
- private final Rect mCurrentDockedBounds = new Rect();
-
- private final Runnable mTimeoutRunnable = new Runnable() {
- @Override
- public void run() {
- synchronized (mService) {
- mSupervisor.resizeDockedStackLocked(mCurrentDockedBounds, null, null, null, null,
- PRESERVE_WINDOWS);
- }
- }
- };
-
- ResizeDockedStackTimeout(ActivityManagerService service, ActivityStackSupervisor supervisor,
- Handler handler) {
- mService = service;
- mSupervisor = supervisor;
- mHandler = handler;
- }
-
- void notifyResizing(Rect dockedBounds, boolean hasTempBounds) {
- mHandler.removeCallbacks(mTimeoutRunnable);
- if (!hasTempBounds) {
- return;
- }
- mCurrentDockedBounds.set(dockedBounds);
- mHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MS);
- }
-
-}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 31ef94f..213041e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -144,7 +144,6 @@
*/
public class AudioService extends IAudioService.Stub
implements AccessibilityManager.TouchExplorationStateChangeListener,
- AccessibilityManager.AccessibilityStateChangeListener,
AccessibilityManager.AccessibilityServicesStateChangeListener {
private static final String TAG = "AudioService";
@@ -5926,25 +5925,13 @@
//==========================================================================================
// Accessibility
- /**
- * Compile-time constant to enable the use of an independent a11y volume:
- * - set to true to listen to a11y services state changes and read
- * the whether any exposes the FLAG_ENABLE_ACCESSIBILITY_VOLUME flag
- * - set to false to listen to when accessibility services are started (e.g. "TalkBack started")
- */
- private static final boolean USE_FLAG_ENABLE_ACCESSIBILITY_VOLUME = false;
-
private void initA11yMonitoring() {
final AccessibilityManager accessibilityManager =
(AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
updateDefaultStreamOverrideDelay(accessibilityManager.isTouchExplorationEnabled());
updateA11yVolumeAlias(accessibilityManager.isEnabled());
accessibilityManager.addTouchExplorationStateChangeListener(this);
- if (USE_FLAG_ENABLE_ACCESSIBILITY_VOLUME) {
- accessibilityManager.addAccessibilityServicesStateChangeListener(this);
- } else {
- accessibilityManager.addAccessibilityStateChangeListener(this);
- }
+ accessibilityManager.addAccessibilityServicesStateChangeListener(this);
}
//---------------------------------------------------------------------------------
@@ -5982,12 +5969,6 @@
private static boolean sIndependentA11yVolume = false;
- // implementation of AccessibilityStateChangeListener
- @Override
- public void onAccessibilityStateChanged(boolean enabled) {
- updateA11yVolumeAlias(enabled);
- }
-
// implementation of AccessibilityServicesStateChangeListener
@Override
public void onAccessibilityServicesStateChanged() {
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 6719182..4798880 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -93,6 +93,11 @@
public static final int FLAG_ROUND = 1 << 8;
/**
+ * Flag: This display can show its content when non-secure keyguard is shown.
+ */
+ public static final int FLAG_SHOW_WITH_INSECURE_LOCKSCREEN = 1 << 9;
+
+ /**
* Touch attachment: Display does not receive touch.
*/
public static final int TOUCH_NONE = 0;
@@ -420,6 +425,9 @@
if ((flags & FLAG_ROUND) != 0) {
msg.append(", FLAG_ROUND");
}
+ if ((flags & FLAG_SHOW_WITH_INSECURE_LOCKSCREEN) != 0) {
+ msg.append(", FLAG_SHOW_WITH_INSECURE_LOCKSCREEN");
+ }
return msg.toString();
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index cd07793..7d18d36 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -16,6 +16,13 @@
package com.android.server.display;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
+import static android.hardware.display.DisplayManager
+ .VIRTUAL_DISPLAY_FLAG_SHOW_WITH_INSECURE_LOCKSCREEN;
+
import com.android.internal.util.IndentingPrintWriter;
import android.Manifest;
@@ -1446,11 +1453,17 @@
throw new IllegalArgumentException("Surface can't be single-buffered");
}
- if ((flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) {
- flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
+ if ((flags & VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) {
+ flags |= VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
+
+ // Public displays can't be allowed to show content when locked.
+ if ((flags & VIRTUAL_DISPLAY_FLAG_SHOW_WITH_INSECURE_LOCKSCREEN) != 0) {
+ throw new IllegalArgumentException(
+ "Public display must not be marked as SHOW_WHEN_LOCKED_INSECURE");
+ }
}
- if ((flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY) != 0) {
- flags &= ~DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
+ if ((flags & VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY) != 0) {
+ flags &= ~VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
}
if (projection != null) {
@@ -1465,7 +1478,7 @@
}
if (callingUid != Process.SYSTEM_UID &&
- (flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
+ (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
if (!canProjectVideo(projection)) {
throw new SecurityException("Requires CAPTURE_VIDEO_OUTPUT or "
+ "CAPTURE_SECURE_VIDEO_OUTPUT permission, or an appropriate "
@@ -1473,7 +1486,7 @@
+ "display.");
}
}
- if ((flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE) != 0) {
+ if ((flags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0) {
if (!canProjectSecureVideo(projection)) {
throw new SecurityException("Requires CAPTURE_SECURE_VIDEO_OUTPUT "
+ "or an appropriate MediaProjection token to create a "
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 287a25a..d9422ca 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -223,6 +223,9 @@
if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_ROUND) != 0) {
mBaseDisplayInfo.flags |= Display.FLAG_ROUND;
}
+ if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_SHOW_WITH_INSECURE_LOCKSCREEN) != 0) {
+ mBaseDisplayInfo.flags |= Display.FLAG_SHOW_WITH_INSECURE_LOCKSCREEN;
+ }
mBaseDisplayInfo.type = deviceInfo.type;
mBaseDisplayInfo.address = deviceInfo.address;
mBaseDisplayInfo.name = deviceInfo.name;
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 9d0fde5..6a1ef85 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -333,6 +333,9 @@
}
}
}
+ if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOW_WITH_INSECURE_LOCKSCREEN) != 0) {
+ mInfo.flags |= DisplayDeviceInfo.FLAG_SHOW_WITH_INSECURE_LOCKSCREEN;
+ }
mInfo.type = Display.TYPE_VIRTUAL;
mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
mInfo.state = mSurface != null ? Display.STATE_ON : Display.STATE_OFF;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e03ac00..abf3526 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1866,6 +1866,18 @@
res.removedInfo.args.doPostDeleteLI(true);
}
}
+
+ if (!isEphemeral(res.pkg)) {
+ // Notify DexManager that the package was installed for new users.
+ // The updated users should already be indexed and the package code paths
+ // should not change.
+ // Don't notify the manager for ephemeral apps as they are not expected to
+ // survive long enough to benefit of background optimizations.
+ for (int userId : firstUsers) {
+ PackageInfo info = getPackageInfo(packageName, /*flags*/ 0, userId);
+ mDexManager.notifyPackageInstalled(info, userId);
+ }
+ }
}
// If someone is watching installs - notify them
@@ -21321,9 +21333,11 @@
if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
FileUtils.deleteContentsAndDir(Environment.getUserSystemDirectory(userId));
FileUtils.deleteContentsAndDir(Environment.getDataSystemDeDirectory(userId));
+ FileUtils.deleteContentsAndDir(Environment.getDataMiscDeDirectory(userId));
}
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
FileUtils.deleteContentsAndDir(Environment.getDataSystemCeDirectory(userId));
+ FileUtils.deleteContentsAndDir(Environment.getDataMiscCeDirectory(userId));
}
}
@@ -21351,6 +21365,8 @@
.listFilesOrEmpty(Environment.getDataSystemDeDirectory()));
Collections.addAll(files, FileUtils
.listFilesOrEmpty(Environment.getDataSystemCeDirectory()));
+ Collections.addAll(files, FileUtils
+ .listFilesOrEmpty(Environment.getDataMiscCeDirectory()));
for (File file : files) {
if (!file.isDirectory()) continue;
diff --git a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
index 6e96726..6eac5e3 100644
--- a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
+++ b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
@@ -25,7 +25,6 @@
import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.PinItemRequest;
import android.content.pm.ShortcutInfo;
-import android.os.Binder;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.Log;
@@ -168,8 +167,8 @@
mLock = lock;
}
- public boolean isRequestPinnedShortcutSupported(int callingUserId) {
- return getRequestPinShortcutConfirmationActivity(callingUserId) != null;
+ public boolean isRequestPinItemSupported(int callingUserId, int requestType) {
+ return getRequestPinConfirmationActivity(callingUserId, requestType) != null;
}
/**
@@ -185,8 +184,10 @@
// First, make sure the launcher supports it.
// Find the confirmation activity in the default launcher.
+ final int requestType = inShortcut != null ?
+ PinItemRequest.REQUEST_TYPE_SHORTCUT : PinItemRequest.REQUEST_TYPE_APPWIDGET;
final Pair<ComponentName, Integer> confirmActivity =
- getRequestPinShortcutConfirmationActivity(userId);
+ getRequestPinConfirmationActivity(userId, requestType);
// If the launcher doesn't support it, just return a rejected result and finish.
if (confirmActivity == null) {
@@ -210,7 +211,8 @@
request = new PinItemRequest(inAppWidget,
new PinItemRequestInner(this, resultIntent, launcherUid));
}
- return startRequestConfirmActivity(confirmActivity.first, launcherUserId, request);
+ return startRequestConfirmActivity(confirmActivity.first, launcherUserId, request,
+ requestType);
}
/**
@@ -330,9 +332,13 @@
}
private boolean startRequestConfirmActivity(ComponentName activity, int launcherUserId,
- PinItemRequest request) {
+ PinItemRequest request, int requestType) {
+ final String action = requestType == LauncherApps.PinItemRequest.REQUEST_TYPE_SHORTCUT ?
+ LauncherApps.ACTION_CONFIRM_PIN_SHORTCUT :
+ LauncherApps.ACTION_CONFIRM_PIN_APPWIDGET;
+
// Start the activity.
- final Intent confirmIntent = new Intent(LauncherApps.ACTION_CONFIRM_PIN_ITEM);
+ final Intent confirmIntent = new Intent(action);
confirmIntent.setComponent(activity);
confirmIntent.putExtra(LauncherApps.EXTRA_PIN_ITEM_REQUEST, request);
confirmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
@@ -351,13 +357,13 @@
}
/**
- * Find the activity that handles {@link LauncherApps#ACTION_CONFIRM_PIN_ITEM} in the
+ * Find the activity that handles {@link LauncherApps#ACTION_CONFIRM_PIN_SHORTCUT} in the
* default launcher.
*/
@Nullable
@VisibleForTesting
- Pair<ComponentName, Integer> getRequestPinShortcutConfirmationActivity(
- int callingUserId) {
+ Pair<ComponentName, Integer> getRequestPinConfirmationActivity(
+ int callingUserId, int requestType) {
// Find the default launcher.
final int launcherUserId = mService.getParentOrSelfUserId(callingUserId);
final ComponentName defaultLauncher = mService.getDefaultLauncher(launcherUserId);
@@ -367,7 +373,7 @@
return null;
}
final ComponentName activity = mService.injectGetPinConfirmationActivity(
- defaultLauncher.getPackageName(), launcherUserId);
+ defaultLauncher.getPackageName(), launcherUserId, requestType);
return (activity == null) ? null : Pair.create(activity, launcherUserId);
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 56d679e..c0fc24c 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -2117,10 +2117,11 @@
}
@Override
- public boolean isRequestPinShortcutSupported(int callingUserId) {
+ public boolean isRequestPinItemSupported(int callingUserId, int requestType) {
final long token = injectClearCallingIdentity();
try {
- return mShortcutRequestPinProcessor.isRequestPinnedShortcutSupported(callingUserId);
+ return mShortcutRequestPinProcessor
+ .isRequestPinItemSupported(callingUserId, requestType);
} finally {
injectRestoreCallingIdentity(token);
}
@@ -2621,6 +2622,11 @@
Preconditions.checkNotNull(appWidget);
return requestPinItem(callingPackage, userId, null, appWidget, resultIntent);
}
+
+ @Override
+ public boolean isRequestPinItemSupported(int callingUserId, int requestType) {
+ return ShortcutService.this.isRequestPinItemSupported(callingUserId, requestType);
+ }
}
final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -3250,16 +3256,19 @@
}
/**
- * Get the {@link LauncherApps#ACTION_CONFIRM_PIN_ITEM} activity in a given package.
+ * Get the {@link LauncherApps#ACTION_CONFIRM_PIN_SHORTCUT} or
+ * {@link LauncherApps#ACTION_CONFIRM_PIN_APPWIDGET} activity in a given package depending on
+ * the requestType.
*/
@Nullable
ComponentName injectGetPinConfirmationActivity(@NonNull String launcherPackageName,
- int launcherUserId) {
+ int launcherUserId, int requestType) {
Preconditions.checkNotNull(launcherPackageName);
+ String action = requestType == LauncherApps.PinItemRequest.REQUEST_TYPE_SHORTCUT ?
+ LauncherApps.ACTION_CONFIRM_PIN_SHORTCUT :
+ LauncherApps.ACTION_CONFIRM_PIN_APPWIDGET;
- final Intent confirmIntent = new Intent(LauncherApps.ACTION_CONFIRM_PIN_ITEM);
- confirmIntent.setPackage(launcherPackageName);
-
+ final Intent confirmIntent = new Intent(action).setPackage(launcherPackageName);
final List<ResolveInfo> candidates = queryActivities(
confirmIntent, launcherUserId, /* exportedOnly =*/ false);
for (ResolveInfo ri : candidates) {
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 6d06838..e809213 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -132,8 +132,8 @@
// - new installed splits
// If we can't find the owner of the dex we simply do not track it. The impact is
// that the dex file will not be considered for offline optimizations.
- // TODO(calin): add hooks for install/uninstall notifications to
- // capture new or obsolete packages.
+ // TODO(calin): add hooks for move/uninstall notifications to
+ // capture package moves or obsolete packages.
if (DEBUG) {
Slog.i(TAG, "Could not find owning package for dex file: " + dexPath);
}
@@ -157,6 +157,20 @@
}
}
+ public void notifyPackageInstalled(PackageInfo info, int userId) {
+ cachePackageCodeLocation(info, userId);
+ }
+
+ private void cachePackageCodeLocation(PackageInfo info, int userId) {
+ PackageCodeLocations pcl = mPackageCodeLocationsCache.get(info.packageName);
+ if (pcl != null) {
+ pcl.mergeAppDataDirs(info.applicationInfo, userId);
+ } else {
+ mPackageCodeLocationsCache.put(info.packageName,
+ new PackageCodeLocations(info.applicationInfo, userId));
+ }
+ }
+
private void loadInternal(Map<Integer, List<PackageInfo>> existingPackages) {
Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
// Cache the code locations for the installed packages. This allows for
@@ -166,13 +180,8 @@
int userId = entry.getKey();
for (PackageInfo pi : packageInfoList) {
// Cache the code locations.
- PackageCodeLocations pcl = mPackageCodeLocationsCache.get(pi.packageName);
- if (pcl != null) {
- pcl.mergeAppDataDirs(pi.applicationInfo, userId);
- } else {
- mPackageCodeLocationsCache.put(pi.packageName,
- new PackageCodeLocations(pi.applicationInfo, userId));
- }
+ cachePackageCodeLocation(pi, userId);
+
// Cache a map from package name to the set of user ids who installed the package.
// We will use it to sync the data and remove obsolete entries from
// mPackageDexUsage.
diff --git a/services/core/java/com/android/server/storage/DiskStatsLoggingService.java b/services/core/java/com/android/server/storage/DiskStatsLoggingService.java
index 0a3abf3..7c43162 100644
--- a/services/core/java/com/android/server/storage/DiskStatsLoggingService.java
+++ b/services/core/java/com/android/server/storage/DiskStatsLoggingService.java
@@ -21,6 +21,7 @@
import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageStats;
import android.os.AsyncTask;
@@ -28,6 +29,7 @@
import android.os.Environment;
import android.os.Environment.UserEnvironment;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -54,7 +56,7 @@
public boolean onStartJob(JobParameters params) {
// We need to check the preconditions again because they may not be enforced for
// subsequent runs.
- if (!isCharging(this)) {
+ if (!isCharging(this) || !isDumpsysTaskEnabled(getContentResolver())) {
jobFinished(params, true);
return false;
}
@@ -105,6 +107,12 @@
}
@VisibleForTesting
+ static boolean isDumpsysTaskEnabled(ContentResolver resolver) {
+ // The default is to treat the task as enabled.
+ return Settings.Global.getInt(resolver, Settings.Global.ENABLE_DISKSTATS_LOGGING, 1) != 0;
+ }
+
+ @VisibleForTesting
static class LogRunnable implements Runnable {
private static final long TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(10);
diff --git a/services/core/java/com/android/server/storage/FileCollector.java b/services/core/java/com/android/server/storage/FileCollector.java
index 90f9f139..59cfaf7 100644
--- a/services/core/java/com/android/server/storage/FileCollector.java
+++ b/services/core/java/com/android/server/storage/FileCollector.java
@@ -26,7 +26,6 @@
import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Map;
/**
* FileCollector walks over a directory and categorizes storage usage by their type.
@@ -44,8 +43,9 @@
AUDIO })
private @interface FileTypes {}
-
- private static final Map<String, Integer> EXTENSION_MAP = new ArrayMap<String, Integer>();
+ // NOTE: If you update these extensions, you'll also want to update
+ // matchgen.py over in installd which is used for non-quota stats.
+ private static final ArrayMap<String, Integer> EXTENSION_MAP = new ArrayMap<>();
static {
// Audio
EXTENSION_MAP.put("aac", AUDIO);
@@ -144,6 +144,36 @@
EXTENSION_MAP.put("xwd", IMAGES);
}
+ private static File mkdir(File parent, String name) {
+ final File file = new File(parent, name);
+ file.mkdir();
+ return file;
+ }
+
+ /**
+ * Update the mapping used by sdcardfs to map from file extensions to GIDs
+ * used for statistics purposes.
+ */
+ public static void updateKernelExtensions() {
+ final File root = new File("/config/sdcardfs/extensions/");
+ if (!root.exists()) return;
+
+ final File audio = mkdir(root, Integer.toString(android.os.Process.MEDIA_AUDIO_GID));
+ final File video = mkdir(root, Integer.toString(android.os.Process.MEDIA_VIDEO_GID));
+ final File image = mkdir(root, Integer.toString(android.os.Process.MEDIA_IMAGE_GID));
+
+ for (int i = 0; i < EXTENSION_MAP.size(); i++) {
+ final String ext = EXTENSION_MAP.keyAt(i);
+ final int type = EXTENSION_MAP.valueAt(i);
+
+ switch (type) {
+ case AUDIO: mkdir(audio, ext); break;
+ case VIDEO: mkdir(video, ext); break;
+ case IMAGES: mkdir(image, ext); break;
+ }
+ }
+ }
+
/**
* Returns the file categorization measurement result.
* @param path Directory to collect and categorize storage in.
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
index 2ec2dba..3a6e328 100644
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ b/services/core/java/com/android/server/wm/DimLayerController.java
@@ -322,6 +322,9 @@
}
mState.remove(dimLayerUser);
}
+ if (mState.isEmpty()) {
+ mSharedFullScreenDimLayer = null;
+ }
}
@VisibleForTesting
@@ -329,6 +332,11 @@
return mState.containsKey(dimLayerUser);
}
+ @VisibleForTesting
+ boolean hasSharedFullScreenDimLayer() {
+ return mSharedFullScreenDimLayer != null;
+ }
+
void applyDimBehind(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
applyDim(dimLayerUser, animator, false /* aboveApp */);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3a74ded..679f178 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -627,6 +627,8 @@
};
/**
+ * Create new {@link DisplayContent} instance, add itself to the root window container and
+ * initialize direct children.
* @param display May not be null.
* @param service You know.
* @param layersController window layer controller used to assign layer to the windows on this
@@ -661,6 +663,9 @@
super.addChild(mTaskStackContainers, null);
super.addChild(mAboveAppWindowsContainers, null);
super.addChild(mImeWindowsContainers, null);
+
+ // Add itself as a child to the root container.
+ mService.mRoot.addChild(this, null);
}
int getDisplayId() {
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index aaf724e..5a2ee9a 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -102,12 +102,14 @@
private int mDividerWindowWidth;
private int mDividerWindowWidthInactive;
private int mDividerInsets;
+ private int mTaskHeightInMinimizedMode;
private boolean mResizing;
private WindowState mWindow;
private final Rect mTmpRect = new Rect();
private final Rect mTmpRect2 = new Rect();
private final Rect mTmpRect3 = new Rect();
private final Rect mLastRect = new Rect();
+ private final Rect mMiddlePositionDockedStackRect = new Rect();
private boolean mLastVisibility = false;
private final RemoteCallbackList<IDockedStackListener> mDockedStackListeners
= new RemoteCallbackList<>();
@@ -189,6 +191,40 @@
return (int) (minWidth / mDisplayContent.getDisplayMetrics().density);
}
+ /**
+ * The middlePositionDockedStackRect is half the screen area that sits at the top (portrait) or
+ * left (landscape).
+ *
+ * @return fixed rect for temp stack
+ */
+ Rect getMiddlePositionDockedStackRect() {
+ return mMinimizedDock && isHomeStackResizable() ? mMiddlePositionDockedStackRect : null;
+ }
+
+ void getHomeStackBoundsInDockedMode(Rect outBounds) {
+ final DisplayInfo di = mDisplayContent.getDisplayInfo();
+ mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
+ mTmpRect);
+ int dividerSize = mDividerWindowWidth - 2 * mDividerInsets;
+ Configuration configuration = mDisplayContent.getConfiguration();
+ if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
+ outBounds.set(0, mTaskHeightInMinimizedMode + dividerSize + mTmpRect.top,
+ di.logicalWidth, di.logicalHeight);
+ } else {
+ outBounds.set(mTaskHeightInMinimizedMode + dividerSize + mTmpRect.left, 0,
+ di.logicalWidth, di.logicalHeight);
+ }
+ }
+
+ boolean isHomeStackResizable() {
+ final TaskStack homeStack = mDisplayContent.getHomeStack();
+ if (homeStack == null) {
+ return false;
+ }
+ final Task homeTask = homeStack.findHomeTask();
+ return homeTask != null && homeTask.isResizeable();
+ }
+
private void initSnapAlgorithmForRotations() {
final Configuration baseConfig = mDisplayContent.getConfiguration();
@@ -228,11 +264,34 @@
com.android.internal.R.dimen.docked_stack_divider_insets);
mDividerWindowWidthInactive = WindowManagerService.dipToPixel(
DIVIDER_WIDTH_INACTIVE_DP, mDisplayContent.getDisplayMetrics());
+ mTaskHeightInMinimizedMode = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.task_height_of_minimized_mode);
initSnapAlgorithmForRotations();
}
+ /**
+ * Calculates the constant rects {@link mMiddlePositionDockedStackRect} based on orientation,
+ * stable insets and display size.
+ */
+ private void updateConstantRects() {
+ final DisplayInfo di = mDisplayContent.getDisplayInfo();
+ mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
+ mTmpRect);
+ int dividerSize = mDividerWindowWidth - 2 * mDividerInsets;
+ Configuration configuration = mDisplayContent.getConfiguration();
+ boolean isHorizontal = configuration.orientation == Configuration.ORIENTATION_PORTRAIT;
+ int middlePosition = DockedDividerUtils.calculateMiddlePosition(isHorizontal, mTmpRect,
+ di.logicalWidth, di.logicalHeight, dividerSize);
+ if (isHorizontal) {
+ mMiddlePositionDockedStackRect.set(0, 0, di.logicalWidth, middlePosition);
+ } else {
+ mMiddlePositionDockedStackRect.set(0, 0, middlePosition, di.logicalHeight);
+ }
+ }
+
void onConfigurationChanged() {
loadDimens();
+ updateConstantRects();
}
boolean isResizing() {
@@ -412,7 +471,19 @@
return mImeHideRequested;
}
- private void notifyDockedStackMinimizedChanged(boolean minimizedDock, long animDuration) {
+ private void notifyDockedStackMinimizedChanged(boolean minimizedDock, boolean animate,
+ boolean isHomeStackResizable) {
+ long animDuration = 0;
+ if (animate) {
+ final TaskStack stack = mDisplayContent.getStackById(DOCKED_STACK_ID);
+ final long transitionDuration = isAnimationMaximizing()
+ ? mService.mAppTransition.getLastClipRevealTransitionDuration()
+ : DEFAULT_APP_TRANSITION_DURATION;
+ mAnimationDuration = (long)
+ (transitionDuration * mService.getTransitionAnimationScaleLocked());
+ mMaximizeMeetFraction = getClipRevealMeetFraction(stack);
+ animDuration = (long) (mAnimationDuration * mMaximizeMeetFraction);
+ }
mService.mH.removeMessages(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED);
mService.mH.obtainMessage(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED,
minimizedDock ? 1 : 0, 0).sendToTarget();
@@ -420,7 +491,8 @@
for (int i = 0; i < size; ++i) {
final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
try {
- listener.onDockedStackMinimizedChanged(minimizedDock, animDuration);
+ listener.onDockedStackMinimizedChanged(minimizedDock, animDuration,
+ isHomeStackResizable);
} catch (RemoteException e) {
Slog.e(TAG_WM, "Error delivering minimized dock changed event.", e);
}
@@ -458,7 +530,8 @@
mDockedStackListeners.register(listener);
notifyDockedDividerVisibilityChanged(wasVisible());
notifyDockedStackExistsChanged(mDisplayContent.getDockedStackIgnoringVisibility() != null);
- notifyDockedStackMinimizedChanged(mMinimizedDock, 0 /* animDuration */);
+ notifyDockedStackMinimizedChanged(mMinimizedDock, false /* animate */,
+ isHomeStackResizable());
notifyAdjustedForImeChanged(mAdjustedForIme, 0 /* animDuration */);
}
@@ -577,17 +650,23 @@
final boolean imeChanged = clearImeAdjustAnimation();
boolean minimizedChange = false;
- if (minimizedDock) {
- if (animate) {
- startAdjustAnimation(0f, 1f);
- } else {
- minimizedChange |= setMinimizedDockedStack(true);
- }
+ if (isHomeStackResizable()) {
+ notifyDockedStackMinimizedChanged(minimizedDock, true /* animate */,
+ true /* isHomeStackResizable */);
+ minimizedChange = true;
} else {
- if (animate) {
- startAdjustAnimation(1f, 0f);
+ if (minimizedDock) {
+ if (animate) {
+ startAdjustAnimation(0f, 1f);
+ } else {
+ minimizedChange |= setMinimizedDockedStack(true);
+ }
} else {
- minimizedChange |= setMinimizedDockedStack(false);
+ if (animate) {
+ startAdjustAnimation(1f, 0f);
+ } else {
+ minimizedChange |= setMinimizedDockedStack(false);
+ }
}
}
if (imeChanged || minimizedChange) {
@@ -688,7 +767,7 @@
private boolean setMinimizedDockedStack(boolean minimized) {
final TaskStack stack = mDisplayContent.getDockedStackIgnoringVisibility();
- notifyDockedStackMinimizedChanged(minimized, 0);
+ notifyDockedStackMinimizedChanged(minimized, false /* animate */, isHomeStackResizable());
return stack != null && stack.setAdjustedForMinimizedDock(minimized ? 1f : 0f);
}
@@ -742,14 +821,8 @@
if (!mAnimationStarted) {
mAnimationStarted = true;
mAnimationStartTime = now;
- final long transitionDuration = isAnimationMaximizing()
- ? mService.mAppTransition.getLastClipRevealTransitionDuration()
- : DEFAULT_APP_TRANSITION_DURATION;
- mAnimationDuration = (long)
- (transitionDuration * mService.getTransitionAnimationScaleLocked());
- mMaximizeMeetFraction = getClipRevealMeetFraction(stack);
- notifyDockedStackMinimizedChanged(mMinimizedDock,
- (long) (mAnimationDuration * mMaximizeMeetFraction));
+ notifyDockedStackMinimizedChanged(mMinimizedDock, true /* animate */,
+ isHomeStackResizable() /* isHomeStackResizable */);
}
float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
t = (isAnimationMaximizing() ? TOUCH_RESPONSE_INTERPOLATOR : mMinimizedDockInterpolator)
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index e039583..5f53d84 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -625,7 +625,7 @@
final InputChannel inputChannel = w.mInputChannel;
final InputWindowHandle inputWindowHandle = w.mInputWindowHandle;
if (inputChannel == null || inputWindowHandle == null || w.mRemoved
- || w.isAdjustedForMinimizedDock()) {
+ || w.canReceiveTouchInput()) {
// Skip this window because it cannot possibly receive input.
return;
}
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 08f9b45..596c3d8 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -114,6 +114,7 @@
public void setIsMinimized(final boolean isMinimized) {
mHandler.post(() -> {
mIsMinimized = isMinimized;
+ mSnapAlgorithm.setMinimized(isMinimized);
});
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 22abf30..80e6655 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -213,7 +213,6 @@
final int displayId = display.getDisplayId();
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
- addChild(dc, null);
final DisplayInfo displayInfo = dc.getDisplayInfo();
final Rect rect = new Rect();
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index 9a6f3eb5..e2ea2c5 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -220,13 +220,17 @@
}
}
- public void getStackDockedModeBounds(Rect outBounds, boolean ignoreVisibility) {
+ public void getStackDockedModeBounds(Rect outBounds, Rect outTempBounds,
+ Rect outTempInsetBounds, boolean ignoreVisibility) {
synchronized (mWindowMap) {
if (mContainer != null) {
- mContainer.getStackDockedModeBoundsLocked(outBounds, ignoreVisibility);
+ mContainer.getStackDockedModeBoundsLocked(outBounds, outTempBounds,
+ outTempInsetBounds, ignoreVisibility);
return;
}
outBounds.setEmpty();
+ outTempBounds.setEmpty();
+ outTempInsetBounds.setEmpty();
}
}
@@ -269,11 +273,9 @@
}
}
- public Rect getBoundsForNewConfiguration() {
+ public void getBoundsForNewConfiguration(Rect outBounds, Rect outTempBounds) {
synchronized(mWindowMap) {
- final Rect outBounds = new Rect();
- mContainer.getBoundsForNewConfiguration(outBounds);
- return outBounds;
+ mContainer.getBoundsForNewConfiguration(outBounds, outTempBounds);
}
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index d3eae8c..0ff1f0c 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -213,7 +213,7 @@
mAdjustedBounds.set(bounds);
final boolean adjusted = !mAdjustedBounds.isEmpty();
Rect insetBounds = null;
- if (adjusted && isAdjustedForMinimizedDock()) {
+ if (adjusted && isAdjustedForMinimizedDockedStack()) {
insetBounds = mBounds;
} else if (adjusted && mAdjustedForIme) {
if (mImeGoingAway) {
@@ -420,9 +420,14 @@
return true;
}
- void getBoundsForNewConfiguration(Rect outBounds) {
+ void getBoundsForNewConfiguration(Rect outBounds, Rect outTempBounds) {
outBounds.set(mBoundsAfterRotation);
mBoundsAfterRotation.setEmpty();
+ final DockedStackDividerController controller = getDisplayContent()
+ .mDividerControllerLocked;
+ if (controller.isMinimizedDock() && mStackId == DOCKED_STACK_ID) {
+ outTempBounds.set(controller.getMiddlePositionDockedStackRect());
+ }
}
/**
@@ -482,7 +487,8 @@
mService.mPolicy.getStableInsetsLw(rotation, displayWidth, displayHeight, outBounds);
final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm(
mService.mContext.getResources(), displayWidth, displayHeight,
- dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds);
+ dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds,
+ isMinimizedDockAndHomeStackResizable());
final SnapTarget target = algorithm.calculateNonDismissingSnapTarget(dividerPosition);
// Recalculate the bounds based on the position of the target.
@@ -675,7 +681,18 @@
super.onDisplayChanged(dc);
}
- void getStackDockedModeBoundsLocked(Rect outBounds, boolean ignoreVisibility) {
+ void getStackDockedModeBoundsLocked(Rect outBounds, Rect outTempBounds,
+ Rect outTempInsetBounds, boolean ignoreVisibility) {
+ if (mStackId == HOME_STACK_ID && findHomeTask().isResizeable()) {
+ // Calculate the home stack bounds when in docked mode
+ getDisplayContent().mDividerControllerLocked
+ .getHomeStackBoundsInDockedMode(outTempBounds);
+ outTempInsetBounds.set(outTempBounds);
+ } else {
+ outTempBounds.setEmpty();
+ outTempInsetBounds.setEmpty();
+ }
+
if ((mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId))
|| mDisplayContent == null) {
outBounds.set(mBounds);
@@ -789,7 +806,9 @@
mService.mDockedStackCreateBounds = null;
final Rect bounds = new Rect();
- getStackDockedModeBoundsLocked(bounds, true /*ignoreVisibility*/);
+ final Rect tempBounds = new Rect();
+ final Rect tempInsetBounds = new Rect();
+ getStackDockedModeBoundsLocked(bounds, tempBounds, tempInsetBounds, true /*ignoreVisibility*/);
getController().requestResize(bounds);
}
@@ -946,8 +965,9 @@
}
}
- boolean isAdjustedForMinimizedDock() {
- return mMinimizeAmount != 0f;
+ boolean shouldIgnoreInput() {
+ return isAdjustedForMinimizedDockedStack() || mStackId == DOCKED_STACK_ID &&
+ isMinimizedDockAndHomeStackResizable();
}
/**
@@ -1075,6 +1095,11 @@
return true;
}
+ private boolean isMinimizedDockAndHomeStackResizable() {
+ return mDisplayContent.mDividerControllerLocked.isMinimizedDock()
+ && mDisplayContent.mDividerControllerLocked.isHomeStackResizable();
+ }
+
/**
* @return the distance in pixels how much the stack gets minimized from it's original size
*/
@@ -1344,9 +1369,17 @@
* tasks (including the focused).
*
* We save the focused task region once we find it, and add it back at the end.
+ *
+ * If the task is home stack and it is resizable in the minimized state, we want to
+ * exclude the docked stack from touch so we need the entire screen area and not just a
+ * small portion which the home stack currently is resized to.
*/
- task.getDimBounds(mTmpRect);
+ if (task.isHomeTask() && isMinimizedDockAndHomeStackResizable()) {
+ mDisplayContent.getLogicalDisplayRect(mTmpRect);
+ } else {
+ task.getDimBounds(mTmpRect);
+ }
if (task == focusedTask) {
// Add the focused task rect back into the exclude region once we are done
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1987f90..d62c62e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -4826,6 +4826,14 @@
mDisplayMetrics, dw, dh, displayId);
config.densityDpi = displayInfo.logicalDensityDpi;
+ config.colorMode =
+ (displayInfo.isHdr()
+ ? Configuration.COLOR_MODE_HDR_YES
+ : Configuration.COLOR_MODE_HDR_NO)
+ | (displayInfo.isWideColorGamut()
+ ? Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_YES
+ : Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_NO);
+
// Update the configuration based on available input devices, lid switch,
// and platform configuration.
config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 724aab1..050adfe 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1672,11 +1672,6 @@
return !mLastReportedConfiguration.equals(getConfiguration());
}
- boolean isAdjustedForMinimizedDock() {
- return mAppToken != null && mAppToken.mTask != null
- && mAppToken.mTask.mStack.isAdjustedForMinimizedDock();
- }
-
void onWindowReplacementTimeout() {
if (mWillReplaceWindow) {
// Since the window already timed out, remove it immediately now.
@@ -2365,7 +2360,13 @@
&& (mViewVisibility == View.VISIBLE) && !mRemoveOnExit
&& ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
&& (mAppToken == null || mAppToken.windowsAreFocusable())
- && !isAdjustedForMinimizedDock();
+ && !canReceiveTouchInput();
+ }
+
+ /** @return true if this window desires touch events. */
+ boolean canReceiveTouchInput() {
+ return mAppToken != null && mAppToken.mTask != null
+ && mAppToken.mTask.mStack.shouldIgnoreInput();
}
@Override
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 2af5f0d..280ff2d0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1531,6 +1531,10 @@
return (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
}
+ AlarmManager getAlarmManager() {
+ return (AlarmManager) mContext.getSystemService(AlarmManager.class);
+ }
+
IWindowManager getIWindowManager() {
return IWindowManager.Stub
.asInterface(ServiceManager.getService(Context.WINDOW_SERVICE));
@@ -2091,7 +2095,7 @@
long token = mInjector.binderClearCallingIdentity();
try {
int affectedUserHandle = parent ? getProfileParentId(userHandle) : userHandle;
- AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ AlarmManager am = mInjector.getAlarmManager();
PendingIntent pi = PendingIntent.getBroadcastAsUser(context, REQUEST_EXPIRE_PASSWORD,
new Intent(ACTION_EXPIRED_PASSWORD_NOTIFICATION),
PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
index 7d68412..9b4de043 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
@@ -16,12 +16,15 @@
package com.android.server.devicepolicy;
+import android.app.AlarmManager;
+import android.app.AlarmManager.OnAlarmListener;
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.NetworkEvent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.SystemClock;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -42,11 +45,26 @@
// If this value changes, update DevicePolicyManager#retrieveNetworkLogs() javadoc
private static final int MAX_EVENTS_PER_BATCH = 1200;
private static final long BATCH_FINALIZATION_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(90);
+ private static final long BATCH_FINALIZATION_TIMEOUT_ALARM_INTERVAL_MS =
+ TimeUnit.MINUTES.toMillis(30);
- static final int LOG_NETWORK_EVENT_MSG = 1;
- static final int FINALIZE_BATCH_MSG = 2;
+ private static final String NETWORK_LOGGING_TIMEOUT_ALARM_TAG = "NetworkLogging.batchTimeout";
private final DevicePolicyManagerService mDpm;
+ private final AlarmManager mAlarmManager;
+
+ private final OnAlarmListener mBatchTimeoutAlarmListener = new OnAlarmListener() {
+ @Override
+ public void onAlarm() {
+ Log.d(TAG, "Received a batch finalization timeout alarm, finalizing "
+ + mNetworkEvents.size() + " pending events.");
+ synchronized (NetworkLoggingHandler.this) {
+ finalizeBatchAndNotifyDeviceOwnerLocked();
+ }
+ }
+ };
+
+ static final int LOG_NETWORK_EVENT_MSG = 1;
// threadsafe as it's Handler's thread confined
@GuardedBy("this")
@@ -68,6 +86,7 @@
NetworkLoggingHandler(Looper looper, DevicePolicyManagerService dpm) {
super(looper);
mDpm = dpm;
+ mAlarmManager = mDpm.mInjector.getAlarmManager();
}
@Override
@@ -85,19 +104,19 @@
}
break;
}
- case FINALIZE_BATCH_MSG: {
- synchronized (NetworkLoggingHandler.this) {
- finalizeBatchAndNotifyDeviceOwnerLocked();
- }
+ default: {
+ Log.d(TAG, "NetworkLoggingHandler received an unknown of message.");
break;
}
}
}
void scheduleBatchFinalization() {
- removeMessages(FINALIZE_BATCH_MSG);
- sendMessageDelayed(obtainMessage(FINALIZE_BATCH_MSG), BATCH_FINALIZATION_TIMEOUT_MS);
- Log.d(TAG, "Scheduled new batch finalization " + BATCH_FINALIZATION_TIMEOUT_MS
+ final long when = SystemClock.elapsedRealtime() + BATCH_FINALIZATION_TIMEOUT_MS;
+ mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP, when,
+ BATCH_FINALIZATION_TIMEOUT_ALARM_INTERVAL_MS, NETWORK_LOGGING_TIMEOUT_ALARM_TAG,
+ mBatchTimeoutAlarmListener, this);
+ Log.d(TAG, "Scheduled a new batch finalization alarm " + BATCH_FINALIZATION_TIMEOUT_MS
+ "ms from now.");
}
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index 0ec5f16..e183a31 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -57,14 +57,15 @@
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
-// THESE TESTS ARE DISABLED FOR NOW BECAUSE THEY DO NOT FINISH 1/2 THE TIME.
public class NotificationManagerServiceTest {
+ private static final long WAIT_FOR_IDLE_TIMEOUT = 2;
private final String pkg = "com.android.server.notification";
private final int uid = Binder.getCallingUid();
private NotificationManagerService mNotificationManagerService;
@@ -74,6 +75,7 @@
private HandlerThread mThread;
@Before
+ @Test
@UiThreadTest
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
@@ -108,6 +110,9 @@
public void waitForIdle() throws Exception {
MessageQueue queue = mThread.getLooper().getQueue();
+ if (queue.isIdle()) {
+ return;
+ }
CountDownLatch latch = new CountDownLatch(1);
queue.addIdleHandler(new MessageQueue.IdleHandler() {
@Override public boolean queueIdle() {
@@ -115,10 +120,10 @@
return false;
}
});
- latch.await();
- if (!queue.isIdle()) {
- waitForIdle();
- }
+ // Timeout is valid in the cases where the queue goes idle before the IdleHandler
+ // is added.
+ latch.await(WAIT_FOR_IDLE_TIMEOUT, TimeUnit.SECONDS);
+ waitForIdle();
}
private NotificationRecord generateNotificationRecord(NotificationChannel channel) {
@@ -136,6 +141,7 @@
return new NotificationRecord(mContext, sbn, channel);
}
+ @Test
@UiThreadTest
public void testCreateNotificationChannels_SingleChannel() throws Exception {
final NotificationChannel channel =
@@ -147,6 +153,7 @@
assertTrue(createdChannel != null);
}
+ @Test
@UiThreadTest
public void testCreateNotificationChannels_NullChannelThrowsException() throws Exception {
try {
@@ -158,6 +165,7 @@
}
}
+ @Test
@UiThreadTest
public void testCreateNotificationChannels_TwoChannels() throws Exception {
final NotificationChannel channel1 =
@@ -170,6 +178,7 @@
assertTrue(mBinderService.getNotificationChannel("test_pkg", "id2") != null);
}
+ @Test
@UiThreadTest
public void testCreateNotificationChannels_SecondCreateDoesNotChangeImportance()
throws Exception {
@@ -188,6 +197,7 @@
assertEquals(NotificationManager.IMPORTANCE_DEFAULT, createdChannel.getImportance());
}
+ @Test
@UiThreadTest
public void testCreateNotificationChannels_IdenticalChannelsInListIgnoresSecond()
throws Exception {
@@ -202,6 +212,7 @@
assertEquals(NotificationManager.IMPORTANCE_DEFAULT, createdChannel.getImportance());
}
+ @Test
@UiThreadTest
public void testBlockedNotifications_suspended() throws Exception {
NotificationUsageStats usageStats = mock(NotificationUsageStats.class);
@@ -217,6 +228,7 @@
verify(usageStats, times(1)).registerSuspendedByAdmin(eq(r));
}
+ @Test
@UiThreadTest
public void testBlockedNotifications_blockedChannel() throws Exception {
NotificationUsageStats usageStats = mock(NotificationUsageStats.class);
@@ -233,6 +245,7 @@
verify(usageStats, times(1)).registerBlocked(eq(r));
}
+ @Test
@UiThreadTest
public void testBlockedNotifications_blockedApp() throws Exception {
NotificationUsageStats usageStats = mock(NotificationUsageStats.class);
@@ -249,6 +262,7 @@
verify(usageStats, times(1)).registerBlocked(eq(r));
}
+ @Test
@UiThreadTest
public void testEnqueueNotificationWithTag_PopulatesGetActiveNotifications() throws Exception {
mBinderService.enqueueNotificationWithTag(mContext.getPackageName(), "opPkg", "tag", 0,
@@ -259,6 +273,7 @@
assertEquals(1, notifs.length);
}
+ @Test
@UiThreadTest
public void testCancelNotificationImmediatelyAfterEnqueue() throws Exception {
mBinderService.enqueueNotificationWithTag(mContext.getPackageName(), "opPkg", "tag", 0,
@@ -270,6 +285,7 @@
assertEquals(0, notifs.length);
}
+ @Test
@UiThreadTest
public void testCancelNotificationsFromListenerImmediatelyAfterEnqueue() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
@@ -282,6 +298,7 @@
assertEquals(0, notifs.length);
}
+ @Test
@UiThreadTest
public void testCancelAllNotificationsImmediatelyAfterEnqueue() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
@@ -294,6 +311,7 @@
assertEquals(0, notifs.length);
}
+ @Test
@UiThreadTest
public void testCancelAllNotifications_IgnoreForegroundService() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
@@ -307,6 +325,7 @@
assertEquals(1, notifs.length);
}
+ @Test
@UiThreadTest
public void testCancelAllNotifications_IgnoreOtherPackages() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
@@ -320,6 +339,7 @@
assertEquals(1, notifs.length);
}
+ @Test
@UiThreadTest
public void testCancelAllNotifications_NullPkgRemovesAll() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
@@ -332,6 +352,7 @@
assertEquals(0, notifs.length);
}
+ @Test
@UiThreadTest
public void testCancelAllNotifications_NullPkgIgnoresUserAllNotifications() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
diff --git a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
index 677e468..d7bfc44 100644
--- a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
@@ -36,6 +36,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
+import android.content.pm.LauncherApps;
import android.content.pm.ShortcutServiceInternal;
import android.os.Handler;
import android.os.UserHandle;
@@ -121,6 +122,18 @@
assertEquals(provider, providerCaptor.getValue().provider);
}
+ public void testIsRequestPinAppWidgetSupported() {
+ ComponentName provider = new ComponentName(mTestContext, DummyAppWidget.class);
+ // Set up users.
+ when(mMockShortcutService.isRequestPinItemSupported(anyInt(), anyInt()))
+ .thenReturn(true, false);
+ assertTrue(mManager.isRequestPinAppWidgetSupported());
+ assertFalse(mManager.isRequestPinAppWidgetSupported());
+
+ verify(mMockShortcutService, times(2)).isRequestPinItemSupported(anyInt(),
+ eq(LauncherApps.PinItemRequest.REQUEST_TYPE_APPWIDGET));
+ }
+
public void testProviderUpdatesReceived() throws Exception {
int widgetId = setupHostAndWidget();
RemoteViews view = new RemoteViews(mPkgName, android.R.layout.simple_list_item_1);
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 9835c88..8c23a91 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -398,7 +398,7 @@
@Override
ComponentName injectGetPinConfirmationActivity(@NonNull String launcherPackageName,
- int launcherUserId) {
+ int launcherUserId, int requestType) {
return mPinConfirmActivityFetcher.apply(launcherPackageName, launcherUserId);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
index 96e8948..0310e16 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
@@ -93,21 +93,24 @@
Pair<ComponentName, Integer> actual;
// User 0
- actual = mProcessor.getRequestPinShortcutConfirmationActivity(USER_0);
+ actual = mProcessor.getRequestPinConfirmationActivity(USER_0,
+ PinItemRequest.REQUEST_TYPE_SHORTCUT);
assertEquals(LAUNCHER_1, actual.first.getPackageName());
assertEquals(PIN_CONFIRM_ACTIVITY_CLASS, actual.first.getClassName());
assertEquals(USER_0, (int) actual.second);
// User 10
- actual = mProcessor.getRequestPinShortcutConfirmationActivity(USER_10);
+ actual = mProcessor.getRequestPinConfirmationActivity(USER_10,
+ PinItemRequest.REQUEST_TYPE_SHORTCUT);
assertEquals(LAUNCHER_2, actual.first.getPackageName());
assertEquals(PIN_CONFIRM_ACTIVITY_CLASS, actual.first.getClassName());
assertEquals(USER_10, (int) actual.second);
// User P0 -> managed profile, return user-0's launcher.
- actual = mProcessor.getRequestPinShortcutConfirmationActivity(USER_P0);
+ actual = mProcessor.getRequestPinConfirmationActivity(USER_P0,
+ PinItemRequest.REQUEST_TYPE_SHORTCUT);
assertEquals(LAUNCHER_1, actual.first.getPackageName());
assertEquals(PIN_CONFIRM_ACTIVITY_CLASS, actual.first.getClassName());
@@ -133,15 +136,18 @@
? null : new ComponentName(packageName, PIN_CONFIRM_ACTIVITY_CLASS);
// User 10 -- still has confirm activity.
- actual = mProcessor.getRequestPinShortcutConfirmationActivity(USER_10);
+ actual = mProcessor.getRequestPinConfirmationActivity(USER_10,
+ PinItemRequest.REQUEST_TYPE_SHORTCUT);
assertEquals(LAUNCHER_2, actual.first.getPackageName());
assertEquals(PIN_CONFIRM_ACTIVITY_CLASS, actual.first.getClassName());
assertEquals(USER_10, (int) actual.second);
// But user-0 and user p0 no longer has a confirmation activity.
- assertNull(mProcessor.getRequestPinShortcutConfirmationActivity(USER_0));
- assertNull(mProcessor.getRequestPinShortcutConfirmationActivity(USER_P0));
+ assertNull(mProcessor.getRequestPinConfirmationActivity(USER_0,
+ PinItemRequest.REQUEST_TYPE_SHORTCUT));
+ assertNull(mProcessor.getRequestPinConfirmationActivity(USER_P0,
+ PinItemRequest.REQUEST_TYPE_SHORTCUT));
// Check from the public API.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
@@ -204,7 +210,7 @@
}
private void assertPinItemRequestIntent(Intent actualIntent, String expectedPackage) {
- assertEquals(LauncherApps.ACTION_CONFIRM_PIN_ITEM, actualIntent.getAction());
+ assertEquals(LauncherApps.ACTION_CONFIRM_PIN_SHORTCUT, actualIntent.getAction());
assertEquals(expectedPackage, actualIntent.getComponent().getPackageName());
assertEquals(PIN_CONFIRM_ACTIVITY_CLASS,
actualIntent.getComponent().getClassName());
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest9.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest9.java
index cfa35c2..26033a3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest9.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest9.java
@@ -72,7 +72,7 @@
}
private void assertPinItemRequestIntent(Intent actualIntent, String expectedPackage) {
- assertEquals(LauncherApps.ACTION_CONFIRM_PIN_ITEM, actualIntent.getAction());
+ assertEquals(LauncherApps.ACTION_CONFIRM_PIN_APPWIDGET, actualIntent.getAction());
assertEquals(expectedPackage, actualIntent.getComponent().getPackageName());
assertEquals(PIN_CONFIRM_ACTIVITY_CLASS,
actualIntent.getComponent().getClassName());
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index b655f3a..2d07e65 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -204,6 +204,46 @@
assertNull(getPackageUseInfo(mBarUser1));
}
+ @Test
+ public void testNotifyPackageInstallUsedByOther() {
+ TestData newPackage = new TestData("newPackage",
+ VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]), mUser0);
+
+ List<String> newSecondaries = newPackage.getSecondaryDexPaths();
+ // Before we notify about the installation of the newPackage if mFoo
+ // is trying to load something from it we should not find it.
+ notifyDexLoad(mFooUser0, newSecondaries, mUser0);
+ assertNull(getPackageUseInfo(newPackage));
+
+ // Notify about newPackage install and let mFoo load its dexes.
+ mDexManager.notifyPackageInstalled(newPackage.mPackageInfo, mUser0);
+ notifyDexLoad(mFooUser0, newSecondaries, mUser0);
+
+ // We should get back the right info.
+ PackageUseInfo pui = getPackageUseInfo(newPackage);
+ assertNotNull(pui);
+ assertFalse(pui.isUsedByOtherApps());
+ assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
+ assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/true, mUser0);
+ }
+
+ @Test
+ public void testNotifyPackageInstallSelfUse() {
+ TestData newPackage = new TestData("newPackage",
+ VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]), mUser0);
+
+ List<String> newSecondaries = newPackage.getSecondaryDexPaths();
+ // Packages should be able to find their own dex files even if the notification about
+ // their installation is delayed.
+ notifyDexLoad(newPackage, newSecondaries, mUser0);
+
+ PackageUseInfo pui = getPackageUseInfo(newPackage);
+ assertNotNull(pui);
+ assertFalse(pui.isUsedByOtherApps());
+ assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
+ assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/false, mUser0);
+ }
+
private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId) {
for (String dex : secondaries) {
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimLayerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimLayerControllerTests.java
new file mode 100644
index 0000000..c3a471a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/DimLayerControllerTests.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 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.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.hardware.display.DisplayManagerGlobal;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for the {@link DimLayerController} class.
+ *
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.wm.DimLayerControllerTests
+ */
+@SmallTest
+@Presubmit
+@org.junit.runner.RunWith(AndroidJUnit4.class)
+public class DimLayerControllerTests extends WindowTestsBase {
+
+ /**
+ * This tests if shared fullscreen dim layer is added when stack is added to display
+ * and is removed when the only stack on the display is removed.
+ */
+ @Test
+ public void testSharedFullScreenDimLayer() throws Exception {
+ // Create a display.
+ final DisplayContent dc = createNewDisplay();
+ assertFalse(dc.mDimLayerController.hasSharedFullScreenDimLayer());
+
+ // Add stack with activity.
+ final TaskStack stack = createTaskStackOnDisplay(dc);
+ assertTrue(dc.mDimLayerController.hasDimLayerUser(stack));
+ assertTrue(dc.mDimLayerController.hasSharedFullScreenDimLayer());
+
+ // Remove the only stack on the display and check if the shared dim layer clears.
+ stack.removeImmediately();
+ assertFalse(dc.mDimLayerController.hasDimLayerUser(stack));
+ assertFalse(dc.mDimLayerController.hasSharedFullScreenDimLayer());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index e54e319..30f99e5 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -29,6 +29,7 @@
import java.util.ArrayList;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
@@ -215,13 +216,8 @@
*/
@Test
public void testMoveStackBetweenDisplays() throws Exception {
- // Create second display.
- final Display display = new Display(DisplayManagerGlobal.getInstance(),
- sDisplayContent.getDisplayId() + 1, new DisplayInfo(),
- DEFAULT_DISPLAY_ADJUSTMENTS);
- final DisplayContent dc = new DisplayContent(display, sWm, sLayersController,
- new WallpaperController(sWm));
- sWm.mRoot.addChild(dc, 1);
+ // Create a second display.
+ final DisplayContent dc = createNewDisplay();
// Add stack with activity.
final TaskStack stack = createTaskStackOnDisplay(dc);
@@ -261,10 +257,31 @@
// Check that override config is applied.
assertEquals(newOverrideConfig, sDisplayContent.getOverrideConfiguration());
+ }
+
+ /**
+ * This tests global configuration updates when default display config is updated.
+ */
+ @Test
+ public void testDefaultDisplayOverrideConfigUpdate() throws Exception {
+ final Configuration currentOverrideConfig = sDisplayContent.getOverrideConfiguration();
+
+ // Create new, slightly changed override configuration and apply it to the display.
+ final Configuration newOverrideConfig = new Configuration(currentOverrideConfig);
+ newOverrideConfig.densityDpi += 120;
+ newOverrideConfig.fontScale += 0.3;
+
+ sWm.setNewDisplayOverrideConfiguration(newOverrideConfig, DEFAULT_DISPLAY);
// Check that global configuration is updated, as we've updated default display's config.
- final Configuration globalConfig = sWm.mRoot.getConfiguration();
+ Configuration globalConfig = sWm.mRoot.getConfiguration();
assertEquals(newOverrideConfig.densityDpi, globalConfig.densityDpi);
assertEquals(newOverrideConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */);
+
+ // Return back to original values.
+ sWm.setNewDisplayOverrideConfiguration(currentOverrideConfig, DEFAULT_DISPLAY);
+ globalConfig = sWm.mRoot.getConfiguration();
+ assertEquals(currentOverrideConfig.densityDpi, globalConfig.densityDpi);
+ assertEquals(currentOverrideConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java
index 7a789d4..b0eba0b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java
@@ -102,12 +102,7 @@
task1.mOnDisplayChangedCalled = false;
// Create second display and put second stack on it.
- final Display display = new Display(DisplayManagerGlobal.getInstance(),
- sDisplayContent.getDisplayId() + 1, new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
- final DisplayContent dc = new DisplayContent(display, sWm, sLayersController,
- new WallpaperController(sWm));
- sWm.mRoot.addChild(dc, 1);
-
+ final DisplayContent dc = createNewDisplay();
final StackWindowController stack2Controller =
createStackControllerOnDisplay(dc);
final TaskStack stack2 = stack2Controller.mContainer;
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
index 3ee1da43..f79908e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
@@ -128,13 +128,10 @@
new TestTaskWindowContainerController(stack1Controller);
final TestTask task1 = (TestTask) taskController.mContainer;
task1.mOnDisplayChangedCalled = false;
+ assertEquals(sDisplayContent, stack1.getDisplayContent());
// Create second display and put second stack on it.
- final Display display = new Display(DisplayManagerGlobal.getInstance(),
- sDisplayContent.getDisplayId() + 1, new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
- final DisplayContent dc = new DisplayContent(display, sWm, sLayersController,
- new WallpaperController(sWm));
- sWm.mRoot.addChild(dc, 1);
+ final DisplayContent dc = createNewDisplay();
final StackWindowController stack2Controller = createStackControllerOnDisplay(dc);
final TaskStack stack2 = stack2Controller.mContainer;
final TestTaskWindowContainerController taskController2 =
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 72157b6..ca9dd9d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -20,7 +20,10 @@
import android.app.ActivityManagerInternal;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.hardware.display.DisplayManagerGlobal;
import android.os.Binder;
+import android.view.Display;
+import android.view.DisplayInfo;
import android.view.IApplicationToken;
import org.junit.Assert;
import org.junit.Before;
@@ -40,6 +43,7 @@
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.EMPTY;
+import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
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;
@@ -65,11 +69,13 @@
static TestWindowManagerPolicy sPolicy = null;
private final static IWindow sIWindow = new TestIWindow();
private final static Session sMockSession = mock(Session.class);
+ private static int sNextDisplayId = Display.DEFAULT_DISPLAY + 1;
static int sNextStackId = FIRST_DYNAMIC_STACK_ID;
private static int sNextTaskId = 0;
private static boolean sOneTimeSetupDone = false;
static DisplayContent sDisplayContent;
+ static DisplayInfo sDisplayInfo = new DisplayInfo();
static WindowLayersController sLayersController;
static WindowState sWallpaperWindow;
static WindowState sImeWindow;
@@ -100,9 +106,15 @@
if (sDisplayContent != null) {
sDisplayContent.removeImmediately();
}
- sDisplayContent = new DisplayContent(context.getDisplay(), sWm, sLayersController,
- new WallpaperController(sWm));
- sWm.mRoot.addChild(sDisplayContent, 0);
+ // Make sure that display ids don't overlap, so there won't be several displays with same
+ // ids among RootWindowContainer children.
+ for (DisplayContent dc : sWm.mRoot.mChildren) {
+ if (dc.getDisplayId() >= sNextDisplayId) {
+ sNextDisplayId = dc.getDisplayId() + 1;
+ }
+ }
+ context.getDisplay().getDisplayInfo(sDisplayInfo);
+ sDisplayContent = createNewDisplay();
sWm.mDisplayEnabled = true;
sWm.mDisplayReady = true;
@@ -187,7 +199,7 @@
true /* onTop */, new Rect(), sWm);
}
- /**Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
+ /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
static Task createTaskInStack(TaskStack stack, int userId) {
final Task newTask = new Task(sNextTaskId++, stack, userId, sWm, null, EMPTY, false, 0,
false, false, new TaskDescription(), null);
@@ -195,6 +207,14 @@
return newTask;
}
+ /** Creates a {@link DisplayContent} and adds it to the system. */
+ DisplayContent createNewDisplay() {
+ final int displayId = sNextDisplayId++;
+ final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
+ sDisplayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
+ return new DisplayContent(display, sWm, sLayersController, new WallpaperController(sWm));
+ }
+
/* Used so we can gain access to some protected members of the {@link WindowToken} class */
static class TestWindowToken extends WindowToken {
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 46b6403..2d7a68f 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -68,7 +68,6 @@
import android.os.SystemClock;
import android.provider.Settings;
import android.test.AndroidTestCase;
-import android.test.FlakyTest;
import android.test.mock.MockContentResolver;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
@@ -154,49 +153,32 @@
}
/**
- * A subclass of HandlerThread that allows callers to wait for it to become idle. waitForIdle
- * will return immediately if the handler is already idle.
+ * Block until the given handler becomes idle, or until timeoutMs has passed.
*/
- private class IdleableHandlerThread extends HandlerThread {
- private IdleHandler mIdleHandler;
-
- public IdleableHandlerThread(String name) {
- super(name);
- }
-
- public void waitForIdle(int timeoutMs) {
- final ConditionVariable cv = new ConditionVariable();
- final MessageQueue queue = getLooper().getQueue();
-
+ private static void waitForIdleHandler(HandlerThread handler, int timeoutMs) {
+ final ConditionVariable cv = new ConditionVariable();
+ final MessageQueue queue = handler.getLooper().getQueue();
+ final IdleHandler idleHandler = () -> {
synchronized (queue) {
- if (queue.isIdle()) {
- return;
- }
-
- assertNull("BUG: only one idle handler allowed", mIdleHandler);
- mIdleHandler = new IdleHandler() {
- public boolean queueIdle() {
- synchronized (queue) {
- cv.open();
- mIdleHandler = null;
- return false; // Remove the handler.
- }
- }
- };
- queue.addIdleHandler(mIdleHandler);
+ cv.open();
+ return false; // Remove the idleHandler.
}
-
- if (!cv.block(timeoutMs)) {
- fail("HandlerThread " + getName() +
- " did not become idle after " + timeoutMs + " ms");
- queue.removeIdleHandler(mIdleHandler);
+ };
+ synchronized (queue) {
+ if (queue.isIdle()) {
+ return;
}
+ queue.addIdleHandler(idleHandler);
+ }
+ if (!cv.block(timeoutMs)) {
+ fail("HandlerThread " + handler.getName() +
+ " did not become idle after " + timeoutMs + " ms");
+ queue.removeIdleHandler(idleHandler);
}
}
- // Tests that IdleableHandlerThread works as expected.
@SmallTest
- public void testIdleableHandlerThread() {
+ public void testWaitForIdle() {
final int attempts = 50; // Causes the test to take about 200ms on bullhead-eng.
// Tests that waitForIdle returns immediately if the service is already idle.
@@ -220,9 +202,9 @@
}
}
- @SmallTest
- @FlakyTest(tolerance = 3)
- public void testNotWaitingForIdleCausesRaceConditions() {
+ // This test has an inherent race condition in it, and cannot be enabled for continuous testing
+ // or presubmit tests. It is kept for manual runs and documentation purposes.
+ public void verifyThatNotWaitingForIdleCausesRaceConditions() {
// Bring up a network that we can use to send messages to ConnectivityService.
ConditionVariable cv = waitForConnectivityBroadcasts(1);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
@@ -249,7 +231,7 @@
private final WrappedNetworkMonitor mWrappedNetworkMonitor;
private final NetworkInfo mNetworkInfo;
private final NetworkCapabilities mNetworkCapabilities;
- private final IdleableHandlerThread mHandlerThread;
+ private final HandlerThread mHandlerThread;
private final ConditionVariable mDisconnected = new ConditionVariable();
private final ConditionVariable mNetworkStatusReceived = new ConditionVariable();
private final ConditionVariable mPreventReconnectReceived = new ConditionVariable();
@@ -281,7 +263,7 @@
default:
throw new UnsupportedOperationException("unimplemented network type");
}
- mHandlerThread = new IdleableHandlerThread("Mock-" + typeName);
+ mHandlerThread = new HandlerThread("Mock-" + typeName);
mHandlerThread.start();
mNetworkAgent = new NetworkAgent(mHandlerThread.getLooper(), mServiceContext,
"Mock-" + typeName, mNetworkInfo, mNetworkCapabilities,
@@ -321,7 +303,7 @@
}
public void waitForIdle(int timeoutMs) {
- mHandlerThread.waitForIdle(timeoutMs);
+ waitForIdleHandler(mHandlerThread, timeoutMs);
}
public void waitForIdle() {
@@ -648,11 +630,6 @@
}
@Override
- protected HandlerThread createHandlerThread() {
- return new IdleableHandlerThread("WrappedConnectivityService");
- }
-
- @Override
protected int getDefaultTcpRwnd() {
// Prevent wrapped ConnectivityService from trying to write to SystemProperties.
return 0;
@@ -710,7 +687,7 @@
}
public void waitForIdle(int timeoutMs) {
- ((IdleableHandlerThread) mHandlerThread).waitForIdle(timeoutMs);
+ waitForIdleHandler(mHandlerThread, timeoutMs);
}
public void waitForIdle() {
@@ -1135,7 +1112,7 @@
// Chosen to be much less than the linger timeout. This ensures that we can distinguish
// between a LOST callback that arrives immediately and a LOST callback that arrives after
// the linger timeout.
- private final static int TIMEOUT_MS = 50;
+ private final static int TIMEOUT_MS = 100;
private final LinkedBlockingQueue<CallbackInfo> mCallbacks = new LinkedBlockingQueue<>();
@@ -1487,8 +1464,8 @@
// Let linger run its course.
callback.assertNoCallback();
- callback.expectCallback(CallbackState.LOST, mCellNetworkAgent,
- TEST_LINGER_DELAY_MS /* timeoutMs */);
+ final int lingerTimeoutMs = TEST_LINGER_DELAY_MS + TEST_LINGER_DELAY_MS / 4;
+ callback.expectCallback(CallbackState.LOST, mCellNetworkAgent, lingerTimeoutMs);
// Clean up.
mWiFiNetworkAgent.disconnect();
@@ -1977,7 +1954,9 @@
assertTrue(isForegroundNetwork(mWiFiNetworkAgent));
// When lingering is complete, cell is still there but is now in the background.
- fgCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent, TEST_LINGER_DELAY_MS);
+ mService.waitForIdle();
+ int timeoutMs = TEST_LINGER_DELAY_MS + TEST_LINGER_DELAY_MS / 4;
+ fgCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent, timeoutMs);
callback.assertNoCallback();
assertFalse(isForegroundNetwork(mCellNetworkAgent));
assertTrue(isForegroundNetwork(mWiFiNetworkAgent));
@@ -2003,6 +1982,7 @@
// Disconnect wifi and check that cell is foreground again.
mWiFiNetworkAgent.disconnect();
+ mService.waitForIdle();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
fgCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
fgCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
@@ -2339,14 +2319,14 @@
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI).build();
final TestNetworkCallback networkCallback = new TestNetworkCallback();
- mCm.requestNetwork(nr, networkCallback, 10);
+ final int timeoutMs = 150;
+ mCm.requestNetwork(nr, networkCallback, timeoutMs);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- networkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ networkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent, timeoutMs);
// pass timeout and validate that UNAVAILABLE is not called
- sleepFor(15);
networkCallback.assertNoCallback();
}
@@ -2359,17 +2339,19 @@
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI).build();
final TestNetworkCallback networkCallback = new TestNetworkCallback();
- mCm.requestNetwork(nr, networkCallback, 500);
+ final int requestTimeoutMs = 100;
+ mCm.requestNetwork(nr, networkCallback, requestTimeoutMs);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- networkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ final int assertTimeoutMs = 150;
+ networkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent, assertTimeoutMs);
sleepFor(20);
mWiFiNetworkAgent.disconnect();
networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
// pass timeout and validate that UNAVAILABLE is not called
- sleepFor(600);
+ sleepFor(100);
networkCallback.assertNoCallback();
}
@@ -2383,7 +2365,8 @@
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI).build();
final TestNetworkCallback networkCallback = new TestNetworkCallback();
- mCm.requestNetwork(nr, networkCallback, 10);
+ final int timeoutMs = 10;
+ mCm.requestNetwork(nr, networkCallback, timeoutMs);
// pass timeout and validate that UNAVAILABLE is called
networkCallback.expectCallback(CallbackState.UNAVAILABLE, null);
@@ -2403,7 +2386,8 @@
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI).build();
final TestNetworkCallback networkCallback = new TestNetworkCallback();
- mCm.requestNetwork(nr, networkCallback, 10);
+ final int timeoutMs = 10;
+ mCm.requestNetwork(nr, networkCallback, timeoutMs);
// remove request
mCm.unregisterNetworkCallback(networkCallback);
@@ -2420,13 +2404,13 @@
networkCallback.assertNoCallback();
}
- public void assertEventuallyTrue(BooleanSupplier fn, long maxWaitingTimeMs) throws Exception {
+ public void assertEventuallyTrue(BooleanSupplier fn, long maxWaitingTimeMs) {
long start = SystemClock.elapsedRealtime();
while (SystemClock.elapsedRealtime() <= start + maxWaitingTimeMs) {
if (fn.getAsBoolean()) {
return;
}
- Thread.sleep(10);
+ sleepFor(15);
}
assertTrue(fn.getAsBoolean());
}
@@ -2594,7 +2578,9 @@
callback.expectError(PacketKeepalive.ERROR_INVALID_NETWORK);
// ... and that stopping it after that has no adverse effects.
- assertNull(mCm.getNetworkCapabilities(myNet));
+ // TODO: investigate assertEventuallyTrue is needed and waitForIdle() is not enough
+ final Network myNetAlias = myNet;
+ assertEventuallyTrue(() -> mCm.getNetworkCapabilities(myNetAlias) == null, 100);
ka.stop();
// Reconnect.
@@ -2838,11 +2824,11 @@
}
/* test utilities */
+ // TODO: eliminate all usages of sleepFor and replace by proper timeouts/waitForIdle.
static private void sleepFor(int ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
}
-
}
}
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index e410a9c..5028d47 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -142,7 +142,7 @@
private HashMap<String, String> mFields = new HashMap<String, String>();
private X509Certificate[] mCaCerts;
private PrivateKey mClientPrivateKey;
- private X509Certificate mClientCertificate;
+ private X509Certificate[] mClientCertificateChain;
private int mEapMethod = Eap.NONE;
private int mPhase2Method = Phase2.NONE;
@@ -161,9 +161,19 @@
for (String key : source.mFields.keySet()) {
mFields.put(key, source.mFields.get(key));
}
- mCaCerts = source.mCaCerts;
+ if (source.mCaCerts != null) {
+ mCaCerts = Arrays.copyOf(source.mCaCerts, source.mCaCerts.length);
+ } else {
+ mCaCerts = null;
+ }
mClientPrivateKey = source.mClientPrivateKey;
- mClientCertificate = source.mClientCertificate;
+ if (source.mClientCertificateChain != null) {
+ mClientCertificateChain = Arrays.copyOf(
+ source.mClientCertificateChain,
+ source.mClientCertificateChain.length);
+ } else {
+ mClientCertificateChain = null;
+ }
mEapMethod = source.mEapMethod;
mPhase2Method = source.mPhase2Method;
}
@@ -185,7 +195,7 @@
dest.writeInt(mPhase2Method);
ParcelUtil.writeCertificates(dest, mCaCerts);
ParcelUtil.writePrivateKey(dest, mClientPrivateKey);
- ParcelUtil.writeCertificate(dest, mClientCertificate);
+ ParcelUtil.writeCertificates(dest, mClientCertificateChain);
}
public static final Creator<WifiEnterpriseConfig> CREATOR =
@@ -204,7 +214,7 @@
enterpriseConfig.mPhase2Method = in.readInt();
enterpriseConfig.mCaCerts = ParcelUtil.readCertificates(in);
enterpriseConfig.mClientPrivateKey = ParcelUtil.readPrivateKey(in);
- enterpriseConfig.mClientCertificate = ParcelUtil.readCertificate(in);
+ enterpriseConfig.mClientCertificateChain = ParcelUtil.readCertificates(in);
return enterpriseConfig;
}
@@ -742,10 +752,51 @@
* @throws IllegalArgumentException for an invalid key or certificate.
*/
public void setClientKeyEntry(PrivateKey privateKey, X509Certificate clientCertificate) {
- if (clientCertificate != null) {
- if (clientCertificate.getBasicConstraints() != -1) {
- throw new IllegalArgumentException("Cannot be a CA certificate");
+ setClientKeyEntryWithCertificateChain(privateKey,
+ new X509Certificate[] {clientCertificate});
+ }
+
+ /**
+ * Specify a private key and client certificate chain for client authorization.
+ *
+ * <p>A default name is automatically assigned to the key entry and used
+ * with this configuration. The framework takes care of installing the
+ * key entry when the config is saved and removing the key entry when
+ * the config is removed.
+
+ * @param privateKey
+ * @param clientCertificateChain
+ * @throws IllegalArgumentException for an invalid key or certificate.
+ */
+ public void setClientKeyEntryWithCertificateChain(PrivateKey privateKey,
+ X509Certificate[] clientCertificateChain) {
+ X509Certificate[] newCerts = null;
+ if (clientCertificateChain != null && clientCertificateChain.length > 0) {
+ // We validate that this is a well formed chain that starts
+ // with an end-certificate and is followed by CA certificates.
+ // We don't validate that each following certificate verifies
+ // the previous. https://en.wikipedia.org/wiki/Chain_of_trust
+ //
+ // Basic constraints is an X.509 extension type that defines
+ // whether a given certificate is allowed to sign additional
+ // certificates and what path length restrictions may exist.
+ // We use this to judge whether the certificate is an end
+ // certificate or a CA certificate.
+ // https://cryptography.io/en/latest/x509/reference/
+ if (clientCertificateChain[0].getBasicConstraints() != -1) {
+ throw new IllegalArgumentException(
+ "First certificate in the chain must be a client end certificate");
}
+
+ for (int i = 1; i < clientCertificateChain.length; i++) {
+ if (clientCertificateChain[i].getBasicConstraints() == -1) {
+ throw new IllegalArgumentException(
+ "All certificates following the first must be CA certificates");
+ }
+ }
+ newCerts = Arrays.copyOf(clientCertificateChain,
+ clientCertificateChain.length);
+
if (privateKey == null) {
throw new IllegalArgumentException("Client cert without a private key");
}
@@ -755,7 +806,7 @@
}
mClientPrivateKey = privateKey;
- mClientCertificate = clientCertificate;
+ mClientCertificateChain = newCerts;
}
/**
@@ -764,7 +815,24 @@
* @return X.509 client certificate
*/
public X509Certificate getClientCertificate() {
- return mClientCertificate;
+ if (mClientCertificateChain != null && mClientCertificateChain.length > 0) {
+ return mClientCertificateChain[0];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the complete client certificate chain
+ *
+ * @return X.509 client certificates
+ */
+ @Nullable public X509Certificate[] getClientCertificateChain() {
+ if (mClientCertificateChain != null && mClientCertificateChain.length > 0) {
+ return mClientCertificateChain;
+ } else {
+ return null;
+ }
}
/**
@@ -772,7 +840,7 @@
*/
public void resetClientKeyEntry() {
mClientPrivateKey = null;
- mClientCertificate = null;
+ mClientCertificateChain = null;
}
/**
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 3b6e76f..ab725e2 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -120,11 +120,7 @@
public static final String PASSPOINT_ICON_RECEIVED_ACTION =
"android.net.wifi.PASSPOINT_ICON_RECEIVED";
/** @hide */
- public static final String EXTRA_PASSPOINT_ICON_BSSID = "bssid";
- /** @hide */
public static final String EXTRA_PASSPOINT_ICON_FILE = "file";
- /** @hide */
- public static final String EXTRA_PASSPOINT_ICON_DATA = "icon";
/**
* Broadcast intent action indicating that the a Passpoint release
@@ -159,6 +155,127 @@
public static final String EXTRA_PASSPOINT_WNM_DELAY = "delay";
/**
+ * Broadcast intent action indicating that a Passpoint provider icon has been received.
+ *
+ * Receiver Required Permission: android.Manifest.permission.ACCESS_WIFI_STATE
+ */
+ public static final String ACTION_PASSPOINT_ICON =
+ "android.net.wifi.action.PASSPOINT_ICON";
+ /**
+ * BSSID of the sender.
+ *
+ * Type: long
+ */
+ public static final String EXTRA_PASSPOINT_ICON_BSSID =
+ "android.net.wifi.extra.PASSPOINT_ICON_BSSID";
+ /**
+ * Filename of the icon.
+ *
+ * Type: String
+ */
+ public static final String EXTRA_PASSPOINT_ICON_FILENAME =
+ "android.net.wifi.extra.PASSPOINT_ICON_FILENAME";
+ /**
+ * Binary blob of the icon.
+ *
+ * Type: byte[]
+ */
+ public static final String EXTRA_PASSPOINT_ICON_DATA =
+ "android.net.wifi.extra.PASSPOINT_ICON_DATA";
+
+ /**
+ * Broadcast intent action indicating a Passpoint OSU Providers List element has been received.
+ *
+ * Receiver Required Permission: android.Manifest.permission.ACCESS_WIFI_STATE
+ */
+ public static final String ACTION_PASSPOINT_OSU_PROVIDERS_LIST =
+ "android.net.wifi.action.PASSPOINT_OSU_PROVIDERS_LIST";
+ /**
+ * BSSID of the sender.
+ *
+ * Type: long
+ */
+ public static final String EXTRA_PASSPOINT_OSU_PROVIDERS_LIST_BSSID =
+ "android.net.wifi.extra.PASSPOINT_OSU_PROVIDERS_LIST_BSSID";
+ /**
+ * Raw data of OSU Providers List ANQP element. Refer to Section 4.8 of Hotspot 2.0 Release 2
+ * Technical Specification for the exact data format.
+ *
+ * Type: byte[]
+ */
+ public static final String EXTRA_PASSPOINT_OSU_PROVIDERS_LIST_DATA =
+ "android.net.wifi.extra.PASSPOINT_OSU_PROVIDERS_LIST_DATA";
+
+ /**
+ * Broadcast intent action indicating that a Passpoint Deauth Imminent frame has been received.
+ *
+ * Receiver Required Permission: android.Manifest.permission.ACCESS_WIFI_STATE
+ */
+ public static final String ACTION_PASSPOINT_DEAUTH_IMMINENT =
+ "android.net.wifi.action.PASSPOINT_DEAUTH_IMMINENT";
+ /**
+ * The BSSID of the sender.
+ *
+ * Type: long
+ */
+ public static final String EXTRA_PASSPOINT_DEAUTH_IMMINENT_BSSID =
+ "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_BSSID";
+ /**
+ * Flag indicating failure at BSS (Basic Service Set) or ESS (Extended Service Set) level.
+ *
+ * Type: boolean
+ */
+ public static final String EXTRA_PASSPOINT_DEAUTH_IMMINENT_ESS =
+ "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_ESS";
+ /**
+ * Delay in seconds that a device shall wait before attempting re-association to the same BSS
+ * or ESS (as indicated by {@link #EXTRA_PASSPOINT_DEAUTH_IMMINENT_ESS}.
+ *
+ * Type: int
+ */
+ public static final String EXTRA_PASSPOINT_DEAUTH_IMMINENT_REAUTH_DELAY =
+ "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_REAUTH_DELAY";
+ /**
+ * URL that provides a webpage explaining the deauth reason.
+ *
+ * Type: String
+ */
+ public static final String EXTRA_PASSPOINT_DEAUTH_IMMINENT_REASON_URL =
+ "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_REASON_URL";
+
+ /**
+ * Broadcast intent action indicating a Passpoint subscription remediation frame has been
+ * received.
+ *
+ * Receiver Required Permission: android.Manifest.permission.ACCESS_WIFI_STATE
+ */
+ public static final String ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION =
+ "android.net.wifi.action.PASSPOINT_SUBSCRIPTION_REMEDIATION";
+ /**
+ * The BSSID of the sender.
+ *
+ * Type: long
+ */
+ public static final String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_BSSID =
+ "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_BSSID";
+ /**
+ * The protocol supported by the subscription remediation server. The possible values are:
+ * 0 - OMA DM
+ * 1 - SOAP XML SPP
+ *
+ * Type: int
+ */
+ public static final String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_METHOD =
+ "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_METHOD";
+ /**
+ * URL of the subscription remediation server.
+ *
+ * Type: String
+ */
+ public static final String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_URL =
+ "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_URL";
+
+ /**
* Broadcast intent action indicating that Wi-Fi has been enabled, disabled,
* enabling, disabling, or unknown. One extra provides this state as an int.
* Another extra provides the previous state, if available.
@@ -898,10 +1015,10 @@
}
/**
- * Query for a Hotspot 2.0 release 2 OSU icon
+ * Query for a Hotspot 2.0 release 2 OSU icon file.
+ *
* @param bssid The BSSID of the AP
- * @param fileName Icon file name
- * @hide
+ * @param fileName File name of the icon to query
*/
public void queryPasspointIcon(long bssid, String fileName) {
try {
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index 643753a..ca4d121 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -18,9 +18,19 @@
import android.net.wifi.hotspot2.pps.Credential;
import android.net.wifi.hotspot2.pps.HomeSP;
+import android.net.wifi.hotspot2.pps.Policy;
+import android.net.wifi.hotspot2.pps.UpdateParameter;
import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
import android.os.Parcel;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* Class representing Passpoint configuration. This contains configurations specified in
* PerProviderSubscription (PPS) Management Object (MO) tree.
@@ -28,13 +38,108 @@
* For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
* Release 2 Technical Specification.
*
- * Currently, only HomeSP and Credential subtrees are supported.
- *
* @hide
*/
public final class PasspointConfiguration implements Parcelable {
+ private static final String TAG = "PasspointConfiguration";
+
+ /**
+ * Number of bytes for certificate SHA-256 fingerprint byte array.
+ */
+ private static final int CERTIFICATE_SHA256_BYTES = 32;
+
+ /**
+ * Maximum bytes for URL string.
+ */
+ private static final int MAX_URL_BYTES = 1023;
+
+ /**
+ * Integer value used for indicating null value in the Parcel.
+ */
+ private static final int NULL_VALUE = -1;
+
public HomeSP homeSp = null;
public Credential credential = null;
+ public Policy policy = null;
+
+ /**
+ * Meta data for performing subscription update.
+ */
+ public UpdateParameter subscriptionUpdate = null;
+
+ /**
+ * List of HTTPS URL for retrieving trust root certificate and the corresponding SHA-256
+ * fingerprint of the certificate. The certificates are used for verifying AAA server's
+ * identity during EAP authentication.
+ */
+ public Map<String, byte[]> trustRootCertList = null;
+
+ /**
+ * Set by the subscription server, updated every time the configuration is updated by
+ * the subscription server.
+ *
+ * Use Integer.MIN_VALUE to indicate unset value.
+ */
+ public int updateIdentifier = Integer.MIN_VALUE;
+
+ /**
+ * The priority of the credential.
+ *
+ * Use Integer.MIN_VALUE to indicate unset value.
+ */
+ public int credentialPriority = Integer.MIN_VALUE;
+
+ /**
+ * The time this subscription is created. It is in the format of number
+ * of milliseconds since January 1, 1970, 00:00:00 GMT.
+ *
+ * Use Long.MIN_VALUE to indicate unset value.
+ */
+ public long subscriptionCreationTimeInMs = Long.MIN_VALUE;
+
+ /**
+ * The time this subscription will expire. It is in the format of number
+ * of milliseconds since January 1, 1970, 00:00:00 GMT.
+ *
+ * Use Long.MIN_VALUE to indicate unset value.
+ */
+ public long subscriptionExpirationTimeInMs = Long.MIN_VALUE;
+
+ /**
+ * The type of the subscription. This is defined by the provider and the value is provider
+ * specific.
+ */
+ public String subscriptionType = null;
+
+ /**
+ * The time period for usage statistics accumulation. A value of zero means that usage
+ * statistics are not accumulated on a periodic basis (e.g., a one-time limit for
+ * “pay as you go” - PAYG service). A non-zero value specifies the usage interval in minutes.
+ */
+ public long usageLimitUsageTimePeriodInMinutes = Long.MIN_VALUE;
+
+ /**
+ * The time at which usage statistic accumulation begins. It is in the format of number
+ * of milliseconds since January 1, 1970, 00:00:00 GMT.
+ *
+ * Use Long.MIN_VALUE to indicate unset value.
+ */
+ public long usageLimitStartTimeInMs = Long.MIN_VALUE;
+
+ /**
+ * The cumulative data limit in megabytes for the {@link #usageLimitUsageTimePeriodInMinutes}.
+ * A value of zero indicate unlimited data usage.
+ *
+ * Use Long.MIN_VALUE to indicate unset value.
+ */
+ public long usageLimitDataLimit = Long.MIN_VALUE;
+
+ /**
+ * The cumulative time limit in minutes for the {@link #usageLimitUsageTimePeriodInMinutes}.
+ * A value of zero indicate unlimited time usage.
+ */
+ public long usageLimitTimeLimitInMinutes = Long.MIN_VALUE;
+
/**
* Constructor for creating PasspointConfiguration with default values.
@@ -47,14 +152,34 @@
* @param source The source to copy from
*/
public PasspointConfiguration(PasspointConfiguration source) {
- if (source != null) {
- if (source.homeSp != null) {
- homeSp = new HomeSP(source.homeSp);
- }
- if (source.credential != null) {
- credential = new Credential(source.credential);
- }
+ if (source == null) {
+ return;
}
+
+ if (source.homeSp != null) {
+ homeSp = new HomeSP(source.homeSp);
+ }
+ if (source.credential != null) {
+ credential = new Credential(source.credential);
+ }
+ if (source.policy != null) {
+ policy = new Policy(source.policy);
+ }
+ if (source.trustRootCertList != null) {
+ trustRootCertList = Collections.unmodifiableMap(source.trustRootCertList);
+ }
+ if (source.subscriptionUpdate != null) {
+ subscriptionUpdate = new UpdateParameter(source.subscriptionUpdate);
+ }
+ updateIdentifier = source.updateIdentifier;
+ credentialPriority = source.credentialPriority;
+ subscriptionCreationTimeInMs = source.subscriptionCreationTimeInMs;
+ subscriptionExpirationTimeInMs = source.subscriptionExpirationTimeInMs;
+ subscriptionType = source.subscriptionType;
+ usageLimitDataLimit = source.usageLimitDataLimit;
+ usageLimitStartTimeInMs = source.usageLimitStartTimeInMs;
+ usageLimitTimeLimitInMinutes = source.usageLimitTimeLimitInMinutes;
+ usageLimitUsageTimePeriodInMinutes = source.usageLimitUsageTimePeriodInMinutes;
}
@Override
@@ -66,6 +191,18 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(homeSp, flags);
dest.writeParcelable(credential, flags);
+ dest.writeParcelable(policy, flags);
+ dest.writeParcelable(subscriptionUpdate, flags);
+ writeTrustRootCerts(dest, trustRootCertList);
+ dest.writeInt(updateIdentifier);
+ dest.writeInt(credentialPriority);
+ dest.writeLong(subscriptionCreationTimeInMs);
+ dest.writeLong(subscriptionExpirationTimeInMs);
+ dest.writeString(subscriptionType);
+ dest.writeLong(usageLimitUsageTimePeriodInMinutes);
+ dest.writeLong(usageLimitStartTimeInMs);
+ dest.writeLong(usageLimitDataLimit);
+ dest.writeLong(usageLimitTimeLimitInMinutes);
}
@Override
@@ -77,9 +214,22 @@
return false;
}
PasspointConfiguration that = (PasspointConfiguration) thatObject;
- return (homeSp == null ? that.homeSp == null : homeSp.equals(that.homeSp)) &&
- (credential == null ? that.credential == null :
- credential.equals(that.credential));
+ return (homeSp == null ? that.homeSp == null : homeSp.equals(that.homeSp))
+ && (credential == null ? that.credential == null
+ : credential.equals(that.credential))
+ && (policy == null) ? that.policy == null : policy.equals(that.policy)
+ && (subscriptionUpdate == null) ? that.subscriptionUpdate == null
+ : subscriptionUpdate.equals(that.subscriptionUpdate)
+ && isTrustRootCertListEquals(trustRootCertList, that.trustRootCertList)
+ && updateIdentifier == that.updateIdentifier
+ && credentialPriority == that.credentialPriority
+ && subscriptionCreationTimeInMs == that.subscriptionCreationTimeInMs
+ && subscriptionExpirationTimeInMs == that.subscriptionExpirationTimeInMs
+ && TextUtils.equals(subscriptionType, that.subscriptionType)
+ && usageLimitUsageTimePeriodInMinutes == that.usageLimitUsageTimePeriodInMinutes
+ && usageLimitStartTimeInMs == that.usageLimitStartTimeInMs
+ && usageLimitDataLimit == that.usageLimitDataLimit
+ && usageLimitTimeLimitInMinutes == that .usageLimitTimeLimitInMinutes;
}
/**
@@ -94,6 +244,37 @@
if (credential == null || !credential.validate()) {
return false;
}
+ if (policy != null && !policy.validate()) {
+ return false;
+ }
+ if (subscriptionUpdate != null && !subscriptionUpdate.validate()) {
+ return false;
+ }
+ if (trustRootCertList != null) {
+ for (Map.Entry<String, byte[]> entry : trustRootCertList.entrySet()) {
+ String url = entry.getKey();
+ byte[] certFingerprint = entry.getValue();
+ if (TextUtils.isEmpty(url)) {
+ Log.d(TAG, "Empty URL");
+ return false;
+ }
+ if (url.getBytes(StandardCharsets.UTF_8).length > MAX_URL_BYTES) {
+ Log.d(TAG, "URL bytes exceeded the max: "
+ + url.getBytes(StandardCharsets.UTF_8).length);
+ return false;
+ }
+
+ if (certFingerprint == null) {
+ Log.d(TAG, "Fingerprint not specified");
+ return false;
+ }
+ if (certFingerprint.length != CERTIFICATE_SHA256_BYTES) {
+ Log.d(TAG, "Incorrect size of trust root certificate SHA-256 fingerprint: "
+ + certFingerprint.length);
+ return false;
+ }
+ }
+ }
return true;
}
@@ -104,11 +285,88 @@
PasspointConfiguration config = new PasspointConfiguration();
config.homeSp = in.readParcelable(null);
config.credential = in.readParcelable(null);
+ config.policy = in.readParcelable(null);
+ config.subscriptionUpdate = in.readParcelable(null);
+ config.trustRootCertList = readTrustRootCerts(in);
+ config.updateIdentifier = in.readInt();
+ config.credentialPriority = in.readInt();
+ config.subscriptionCreationTimeInMs = in.readLong();
+ config.subscriptionExpirationTimeInMs = in.readLong();
+ config.subscriptionType = in.readString();
+ config.usageLimitUsageTimePeriodInMinutes = in.readLong();
+ config.usageLimitStartTimeInMs = in.readLong();
+ config.usageLimitDataLimit = in.readLong();
+ config.usageLimitTimeLimitInMinutes = in.readLong();
return config;
}
+
@Override
public PasspointConfiguration[] newArray(int size) {
return new PasspointConfiguration[size];
}
+
+ /**
+ * Helper function for reading trust root certificate info list from a Parcel.
+ *
+ * @param in The Parcel to read from
+ * @return The list of trust root certificate URL with the corresponding certificate
+ * fingerprint
+ */
+ private Map<String, byte[]> readTrustRootCerts(Parcel in) {
+ int size = in.readInt();
+ if (size == NULL_VALUE) {
+ return null;
+ }
+ Map<String, byte[]> trustRootCerts = new HashMap<>(size);
+ for (int i = 0; i < size; i++) {
+ String key = in.readString();
+ byte[] value = in.createByteArray();
+ trustRootCerts.put(key, value);
+ }
+ return trustRootCerts;
+ }
};
+
+ /**
+ * Helper function for writing trust root certificate information list.
+ *
+ * @param dest The Parcel to write to
+ * @param trustRootCerts The list of trust root certificate URL with the corresponding
+ * certificate fingerprint
+ */
+ private static void writeTrustRootCerts(Parcel dest, Map<String, byte[]> trustRootCerts) {
+ if (trustRootCerts == null) {
+ dest.writeInt(NULL_VALUE);
+ return;
+ }
+ dest.writeInt(trustRootCerts.size());
+ for (Map.Entry<String, byte[]> entry : trustRootCerts.entrySet()) {
+ dest.writeString(entry.getKey());
+ dest.writeByteArray(entry.getValue());
+ }
+ }
+
+ /**
+ * Helper function for comparing two trust root certificate list. Cannot use Map#equals
+ * method since the value type (byte[]) doesn't override equals method.
+ *
+ * @param list1 The first trust root certificate list
+ * @param list2 The second trust root certificate list
+ * @return true if the two list are equal
+ */
+ private static boolean isTrustRootCertListEquals(Map<String, byte[]> list1,
+ Map<String, byte[]> list2) {
+ if (list1 == null || list2 == null) {
+ return list1 == list2;
+ }
+ if (list1.size() != list2.size()) {
+ return false;
+ }
+ for (Map.Entry<String, byte[]> entry : list1.entrySet()) {
+ if (!Arrays.equals(entry.getValue(), list2.get(entry.getKey()))) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java b/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java
index 98fd0f3..22b0f97 100644
--- a/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java
+++ b/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java
@@ -19,6 +19,8 @@
import android.net.wifi.hotspot2.PasspointConfiguration;
import android.net.wifi.hotspot2.pps.Credential;
import android.net.wifi.hotspot2.pps.HomeSP;
+import android.net.wifi.hotspot2.pps.Policy;
+import android.net.wifi.hotspot2.pps.UpdateParameter;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -131,6 +133,20 @@
private static final String NODE_PER_PROVIDER_SUBSCRIPTION = "PerProviderSubscription";
/**
+ * Fields under PerProviderSubscription.
+ */
+ private static final String NODE_UPDATE_IDENTIFIER = "UpdateIdentifier";
+ private static final String NODE_AAA_SERVER_TRUST_ROOT = "AAAServerTrustRoot";
+ private static final String NODE_SUBSCRIPTION_UPDATE = "SubscriptionUpdate";
+ private static final String NODE_SUBSCRIPTION_PARAMETER = "SubscriptionParameter";
+ private static final String NODE_TYPE_OF_SUBSCRIPTION = "TypeOfSubscription";
+ private static final String NODE_USAGE_LIMITS = "UsageLimits";
+ private static final String NODE_DATA_LIMIT = "DataLimit";
+ private static final String NODE_START_DATE = "StartDate";
+ private static final String NODE_TIME_LIMIT = "TimeLimit";
+ private static final String NODE_USAGE_TIME_PERIOD = "UsageTimePeriod";
+ private static final String NODE_CREDENTIAL_PRIORITY = "CredentialPriority";
+ /**
* Fields under HomeSP subtree.
*/
private static final String NODE_HOMESP = "HomeSP";
@@ -168,13 +184,40 @@
private static final String NODE_INNER_METHOD = "InnerMethod";
private static final String NODE_DIGITAL_CERTIFICATE = "DigitalCertificate";
private static final String NODE_CERTIFICATE_TYPE = "CertificateType";
- private static final String NODE_CERT_SHA256_FINGERPRINT = "CertSHA256FingerPrint";
+ private static final String NODE_CERT_SHA256_FINGERPRINT = "CertSHA256Fingerprint";
private static final String NODE_REALM = "Realm";
private static final String NODE_SIM = "SIM";
private static final String NODE_SIM_IMSI = "IMSI";
private static final String NODE_CHECK_AAA_SERVER_CERT_STATUS = "CheckAAAServerCertStatus";
/**
+ * Fields under Policy subtree.
+ */
+ private static final String NODE_POLICY = "Policy";
+ private static final String NODE_PREFERRED_ROAMING_PARTNER_LIST =
+ "PreferredRoamingPartnerList";
+ private static final String NODE_FQDN_MATCH = "FQDN_Match";
+ private static final String NODE_PRIORITY = "Priority";
+ private static final String NODE_COUNTRY = "Country";
+ private static final String NODE_MIN_BACKHAUL_THRESHOLD = "MinBackhaulThreshold";
+ private static final String NODE_NETWORK_TYPE = "NetworkType";
+ private static final String NODE_DOWNLINK_BANDWIDTH = "DLBandwidth";
+ private static final String NODE_UPLINK_BANDWIDTH = "ULBandwidth";
+ private static final String NODE_POLICY_UPDATE = "PolicyUpdate";
+ private static final String NODE_UPDATE_INTERVAL = "UpdateInterval";
+ private static final String NODE_UPDATE_METHOD = "UpdateMethod";
+ private static final String NODE_RESTRICTION = "Restriction";
+ private static final String NODE_URI = "URI";
+ private static final String NODE_TRUST_ROOT = "TrustRoot";
+ private static final String NODE_CERT_URL = "CertURL";
+ private static final String NODE_SP_EXCLUSION_LIST = "SPExclusionList";
+ private static final String NODE_REQUIRED_PROTO_PORT_TUPLE = "RequiredProtoPortTuple";
+ private static final String NODE_IP_PROTOCOL = "IPProtocol";
+ private static final String NODE_PORT_NUMBER = "PortNumber";
+ private static final String NODE_MAXIMUM_BSS_LOAD_VALUE = "MaximumBSSLoadValue";
+ private static final String NODE_OTHER = "Other";
+
+ /**
* URN (Unique Resource Name) for PerProviderSubscription Management Object Tree.
*/
private static final String PPS_MO_URN =
@@ -349,6 +392,10 @@
* ...
* </RTPProperties>
* <Node>
+ * <NodeName>UpdateIdentifier</NodeName>
+ * <Value>...</Value>
+ * </Node>
+ * <Node>
* ...
* </Node>
* </Node>
@@ -361,11 +408,12 @@
throws ParsingException {
PasspointConfiguration config = null;
String nodeName = null;
+ int updateIdentifier = Integer.MIN_VALUE;
for (XMLNode child : node.getChildren()) {
switch (child.getTag()) {
case TAG_NODE_NAME:
if (nodeName != null) {
- throw new ParsingException("Duplicant NodeName: " + child.getText());
+ throw new ParsingException("Duplicate NodeName: " + child.getText());
}
nodeName = child.getText();
if (!TextUtils.equals(nodeName, NODE_PER_PROVIDER_SUBSCRIPTION)) {
@@ -373,13 +421,22 @@
}
break;
case TAG_NODE:
- // Only one PerProviderSubscription instance is expected and allowed.
- if (config != null) {
- throw new ParsingException("Multiple PPS instance");
+ // A node can be either an UpdateIdentifier node or a PerProviderSubscription
+ // instance node. Flatten out the XML tree first by converting it to a PPS
+ // tree to reduce the complexity of the parsing code.
+ PPSNode ppsNodeRoot = buildPpsNode(child);
+ if (TextUtils.equals(ppsNodeRoot.getName(), NODE_UPDATE_IDENTIFIER)) {
+ if (updateIdentifier != Integer.MIN_VALUE) {
+ throw new ParsingException("Multiple node for UpdateIdentifier");
+ }
+ updateIdentifier = parseInteger(getPpsNodeValue(ppsNodeRoot));
+ } else {
+ // Only one PerProviderSubscription instance is expected and allowed.
+ if (config != null) {
+ throw new ParsingException("Multiple PPS instance");
+ }
+ config = parsePpsInstance(ppsNodeRoot);
}
- // Convert the XML tree to a PPS tree.
- PPSNode ppsInstanceRoot = buildPpsNode(child);
- config = parsePpsInstance(ppsInstanceRoot);
break;
case TAG_RT_PROPERTIES:
// Parse and verify URN stored in the RT (Run Time) Properties.
@@ -392,6 +449,9 @@
throw new ParsingException("Unknown tag under PPS node: " + child.getTag());
}
}
+ if (config != null && updateIdentifier != Integer.MIN_VALUE) {
+ config.updateIdentifier = updateIdentifier;
+ }
return config;
}
@@ -551,6 +611,21 @@
case NODE_CREDENTIAL:
config.credential = parseCredential(child);
break;
+ case NODE_POLICY:
+ config.policy = parsePolicy(child);
+ break;
+ case NODE_AAA_SERVER_TRUST_ROOT:
+ config.trustRootCertList = parseAAAServerTrustRootList(child);
+ break;
+ case NODE_SUBSCRIPTION_UPDATE:
+ config.subscriptionUpdate = parseUpdateParameter(child);
+ break;
+ case NODE_SUBSCRIPTION_PARAMETER:
+ parseSubscriptionParameter(child, config);
+ break;
+ case NODE_CREDENTIAL_PRIORITY:
+ config.credentialPriority = parseInteger(getPpsNodeValue(child));
+ break;
default:
throw new ParsingException("Unknown node: " + child.getName());
}
@@ -616,11 +691,7 @@
String[] oiStrArray = oiStr.split(",");
long[] oiArray = new long[oiStrArray.length];
for (int i = 0; i < oiStrArray.length; i++) {
- try {
- oiArray[i] = Long.parseLong(oiStrArray[i], 16);
- } catch (NumberFormatException e) {
- throw new ParsingException("Invalid OI: " + oiStrArray[i]);
- }
+ oiArray[i] = parseLong(oiStrArray[i], 16);
}
return oiArray;
}
@@ -671,11 +742,7 @@
ssid = getPpsNodeValue(child);
break;
case NODE_HESSID:
- try {
- hessid = Long.parseLong(getPpsNodeValue(child), 16);
- } catch (NumberFormatException e) {
- throw new ParsingException("Invalid HESSID: " + getPpsNodeValue(child));
- }
+ hessid = parseLong(getPpsNodeValue(child), 16);
break;
default:
throw new ParsingException("Unknown node under NetworkID instance: " +
@@ -999,6 +1066,503 @@
}
/**
+ * Parse configurations under PerProviderSubscription/Policy subtree.
+ *
+ * @param node PPSNode representing the root of the PerProviderSubscription/Policy subtree
+ * @return {@link Policy}
+ * @throws ParsingException
+ */
+ private static Policy parsePolicy(PPSNode node) throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for Policy");
+ }
+
+ Policy policy = new Policy();
+ for (PPSNode child : node.getChildren()) {
+ switch (child.getName()) {
+ case NODE_PREFERRED_ROAMING_PARTNER_LIST:
+ policy.preferredRoamingPartnerList = parsePreferredRoamingPartnerList(child);
+ break;
+ case NODE_MIN_BACKHAUL_THRESHOLD:
+ parseMinBackhaulThreshold(child, policy);
+ break;
+ case NODE_POLICY_UPDATE:
+ policy.policyUpdate = parseUpdateParameter(child);
+ break;
+ case NODE_SP_EXCLUSION_LIST:
+ policy.excludedSsidList = parseSpExclusionList(child);
+ break;
+ case NODE_REQUIRED_PROTO_PORT_TUPLE:
+ policy.requiredProtoPortMap = parseRequiredProtoPortTuple(child);
+ break;
+ case NODE_MAXIMUM_BSS_LOAD_VALUE:
+ policy.maximumBssLoadValue = parseInteger(getPpsNodeValue(child));
+ break;
+ default:
+ throw new ParsingException("Unknown node under Policy: " + child.getName());
+ }
+ }
+ return policy;
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/Policy/PreferredRoamingPartnerList
+ * subtree.
+ *
+ * @param node PPSNode representing the root of the
+ * PerProviderSubscription/Policy/PreferredRoamingPartnerList subtree
+ * @return List of {@link Policy#RoamingPartner}
+ * @throws ParsingException
+ */
+ private static List<Policy.RoamingPartner> parsePreferredRoamingPartnerList(PPSNode node)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for PreferredRoamingPartnerList");
+ }
+ List<Policy.RoamingPartner> partnerList = new ArrayList<>();
+ for (PPSNode child : node.getChildren()) {
+ partnerList.add(parsePreferredRoamingPartner(child));
+ }
+ return partnerList;
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/Policy/PreferredRoamingPartnerList/<X+>
+ * subtree.
+ *
+ * @param node PPSNode representing the root of the
+ * PerProviderSubscription/Policy/PreferredRoamingPartnerList/<X+> subtree
+ * @return {@link Policy#RoamingPartner}
+ * @throws ParsingException
+ */
+ private static Policy.RoamingPartner parsePreferredRoamingPartner(PPSNode node)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for PreferredRoamingPartner "
+ + "instance");
+ }
+
+ Policy.RoamingPartner roamingPartner = new Policy.RoamingPartner();
+ for (PPSNode child : node.getChildren()) {
+ switch (child.getName()) {
+ case NODE_FQDN_MATCH:
+ // FQDN_Match field is in the format of "[FQDN],[MatchInfo]", where [MatchInfo]
+ // is either "exactMatch" for exact match of FQDN or "includeSubdomains" for
+ // matching all FQDNs with the same sub-domain.
+ String fqdnMatch = getPpsNodeValue(child);
+ String[] fqdnMatchArray = fqdnMatch.split(",");
+ if (fqdnMatchArray.length != 2) {
+ throw new ParsingException("Invalid FQDN_Match: " + fqdnMatch);
+ }
+ roamingPartner.fqdn = fqdnMatchArray[0];
+ if (TextUtils.equals(fqdnMatchArray[1], "exactMatch")) {
+ roamingPartner.fqdnExactMatch = true;
+ } else if (TextUtils.equals(fqdnMatchArray[1], "includeSubdomains")) {
+ roamingPartner.fqdnExactMatch = false;
+ } else {
+ throw new ParsingException("Invalid FQDN_Match: " + fqdnMatch);
+ }
+ break;
+ case NODE_PRIORITY:
+ roamingPartner.priority = parseInteger(getPpsNodeValue(child));
+ break;
+ case NODE_COUNTRY:
+ roamingPartner.countries = getPpsNodeValue(child);
+ break;
+ default:
+ throw new ParsingException("Unknown node under PreferredRoamingPartnerList "
+ + "instance " + child.getName());
+ }
+ }
+ return roamingPartner;
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/Policy/MinBackhaulThreshold subtree
+ * into the given policy.
+ *
+ * @param node PPSNode representing the root of the
+ * PerProviderSubscription/Policy/MinBackhaulThreshold subtree
+ * @param policy The policy to store the MinBackhualThreshold configuration
+ * @throws ParsingException
+ */
+ private static void parseMinBackhaulThreshold(PPSNode node, Policy policy)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for MinBackhaulThreshold");
+ }
+ for (PPSNode child : node.getChildren()) {
+ parseMinBackhaulThresholdInstance(child, policy);
+ }
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/Policy/MinBackhaulThreshold/<X+> subtree
+ * into the given policy.
+ *
+ * @param node PPSNode representing the root of the
+ * PerProviderSubscription/Policy/MinBackhaulThreshold/<X+> subtree
+ * @param policy The policy to store the MinBackhaulThreshold configuration
+ * @throws ParsingException
+ */
+ private static void parseMinBackhaulThresholdInstance(PPSNode node, Policy policy)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for MinBackhaulThreshold instance");
+ }
+ String networkType = null;
+ long downlinkBandwidth = Long.MIN_VALUE;
+ long uplinkBandwidth = Long.MIN_VALUE;
+ for (PPSNode child : node.getChildren()) {
+ switch (child.getName()) {
+ case NODE_NETWORK_TYPE:
+ networkType = getPpsNodeValue(child);
+ break;
+ case NODE_DOWNLINK_BANDWIDTH:
+ downlinkBandwidth = parseLong(getPpsNodeValue(child), 10);
+ break;
+ case NODE_UPLINK_BANDWIDTH:
+ uplinkBandwidth = parseLong(getPpsNodeValue(child), 10);
+ break;
+ default:
+ throw new ParsingException("Unknown node under MinBackhaulThreshold instance "
+ + child.getName());
+ }
+ }
+ if (networkType == null) {
+ throw new ParsingException("Missing NetworkType field");
+ }
+
+ if (TextUtils.equals(networkType, "home")) {
+ policy.minHomeDownlinkBandwidth = downlinkBandwidth;
+ policy.minHomeUplinkBandwidth = uplinkBandwidth;
+ } else if (TextUtils.equals(networkType, "roaming")) {
+ policy.minRoamingDownlinkBandwidth = downlinkBandwidth;
+ policy.minRoamingUplinkBandwidth = uplinkBandwidth;
+ } else {
+ throw new ParsingException("Invalid network type: " + networkType);
+ }
+ }
+
+ /**
+ * Parse update parameters. This contained configurations from either
+ * PerProviderSubscription/Policy/PolicyUpdate or PerProviderSubscription/SubscriptionUpdate
+ * subtree.
+ *
+ * @param node PPSNode representing the root of the PerProviderSubscription/Policy/PolicyUpdate
+ * or PerProviderSubscription/SubscriptionUpdate subtree
+ * @return {@link UpdateParameter}
+ * @throws ParsingException
+ */
+ private static UpdateParameter parseUpdateParameter(PPSNode node)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for Update Parameters");
+ }
+
+ UpdateParameter updateParam = new UpdateParameter();
+ for (PPSNode child : node.getChildren()) {
+ switch(child.getName()) {
+ case NODE_UPDATE_INTERVAL:
+ updateParam.updateIntervalInMinutes = parseLong(getPpsNodeValue(child), 10);
+ break;
+ case NODE_UPDATE_METHOD:
+ updateParam.updateMethod = getPpsNodeValue(child);
+ break;
+ case NODE_RESTRICTION:
+ updateParam.restriction = getPpsNodeValue(child);
+ break;
+ case NODE_URI:
+ updateParam.serverUri = getPpsNodeValue(child);
+ break;
+ case NODE_USERNAME_PASSWORD:
+ Pair<String, String> usernamePassword = parseUpdateUserCredential(child);
+ updateParam.username = usernamePassword.first;
+ updateParam.base64EncodedPassword = usernamePassword.second;
+ break;
+ case NODE_TRUST_ROOT:
+ Pair<String, byte[]> trustRoot = parseTrustRoot(child);
+ updateParam.trustRootCertUrl = trustRoot.first;
+ updateParam.trustRootCertSha256Fingerprint = trustRoot.second;
+ break;
+ case NODE_OTHER:
+ Log.d(TAG, "Ignore unsupported paramter: " + child.getName());
+ break;
+ default:
+ throw new ParsingException("Unknown node under Update Parameters: "
+ + child.getName());
+ }
+ }
+ return updateParam;
+ }
+
+ /**
+ * Parse username and password parameters associated with policy or subscription update.
+ * This contained configurations under either
+ * PerProviderSubscription/Policy/PolicyUpdate/UsernamePassword or
+ * PerProviderSubscription/SubscriptionUpdate/UsernamePassword subtree.
+ *
+ * @param node PPSNode representing the root of the UsernamePassword subtree
+ * @return Pair of username and password
+ * @throws ParsingException
+ */
+ private static Pair<String, String> parseUpdateUserCredential(PPSNode node)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for UsernamePassword");
+ }
+
+ String username = null;
+ String password = null;
+ for (PPSNode child : node.getChildren()) {
+ switch (child.getName()) {
+ case NODE_USERNAME:
+ username = getPpsNodeValue(child);
+ break;
+ case NODE_PASSWORD:
+ password = getPpsNodeValue(child);
+ break;
+ default:
+ throw new ParsingException("Unknown node under UsernamePassword: "
+ + child.getName());
+ }
+ }
+ return Pair.create(username, password);
+ }
+
+ /**
+ * Parse the trust root parameters associated with policy update, subscription update, or AAA
+ * server trust root.
+ *
+ * This contained configurations under either
+ * PerProviderSubscription/Policy/PolicyUpdate/TrustRoot or
+ * PerProviderSubscription/SubscriptionUpdate/TrustRoot or
+ * PerProviderSubscription/AAAServerTrustRoot/<X+> subtree.
+ *
+ * @param node PPSNode representing the root of the TrustRoot subtree
+ * @return Pair of Certificate URL and fingerprint
+ * @throws ParsingException
+ */
+ private static Pair<String, byte[]> parseTrustRoot(PPSNode node)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for TrustRoot");
+ }
+
+ String certUrl = null;
+ byte[] certFingerprint = null;
+ for (PPSNode child : node.getChildren()) {
+ switch (child.getName()) {
+ case NODE_CERT_URL:
+ certUrl = getPpsNodeValue(child);
+ break;
+ case NODE_CERT_SHA256_FINGERPRINT:
+ certFingerprint = parseHexString(getPpsNodeValue(child));
+ break;
+ default:
+ throw new ParsingException("Unknown node under TrustRoot: "
+ + child.getName());
+ }
+ }
+ return Pair.create(certUrl, certFingerprint);
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/Policy/SPExclusionList subtree.
+ *
+ * @param node PPSNode representing the root of the
+ * PerProviderSubscription/Policy/SPExclusionList subtree
+ * @return Array of excluded SSIDs
+ * @throws ParsingException
+ */
+ private static String[] parseSpExclusionList(PPSNode node) throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for SPExclusionList");
+ }
+ List<String> ssidList = new ArrayList<>();
+ for (PPSNode child : node.getChildren()) {
+ ssidList.add(parseSpExclusionInstance(child));
+ }
+ return ssidList.toArray(new String[ssidList.size()]);
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/Policy/SPExclusionList/<X+> subtree.
+ *
+ * @param node PPSNode representing the root of the
+ * PerProviderSubscription/Policy/SPExclusionList/<X+> subtree
+ * @return String
+ * @throws ParsingException
+ */
+ private static String parseSpExclusionInstance(PPSNode node) throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for SPExclusion instance");
+ }
+ String ssid = null;
+ for (PPSNode child : node.getChildren()) {
+ switch (child.getName()) {
+ case NODE_SSID:
+ ssid = getPpsNodeValue(child);
+ break;
+ default:
+ throw new ParsingException("Unknown node under SPExclusion instance");
+ }
+ }
+ return ssid;
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/Policy/RequiredProtoPortTuple subtree.
+ *
+ * @param node PPSNode representing the root of the
+ * PerProviderSubscription/Policy/RequiredProtoPortTuple subtree
+ * @return Map of IP Protocol to Port Number tuples
+ * @throws ParsingException
+ */
+ private static Map<Integer, String> parseRequiredProtoPortTuple(PPSNode node)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for RequiredProtoPortTuple");
+ }
+ Map<Integer, String> protoPortTupleMap = new HashMap<>();
+ for (PPSNode child : node.getChildren()) {
+ Pair<Integer, String> protoPortTuple = parseProtoPortTuple(child);
+ protoPortTupleMap.put(protoPortTuple.first, protoPortTuple.second);
+ }
+ return protoPortTupleMap;
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/Policy/RequiredProtoPortTuple/<X+>
+ * subtree.
+ *
+ * @param node PPSNode representing the root of the
+ * PerProviderSubscription/Policy/RequiredProtoPortTuple/<X+> subtree
+ * @return Pair of IP Protocol to Port Number tuple
+ * @throws ParsingException
+ */
+ private static Pair<Integer, String> parseProtoPortTuple(PPSNode node)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for RequiredProtoPortTuple "
+ + "instance");
+ }
+ int proto = Integer.MIN_VALUE;
+ String ports = null;
+ for (PPSNode child : node.getChildren()) {
+ switch (child.getName()) {
+ case NODE_IP_PROTOCOL:
+ proto = parseInteger(getPpsNodeValue(child));
+ break;
+ case NODE_PORT_NUMBER:
+ ports = getPpsNodeValue(child);
+ break;
+ default:
+ throw new ParsingException("Unknown node under RequiredProtoPortTuple instance"
+ + child.getName());
+ }
+ }
+ if (proto == Integer.MIN_VALUE) {
+ throw new ParsingException("Missing IPProtocol field");
+ }
+ if (ports == null) {
+ throw new ParsingException("Missing PortNumber field");
+ }
+ return Pair.create(proto, ports);
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/AAAServerTrustRoot subtree.
+ *
+ * @param node PPSNode representing the root of PerProviderSubscription/AAAServerTrustRoot
+ * subtree
+ * @return Map of certificate URL with the corresponding certificate fingerprint
+ * @throws ParsingException
+ */
+ private static Map<String, byte[]> parseAAAServerTrustRootList(PPSNode node)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for AAAServerTrustRoot");
+ }
+ Map<String, byte[]> certList = new HashMap<>();
+ for (PPSNode child : node.getChildren()) {
+ Pair<String, byte[]> certTuple = parseTrustRoot(child);
+ certList.put(certTuple.first, certTuple.second);
+ }
+ return certList;
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/SubscriptionParameter subtree.
+ *
+ * @param node PPSNode representing the root of PerProviderSubscription/SubscriptionParameter
+ * subtree
+ * @param config Instance of {@link PasspointConfiguration}
+ * @throws ParsingException
+ */
+ private static void parseSubscriptionParameter(PPSNode node, PasspointConfiguration config)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for SubscriptionParameter");
+ }
+ for (PPSNode child : node.getChildren()) {
+ switch (child.getName()) {
+ case NODE_CREATION_DATE:
+ config.subscriptionCreationTimeInMs = parseDate(getPpsNodeValue(child));
+ break;
+ case NODE_EXPIRATION_DATE:
+ config.subscriptionExpirationTimeInMs = parseDate(getPpsNodeValue(child));
+ break;
+ case NODE_TYPE_OF_SUBSCRIPTION:
+ config.subscriptionType = getPpsNodeValue(child);
+ break;
+ case NODE_USAGE_LIMITS:
+ parseUsageLimits(child, config);
+ break;
+ default:
+ throw new ParsingException("Unknown node under SubscriptionParameter"
+ + child.getName());
+ }
+ }
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/SubscriptionParameter/UsageLimits
+ * subtree.
+ *
+ * @param node PPSNode representing the root of
+ * PerProviderSubscription/SubscriptionParameter/UsageLimits subtree
+ * @param config Instance of {@link PasspointConfiguration}
+ * @throws ParsingException
+ */
+ private static void parseUsageLimits(PPSNode node, PasspointConfiguration config)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for UsageLimits");
+ }
+ for (PPSNode child : node.getChildren()) {
+ switch (child.getName()) {
+ case NODE_DATA_LIMIT:
+ config.usageLimitDataLimit = parseLong(getPpsNodeValue(child), 10);
+ break;
+ case NODE_START_DATE:
+ config.usageLimitStartTimeInMs = parseDate(getPpsNodeValue(child));
+ break;
+ case NODE_TIME_LIMIT:
+ config.usageLimitTimeLimitInMinutes = parseLong(getPpsNodeValue(child), 10);
+ break;
+ case NODE_USAGE_TIME_PERIOD:
+ config.usageLimitUsageTimePeriodInMinutes =
+ parseLong(getPpsNodeValue(child), 10);
+ break;
+ default:
+ throw new ParsingException("Unknown node under UsageLimits"
+ + child.getName());
+ }
+ }
+ }
+
+ /**
* Convert a hex string to a byte array.
*
* @param str String containing hex values
@@ -1054,6 +1618,21 @@
}
/**
+ * Parse a string representing a long integer.
+ *
+ * @param value String of long integer value
+ * @return long
+ * @throws ParsingException
+ */
+ private static long parseLong(String value, int radix) throws ParsingException {
+ try {
+ return Long.parseLong(value, radix);
+ } catch (NumberFormatException e) {
+ throw new ParsingException("Invalid long integer value: " + value);
+ }
+ }
+
+ /**
* Convert a List<Long> to a primitive long array long[].
*
* @param list List to be converted
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Policy.aidl b/wifi/java/android/net/wifi/hotspot2/pps/Policy.aidl
new file mode 100644
index 0000000..e923f1f
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Policy.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2017, 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.pps;
+
+parcelable Policy;
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Policy.java b/wifi/java/android/net/wifi/hotspot2/pps/Policy.java
new file mode 100644
index 0000000..b2583d3
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Policy.java
@@ -0,0 +1,452 @@
+/**
+ * Copyright (c) 2017, 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.pps;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Class representing Policy subtree in PerProviderSubscription (PPS)
+ * Management Object (MO) tree.
+ *
+ * The Policy specifies additional criteria for Passpoint network selections, such as preferred
+ * roaming partner, minimum backhaul bandwidth, and etc. It also provides the meta data for
+ * updating the policy.
+ *
+ * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
+ * Release 2 Technical Specification.
+ *
+ * @hide
+ */
+public final class Policy implements Parcelable {
+ private static final String TAG = "Policy";
+
+ /**
+ * Default priority for preferred roaming partner.
+ */
+ public static final int PREFERRED_ROAMING_PARTNER_DEFAULT_PRIORITY = 128;
+
+ /**
+ * Maximum number of SSIDs in the exclusion list.
+ */
+ private static final int MAX_EXCLUSION_SSIDS = 128;
+
+ /**
+ * Maximum byte for SSID.
+ */
+ private static final int MAX_SSID_BYTES = 32;
+
+ /**
+ * Maximum bytes for port string in {@link #requiredProtoPortMap}.
+ */
+ private static final int MAX_PORT_STRING_BYTES = 64;
+
+ /**
+ * Integer value used for indicating null value in the Parcel.
+ */
+ private static final int NULL_VALUE = -1;
+
+ /**
+ * Minimum available downlink/uplink bandwidth (in kilobits per second) required when
+ * selecting a network from home providers.
+ *
+ * The bandwidth is calculated as the LinkSpeed * (1 – LinkLoad/255), where LinkSpeed
+ * and LinkLoad parameters are drawn from the WAN Metrics ANQP element at that hotspot.
+ *
+ * Using Long.MIN_VALUE to indicate unset value.
+ */
+ public long minHomeDownlinkBandwidth = Long.MIN_VALUE;
+ public long minHomeUplinkBandwidth = Long.MIN_VALUE;
+
+ /**
+ * Minimum available downlink/uplink bandwidth (in kilobits per second) required when
+ * selecting a network from roaming providers.
+ *
+ * The bandwidth is calculated as the LinkSpeed * (1 – LinkLoad/255), where LinkSpeed
+ * and LinkLoad parameters are drawn from the WAN Metrics ANQP element at that hotspot.
+ *
+ * Using Long.MIN_VALUE to indicate unset value.
+ */
+ public long minRoamingDownlinkBandwidth = Long.MIN_VALUE;
+ public long minRoamingUplinkBandwidth = Long.MIN_VALUE;
+
+ /**
+ * List of SSIDs that are not preferred by the Home SP.
+ */
+ public String[] excludedSsidList = null;
+
+ /**
+ * List of IP protocol and port number required by one or more operator supported application.
+ * The port string contained one or more port numbers delimited by ",".
+ */
+ public Map<Integer, String> requiredProtoPortMap = null;
+
+ /**
+ * This specifies the maximum acceptable BSS load policy. This is used to prevent device
+ * from joining an AP whose channel is overly congested with traffic.
+ * Using Integer.MIN_VALUE to indicate unset value.
+ */
+ public int maximumBssLoadValue = Integer.MIN_VALUE;
+
+ /**
+ * Policy associated with a roaming provider. This specifies a priority associated
+ * with a roaming provider for given list of countries.
+ *
+ * Contains field under PerProviderSubscription/Policy/PreferredRoamingPartnerList.
+ */
+ public static final class RoamingPartner implements Parcelable {
+ /**
+ * FQDN of the roaming partner.
+ */
+ public String fqdn = null;
+
+ /**
+ * Flag indicating the exact match of FQDN is required for FQDN matching.
+ *
+ * When this flag is set to false, sub-domain matching is used. For example, when
+ * {@link #fqdn} s set to "example.com", "host.example.com" would be a match.
+ */
+ public boolean fqdnExactMatch = false;
+
+ /**
+ * Priority associated with this roaming partner policy.
+ */
+ public int priority = PREFERRED_ROAMING_PARTNER_DEFAULT_PRIORITY;
+
+ /**
+ * A string contained One or more, comma delimited (i.e., ",") ISO/IEC 3166-1 two
+ * character country strings or the country-independent value, "*".
+ */
+ public String countries = null;
+
+ public RoamingPartner() {}
+
+ public RoamingPartner(RoamingPartner source) {
+ if (source != null) {
+ fqdn = source.fqdn;
+ fqdnExactMatch = source.fqdnExactMatch;
+ priority = source.priority;
+ countries = source.countries;
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(fqdn);
+ dest.writeInt(fqdnExactMatch ? 1 : 0);
+ dest.writeInt(priority);
+ dest.writeString(countries);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof RoamingPartner)) {
+ return false;
+ }
+
+ RoamingPartner that = (RoamingPartner) thatObject;
+ return TextUtils.equals(fqdn, that.fqdn)
+ && fqdnExactMatch == that.fqdnExactMatch
+ && priority == that.priority
+ && TextUtils.equals(countries, that.countries);
+ }
+
+ /**
+ * Validate RoamingParnter data.
+ *
+ * @return true on success
+ */
+ public boolean validate() {
+ if (TextUtils.isEmpty(fqdn)) {
+ Log.d(TAG, "Missing FQDN");
+ return false;
+ }
+ if (TextUtils.isEmpty(countries)) {
+ Log.d(TAG, "Missing countries");
+ return false;
+ }
+ return true;
+ }
+
+ public static final Creator<RoamingPartner> CREATOR =
+ new Creator<RoamingPartner>() {
+ @Override
+ public RoamingPartner createFromParcel(Parcel in) {
+ RoamingPartner roamingPartner = new RoamingPartner();
+ roamingPartner.fqdn = in.readString();
+ roamingPartner.fqdnExactMatch = in.readInt() != 0;
+ roamingPartner.priority = in.readInt();
+ roamingPartner.countries = in.readString();
+ return roamingPartner;
+ }
+
+ @Override
+ public RoamingPartner[] newArray(int size) {
+ return new RoamingPartner[size];
+ }
+ };
+ }
+ public List<RoamingPartner> preferredRoamingPartnerList = null;
+
+ /**
+ * Meta data used for policy update.
+ */
+ public UpdateParameter policyUpdate = null;
+
+ /**
+ * Constructor for creating Policy with default values.
+ */
+ public Policy() {}
+
+ /**
+ * Copy constructor.
+ *
+ * @param source The source to copy from
+ */
+ public Policy(Policy source) {
+ if (source == null) {
+ return;
+ }
+ minHomeDownlinkBandwidth = source.minHomeDownlinkBandwidth;
+ minHomeUplinkBandwidth = source.minHomeUplinkBandwidth;
+ minRoamingDownlinkBandwidth = source.minRoamingDownlinkBandwidth;
+ minRoamingUplinkBandwidth = source.minRoamingUplinkBandwidth;
+ maximumBssLoadValue = source.maximumBssLoadValue;
+ if (source.excludedSsidList != null) {
+ excludedSsidList = Arrays.copyOf(source.excludedSsidList,
+ source.excludedSsidList.length);
+ }
+ if (source.requiredProtoPortMap != null) {
+ requiredProtoPortMap = Collections.unmodifiableMap(source.requiredProtoPortMap);
+ }
+ if (source.preferredRoamingPartnerList != null) {
+ preferredRoamingPartnerList = Collections.unmodifiableList(
+ source.preferredRoamingPartnerList);
+ }
+ if (source.policyUpdate != null) {
+ policyUpdate = new UpdateParameter(source.policyUpdate);
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(minHomeDownlinkBandwidth);
+ dest.writeLong(minHomeUplinkBandwidth);
+ dest.writeLong(minRoamingDownlinkBandwidth);
+ dest.writeLong(minRoamingUplinkBandwidth);
+ dest.writeStringArray(excludedSsidList);
+ writeProtoPortMap(dest, requiredProtoPortMap);
+ dest.writeInt(maximumBssLoadValue);
+ writeRoamingPartnerList(dest, flags, preferredRoamingPartnerList);
+ dest.writeParcelable(policyUpdate, flags);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof Policy)) {
+ return false;
+ }
+ Policy that = (Policy) thatObject;
+
+ return minHomeDownlinkBandwidth == that.minHomeDownlinkBandwidth
+ && minHomeUplinkBandwidth == that.minHomeUplinkBandwidth
+ && minRoamingDownlinkBandwidth == that.minRoamingDownlinkBandwidth
+ && minRoamingUplinkBandwidth == that.minRoamingUplinkBandwidth
+ && Arrays.equals(excludedSsidList, that.excludedSsidList)
+ && (requiredProtoPortMap == null) ? that.requiredProtoPortMap == null
+ : requiredProtoPortMap.equals(that.requiredProtoPortMap)
+ && maximumBssLoadValue == that.maximumBssLoadValue
+ && (preferredRoamingPartnerList == null) ? that.preferredRoamingPartnerList == null
+ : preferredRoamingPartnerList.equals(that.preferredRoamingPartnerList)
+ && (policyUpdate == null) ? that.policyUpdate == null
+ : policyUpdate.equals(that.policyUpdate);
+ }
+
+ /**
+ * Validate Policy data.
+ *
+ * @return true on success
+ */
+ public boolean validate() {
+ if (policyUpdate == null) {
+ Log.d(TAG, "PolicyUpdate not specified");
+ return false;
+ }
+ if (!policyUpdate.validate()) {
+ return false;
+ }
+
+ // Validate SSID exclusion list.
+ if (excludedSsidList != null) {
+ if (excludedSsidList.length > MAX_EXCLUSION_SSIDS) {
+ Log.d(TAG, "SSID exclusion list size exceeded the max: "
+ + excludedSsidList.length);
+ return false;
+ }
+ for (String ssid : excludedSsidList) {
+ if (ssid.getBytes(StandardCharsets.UTF_8).length > MAX_SSID_BYTES) {
+ Log.d(TAG, "Invalid SSID: " + ssid);
+ return false;
+ }
+ }
+ }
+ // Validate required protocol to port map.
+ if (requiredProtoPortMap != null) {
+ for (Map.Entry<Integer, String> entry : requiredProtoPortMap.entrySet()) {
+ String portNumber = entry.getValue();
+ if (portNumber.getBytes(StandardCharsets.UTF_8).length > MAX_PORT_STRING_BYTES) {
+ Log.d(TAG, "PortNumber string bytes exceeded the max: " + portNumber);
+ return false;
+ }
+ }
+ }
+ // Validate preferred roaming partner list.
+ if (preferredRoamingPartnerList != null) {
+ for (RoamingPartner partner : preferredRoamingPartnerList) {
+ if (!partner.validate()) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public static final Creator<Policy> CREATOR =
+ new Creator<Policy>() {
+ @Override
+ public Policy createFromParcel(Parcel in) {
+ Policy policy = new Policy();
+ policy.minHomeDownlinkBandwidth = in.readLong();
+ policy.minHomeUplinkBandwidth = in.readLong();
+ policy.minRoamingDownlinkBandwidth = in.readLong();
+ policy.minRoamingUplinkBandwidth = in.readLong();
+ policy.excludedSsidList = in.createStringArray();
+ policy.requiredProtoPortMap = readProtoPortMap(in);
+ policy.maximumBssLoadValue = in.readInt();
+ policy.preferredRoamingPartnerList = readRoamingPartnerList(in);
+ policy.policyUpdate = in.readParcelable(null);
+ return policy;
+ }
+
+ @Override
+ public Policy[] newArray(int size) {
+ return new Policy[size];
+ }
+
+ /**
+ * Helper function for reading IP Protocol to Port Number map from a Parcel.
+ *
+ * @param in The Parcel to read from
+ * @return Map of IP protocol to port number
+ */
+ private Map<Integer, String> readProtoPortMap(Parcel in) {
+ int size = in.readInt();
+ if (size == NULL_VALUE) {
+ return null;
+ }
+ Map<Integer, String> protoPortMap = new HashMap<>(size);
+ for (int i = 0; i < size; i++) {
+ int key = in.readInt();
+ String value = in.readString();
+ protoPortMap.put(key, value);
+ }
+ return protoPortMap;
+ }
+
+ /**
+ * Helper function for reading roaming partner list from a Parcel.
+ *
+ * @param in The Parcel to read from
+ * @return List of roaming partners
+ */
+ private List<RoamingPartner> readRoamingPartnerList(Parcel in) {
+ int size = in.readInt();
+ if (size == NULL_VALUE) {
+ return null;
+ }
+ List<RoamingPartner> partnerList = new ArrayList<>();
+ for (int i = 0; i < size; i++) {
+ partnerList.add(in.readParcelable(null));
+ }
+ return partnerList;
+ }
+
+ };
+
+ /**
+ * Helper function for writing IP Protocol to Port Number map to a Parcel.
+ *
+ * @param dest The Parcel to write to
+ * @param protoPortMap The map to write
+ */
+ private static void writeProtoPortMap(Parcel dest, Map<Integer, String> protoPortMap) {
+ if (protoPortMap == null) {
+ dest.writeInt(NULL_VALUE);
+ return;
+ }
+ dest.writeInt(protoPortMap.size());
+ for (Map.Entry<Integer, String> entry : protoPortMap.entrySet()) {
+ dest.writeInt(entry.getKey());
+ dest.writeString(entry.getValue());
+ }
+ }
+
+ /**
+ * Helper function for writing roaming partner list to a Parcel.
+ *
+ * @param dest The Parcel to write to
+ * @param flags The flag about how the object should be written
+ * @param partnerList The partner list to write
+ */
+ private static void writeRoamingPartnerList(Parcel dest, int flags,
+ List<RoamingPartner> partnerList) {
+ if (partnerList == null) {
+ dest.writeInt(NULL_VALUE);
+ return;
+ }
+ dest.writeInt(partnerList.size());
+ for (RoamingPartner partner : partnerList) {
+ dest.writeParcelable(partner, flags);
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.aidl b/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.aidl
new file mode 100644
index 0000000..701db47
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2017, 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.pps;
+
+parcelable UpdateParameter;
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.java b/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.java
new file mode 100644
index 0000000..a390df7
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.java
@@ -0,0 +1,303 @@
+/**
+ * Copyright (c) 2017, 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.pps;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Log;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+/**
+ * Class representing configuration parameters for subscription or policy update in
+ * PerProviderSubscription (PPS) Management Object (MO) tree. This is used by both
+ * PerProviderSubscription/Policy/PolicyUpdate and PerProviderSubscription/SubscriptionUpdate
+ * subtree.
+ *
+ * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
+ * Release 2 Technical Specification.
+ *
+ * @hide
+ */
+public final class UpdateParameter implements Parcelable {
+ private static final String TAG = "UpdateParameter";
+
+ /**
+ * Value indicating policy update is not applicable. Thus, never check with policy server
+ * for updates.
+ */
+ public static final long UPDATE_CHECK_INTERVAL_NEVER = 0xFFFFFFFFL;
+
+ /**
+ * Valid string for UpdateMethod.
+ */
+ public static final String UPDATE_METHOD_OMADM = "OMA-DM-ClientInitiated";
+ public static final String UPDATE_METHOD_SSP = "SSP-ClientInitiated";
+
+ /**
+ * Valid string for Restriction.
+ */
+ public static final String UPDATE_RESTRICTION_HOMESP = "HomeSP";
+ public static final String UPDATE_RESTRICTION_ROAMING_PARTNER = "RoamingPartner";
+ public static final String UPDATE_RESTRICTION_UNRESTRICTED = "Unrestricted";
+
+ /**
+ * Maximum bytes for URI string.
+ */
+ private static final int MAX_URI_BYTES = 1023;
+
+ /**
+ * Maximum bytes for URI string.
+ */
+ private static final int MAX_URL_BYTES = 1023;
+
+ /**
+ * Maximum bytes for username.
+ */
+ private static final int MAX_USERNAME_BYTES = 63;
+
+ /**
+ * Maximum bytes for password.
+ */
+ private static final int MAX_PASSWORD_BYTES = 255;
+
+ /**
+ * Number of bytes for certificate SHA-256 fingerprint byte array.
+ */
+ private static final int CERTIFICATE_SHA256_BYTES = 32;
+
+ /**
+ * This specifies how often the mobile device shall check with policy server for updates.
+ *
+ * Using Long.MIN_VALUE to indicate unset value.
+ */
+ public long updateIntervalInMinutes = Long.MIN_VALUE;
+
+ /**
+ * The method used to update the policy. Permitted values are "OMA-DM-ClientInitiated"
+ * and "SPP-ClientInitiated".
+ */
+ public String updateMethod = null;
+
+ /**
+ * This specifies the hotspots at which the subscription update is permitted. Permitted
+ * values are "HomeSP", "RoamingPartner", or "Unrestricted";
+ */
+ public String restriction = null;
+
+ /**
+ * The URI of the update server.
+ */
+ public String serverUri = null;
+
+ /**
+ * Username used to authenticate with the policy server.
+ */
+ public String username = null;
+
+ /**
+ * Base64 encoded password used to authenticate with the policy server.
+ */
+ public String base64EncodedPassword = null;
+
+ /**
+ * HTTPS URL for retrieving certificate for trust root. The trust root is used to validate
+ * policy server's identity.
+ */
+ public String trustRootCertUrl = null;
+
+ /**
+ * SHA-256 fingerprint of the certificate located at {@link #trustRootCertUrl}
+ */
+ public byte[] trustRootCertSha256Fingerprint = null;
+
+ /**
+ * Constructor for creating Policy with default values.
+ */
+ public UpdateParameter() {}
+
+ /**
+ * Copy constructor.
+ *
+ * @param source The source to copy from
+ */
+ public UpdateParameter(UpdateParameter source) {
+ if (source == null) {
+ return;
+ }
+ updateIntervalInMinutes = source.updateIntervalInMinutes;
+ updateMethod = source.updateMethod;
+ restriction = source.restriction;
+ serverUri = source.serverUri;
+ username = source.username;
+ base64EncodedPassword = source.base64EncodedPassword;
+ trustRootCertUrl = source.trustRootCertUrl;
+ if (source.trustRootCertSha256Fingerprint != null) {
+ trustRootCertSha256Fingerprint = Arrays.copyOf(source.trustRootCertSha256Fingerprint,
+ source.trustRootCertSha256Fingerprint.length);
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(updateIntervalInMinutes);
+ dest.writeString(updateMethod);
+ dest.writeString(restriction);
+ dest.writeString(serverUri);
+ dest.writeString(username);
+ dest.writeString(base64EncodedPassword);
+ dest.writeString(trustRootCertUrl);
+ dest.writeByteArray(trustRootCertSha256Fingerprint);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof UpdateParameter)) {
+ return false;
+ }
+ UpdateParameter that = (UpdateParameter) thatObject;
+
+ return updateIntervalInMinutes == that.updateIntervalInMinutes
+ && TextUtils.equals(updateMethod, that.updateMethod)
+ && TextUtils.equals(restriction, that.restriction)
+ && TextUtils.equals(serverUri, that.serverUri)
+ && TextUtils.equals(username, that.username)
+ && TextUtils.equals(base64EncodedPassword, that.base64EncodedPassword)
+ && TextUtils.equals(trustRootCertUrl, that.trustRootCertUrl)
+ && Arrays.equals(trustRootCertSha256Fingerprint,
+ that.trustRootCertSha256Fingerprint);
+ }
+
+ /**
+ * Validate UpdateParameter data.
+ *
+ * @return true on success
+ */
+ public boolean validate() {
+ if (updateIntervalInMinutes == Long.MIN_VALUE) {
+ Log.d(TAG, "Update interval not specified");
+ return false;
+ }
+ // Update not applicable.
+ if (updateIntervalInMinutes == UPDATE_CHECK_INTERVAL_NEVER) {
+ return true;
+ }
+
+ if (!TextUtils.equals(updateMethod, UPDATE_METHOD_OMADM)
+ && !TextUtils.equals(updateMethod, UPDATE_METHOD_SSP)) {
+ Log.d(TAG, "Unknown update method: " + updateMethod);
+ return false;
+ }
+
+ if (!TextUtils.equals(restriction, UPDATE_RESTRICTION_HOMESP)
+ && !TextUtils.equals(restriction, UPDATE_RESTRICTION_ROAMING_PARTNER)
+ && !TextUtils.equals(restriction, UPDATE_RESTRICTION_UNRESTRICTED)) {
+ Log.d(TAG, "Unknown restriction: " + restriction);
+ return false;
+ }
+
+ if (TextUtils.isEmpty(serverUri)) {
+ Log.d(TAG, "Missing update server URI");
+ return false;
+ }
+ if (serverUri.getBytes(StandardCharsets.UTF_8).length > MAX_URI_BYTES) {
+ Log.d(TAG, "URI bytes exceeded the max: "
+ + serverUri.getBytes(StandardCharsets.UTF_8).length);
+ return false;
+ }
+
+ if (TextUtils.isEmpty(username)) {
+ Log.d(TAG, "Missing username");
+ return false;
+ }
+ if (username.getBytes(StandardCharsets.UTF_8).length > MAX_USERNAME_BYTES) {
+ Log.d(TAG, "Username bytes exceeded the max: "
+ + username.getBytes(StandardCharsets.UTF_8).length);
+ return false;
+ }
+
+ if (TextUtils.isEmpty(base64EncodedPassword)) {
+ Log.d(TAG, "Missing username");
+ return false;
+ }
+ if (base64EncodedPassword.getBytes(StandardCharsets.UTF_8).length > MAX_PASSWORD_BYTES) {
+ Log.d(TAG, "Password bytes exceeded the max: "
+ + base64EncodedPassword.getBytes(StandardCharsets.UTF_8).length);
+ return false;
+ }
+ try {
+ Base64.decode(base64EncodedPassword, Base64.DEFAULT);
+ } catch (IllegalArgumentException e) {
+ Log.d(TAG, "Invalid encoding for password: " + base64EncodedPassword);
+ return false;
+ }
+
+ if (TextUtils.isEmpty(trustRootCertUrl)) {
+ Log.d(TAG, "Missing trust root certificate URL");
+ return false;
+ }
+ if (trustRootCertUrl.getBytes(StandardCharsets.UTF_8).length > MAX_URL_BYTES) {
+ Log.d(TAG, "Trust root cert URL bytes exceeded the max: "
+ + trustRootCertUrl.getBytes(StandardCharsets.UTF_8).length);
+ return false;
+ }
+
+ if (trustRootCertSha256Fingerprint == null) {
+ Log.d(TAG, "Missing trust root certificate SHA-256 fingerprint");
+ return false;
+ }
+ if (trustRootCertSha256Fingerprint.length != CERTIFICATE_SHA256_BYTES) {
+ Log.d(TAG, "Incorrect size of trust root certificate SHA-256 fingerprint: "
+ + trustRootCertSha256Fingerprint.length);
+ return false;
+ }
+ return true;
+ }
+
+ public static final Creator<UpdateParameter> CREATOR =
+ new Creator<UpdateParameter>() {
+ @Override
+ public UpdateParameter createFromParcel(Parcel in) {
+ UpdateParameter updateParam = new UpdateParameter();
+ updateParam.updateIntervalInMinutes = in.readLong();
+ updateParam.updateMethod = in.readString();
+ updateParam.restriction = in.readString();
+ updateParam.serverUri = in.readString();
+ updateParam.username = in.readString();
+ updateParam.base64EncodedPassword = in.readString();
+ updateParam.trustRootCertUrl = in.readString();
+ updateParam.trustRootCertSha256Fingerprint = in.createByteArray();
+ return updateParam;
+ }
+
+ @Override
+ public UpdateParameter[] newArray(int size) {
+ return new UpdateParameter[size];
+ }
+ };
+}
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64
index 8c1eb08..995963d 100644
--- a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64
@@ -42,7 +42,7 @@
a05sY25ScFptbGpZWFJsVkhsd1pUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
eDFaVDU0TlRBNWRqTThMMVpoCmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lD
QWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEowVTBoQk1q
-VTJSbWx1WjJWeVVISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV
+VTJSbWx1WjJWeWNISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV
KwpNV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpG
bU1XWXhaakZtTVdZeFpqRm1NV1l4ClpqRm1NV1l4Wmp3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNB
OEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWcKSUR4T2IyUmxQZ29nSUNB
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf
index 6d86dd5..3ddd09f 100644
--- a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf
@@ -35,7 +35,7 @@
aWZpY2F0ZTwvTm9kZU5hbWU+CiAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAgPE5vZGVOYW1l
PkNlcnRpZmljYXRlVHlwZTwvTm9kZU5hbWU+CiAgICAgICAgICAgIDxWYWx1ZT54NTA5djM8L1Zh
bHVlPgogICAgICAgICAgPC9Ob2RlPgogICAgICAgICAgPE5vZGU+CiAgICAgICAgICAgIDxOb2Rl
-TmFtZT5DZXJ0U0hBMjU2RmluZ2VyUHJpbnQ8L05vZGVOYW1lPgogICAgICAgICAgICA8VmFsdWU+
+TmFtZT5DZXJ0U0hBMjU2RmluZ2VycHJpbnQ8L05vZGVOYW1lPgogICAgICAgICAgICA8VmFsdWU+
MWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYx
ZjFmMWYxZjwvVmFsdWU+CiAgICAgICAgICA8L05vZGU+CiAgICAgICAgPC9Ob2RlPgogICAgICAg
IDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPlNJTTwvTm9kZU5hbWU+CiAgICAgICAgICA8Tm9k
diff --git a/wifi/tests/assets/pps/PerProviderSubscription.xml b/wifi/tests/assets/pps/PerProviderSubscription.xml
index 3969f69..7f2d95d 100644
--- a/wifi/tests/assets/pps/PerProviderSubscription.xml
+++ b/wifi/tests/assets/pps/PerProviderSubscription.xml
@@ -8,6 +8,10 @@
</Type>
</RTProperties>
<Node>
+ <NodeName>UpdateIdentifier</NodeName>
+ <Value>12</Value>
+ </Node>
+ <Node>
<NodeName>i001</NodeName>
<Node>
<NodeName>HomeSP</NodeName>
@@ -143,7 +147,7 @@
<Value>x509v3</Value>
</Node>
<Node>
- <NodeName>CertSHA256FingerPrint</NodeName>
+ <NodeName>CertSHA256Fingerprint</NodeName>
<Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
</Node>
</Node>
@@ -159,6 +163,237 @@
</Node>
</Node>
</Node>
+ <Node>
+ <NodeName>Policy</NodeName>
+ <Node>
+ <NodeName>PreferredRoamingPartnerList</NodeName>
+ <Node>
+ <NodeName>p001</NodeName>
+ <Node>
+ <NodeName>FQDN_Match</NodeName>
+ <Value>test1.fqdn.com,exactMatch</Value>
+ </Node>
+ <Node>
+ <NodeName>Priority</NodeName>
+ <Value>127</Value>
+ </Node>
+ <Node>
+ <NodeName>Country</NodeName>
+ <Value>us,fr</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>p002</NodeName>
+ <Node>
+ <NodeName>FQDN_Match</NodeName>
+ <Value>test2.fqdn.com,includeSubdomains</Value>
+ </Node>
+ <Node>
+ <NodeName>Priority</NodeName>
+ <Value>200</Value>
+ </Node>
+ <Node>
+ <NodeName>Country</NodeName>
+ <Value>*</Value>
+ </Node>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>MinBackhaulThreshold</NodeName>
+ <Node>
+ <NodeName>m001</NodeName>
+ <Node>
+ <NodeName>NetworkType</NodeName>
+ <Value>home</Value>
+ </Node>
+ <Node>
+ <NodeName>DLBandwidth</NodeName>
+ <Value>23412</Value>
+ </Node>
+ <Node>
+ <NodeName>ULBandwidth</NodeName>
+ <Value>9823</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>m002</NodeName>
+ <Node>
+ <NodeName>NetworkType</NodeName>
+ <Value>roaming</Value>
+ </Node>
+ <Node>
+ <NodeName>DLBandwidth</NodeName>
+ <Value>9271</Value>
+ </Node>
+ <Node>
+ <NodeName>ULBandwidth</NodeName>
+ <Value>2315</Value>
+ </Node>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>PolicyUpdate</NodeName>
+ <Node>
+ <NodeName>UpdateInterval</NodeName>
+ <Value>120</Value>
+ </Node>
+ <Node>
+ <NodeName>UpdateMethod</NodeName>
+ <Value>OMA-DM-ClientInitiated</Value>
+ </Node>
+ <Node>
+ <NodeName>Restriction</NodeName>
+ <Value>HomeSP</Value>
+ </Node>
+ <Node>
+ <NodeName>URI</NodeName>
+ <Value>policy.update.com</Value>
+ </Node>
+ <Node>
+ <NodeName>UsernamePassword</NodeName>
+ <Node>
+ <NodeName>Username</NodeName>
+ <Value>updateUser</Value>
+ </Node>
+ <Node>
+ <NodeName>Password</NodeName>
+ <Value>updatePass</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>TrustRoot</NodeName>
+ <Node>
+ <NodeName>CertURL</NodeName>
+ <Value>update.cert.com</Value>
+ </Node>
+ <Node>
+ <NodeName>CertSHA256Fingerprint</NodeName>
+ <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
+ </Node>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>SPExclusionList</NodeName>
+ <Node>
+ <NodeName>s001</NodeName>
+ <Node>
+ <NodeName>SSID</NodeName>
+ <Value>excludeSSID</Value>
+ </Node>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>RequiredProtoPortTuple</NodeName>
+ <Node>
+ <NodeName>r001</NodeName>
+ <Node>
+ <NodeName>IPProtocol</NodeName>
+ <Value>12</Value>
+ </Node>
+ <Node>
+ <NodeName>PortNumber</NodeName>
+ <Value>34,92,234</Value>
+ </Node>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>MaximumBSSLoadValue</NodeName>
+ <Value>23</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>CredentialPriority</NodeName>
+ <Value>99</Value>
+ </Node>
+ <Node>
+ <NodeName>AAAServerTrustRoot</NodeName>
+ <Node>
+ <NodeName>a001</NodeName>
+ <Node>
+ <NodeName>CertURL</NodeName>
+ <Value>server1.trust.root.com</Value>
+ </Node>
+ <Node>
+ <NodeName>CertSHA256Fingerprint</NodeName>
+ <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
+ </Node>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>SubscriptionUpdate</NodeName>
+ <Node>
+ <NodeName>UpdateInterval</NodeName>
+ <Value>120</Value>
+ </Node>
+ <Node>
+ <NodeName>UpdateMethod</NodeName>
+ <Value>SSP-ClientInitiated</Value>
+ </Node>
+ <Node>
+ <NodeName>Restriction</NodeName>
+ <Value>RoamingPartner</Value>
+ </Node>
+ <Node>
+ <NodeName>URI</NodeName>
+ <Value>subscription.update.com</Value>
+ </Node>
+ <Node>
+ <NodeName>UsernamePassword</NodeName>
+ <Node>
+ <NodeName>Username</NodeName>
+ <Value>subscriptionUser</Value>
+ </Node>
+ <Node>
+ <NodeName>Password</NodeName>
+ <Value>subscriptionPass</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>TrustRoot</NodeName>
+ <Node>
+ <NodeName>CertURL</NodeName>
+ <Value>subscription.update.cert.com</Value>
+ </Node>
+ <Node>
+ <NodeName>CertSHA256Fingerprint</NodeName>
+ <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
+ </Node>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>SubscriptionParameter</NodeName>
+ <Node>
+ <NodeName>CreationDate</NodeName>
+ <Value>2016-02-01T10:00:00Z</Value>
+ </Node>
+ <Node>
+ <NodeName>ExpirationDate</NodeName>
+ <Value>2016-03-01T10:00:00Z</Value>
+ </Node>
+ <Node>
+ <NodeName>TypeOfSubscription</NodeName>
+ <Value>Gold</Value>
+ </Node>
+ <Node>
+ <NodeName>UsageLimits</NodeName>
+ <Node>
+ <NodeName>DataLimit</NodeName>
+ <Value>921890</Value>
+ </Node>
+ <Node>
+ <NodeName>StartDate</NodeName>
+ <Value>2016-12-01T10:00:00Z</Value>
+ </Node>
+ <Node>
+ <NodeName>TimeLimit</NodeName>
+ <Value>120</Value>
+ </Node>
+ <Node>
+ <NodeName>UsageTimePeriod</NodeName>
+ <Value>99910</Value>
+ </Node>
+ </Node>
+ </Node>
</Node>
</Node>
</MgmtTree>
diff --git a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
index 0e503d5..5a67a7e 100644
--- a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
@@ -87,6 +87,42 @@
}
@Test
+ public void testSetClientCertificateChain() {
+ PrivateKey clientKey = FakeKeys.RSA_KEY1;
+ X509Certificate cert0 = FakeKeys.CLIENT_CERT;
+ X509Certificate cert1 = FakeKeys.CA_CERT1;
+ X509Certificate[] clientChain = new X509Certificate[] {cert0, cert1};
+ mEnterpriseConfig.setClientKeyEntryWithCertificateChain(clientKey, clientChain);
+ X509Certificate[] result = mEnterpriseConfig.getClientCertificateChain();
+ assertEquals(result.length, 2);
+ assertTrue(result[0] == cert0 && result[1] == cert1);
+ assertTrue(mEnterpriseConfig.getClientCertificate() == cert0);
+ }
+
+ private boolean isClientCertificateChainInvalid(X509Certificate[] clientChain) {
+ boolean exceptionThrown = false;
+ try {
+ PrivateKey clientKey = FakeKeys.RSA_KEY1;
+ mEnterpriseConfig.setClientKeyEntryWithCertificateChain(clientKey, clientChain);
+ } catch (IllegalArgumentException e) {
+ exceptionThrown = true;
+ }
+ return exceptionThrown;
+ }
+
+ @Test
+ public void testSetInvalidClientCertificateChain() {
+ X509Certificate clientCert = FakeKeys.CLIENT_CERT;
+ X509Certificate caCert = FakeKeys.CA_CERT1;
+ assertTrue("Invalid client certificate",
+ isClientCertificateChainInvalid(new X509Certificate[] {caCert, caCert}));
+ assertTrue("Invalid CA certificate",
+ isClientCertificateChainInvalid(new X509Certificate[] {clientCert, clientCert}));
+ assertTrue("Both certificates invalid",
+ isClientCertificateChainInvalid(new X509Certificate[] {caCert, clientCert}));
+ }
+
+ @Test
public void testSaveSingleCaCertificateAlias() {
final String alias = "single_alias 0";
mEnterpriseConfig.setCaCertificateAliases(new String[] {alias});
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
index 2350d32..1eb08e0 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -22,16 +22,26 @@
import android.net.wifi.EAPConstants;
import android.net.wifi.hotspot2.pps.Credential;
import android.net.wifi.hotspot2.pps.HomeSP;
+import android.net.wifi.hotspot2.pps.Policy;
+import android.net.wifi.hotspot2.pps.UpdateParameter;
import android.os.Parcel;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Base64;
import org.junit.Test;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+
/**
* Unit tests for {@link android.net.wifi.hotspot2.PasspointConfiguration}.
*/
@SmallTest
public class PasspointConfigurationTest {
+ private static final int MAX_URL_BYTES = 1023;
+ private static final int CERTIFICATE_FINGERPRINT_BYTES = 32;
/**
* Utility function for creating a {@link android.net.wifi.hotspot2.pps.HomeSP}.
@@ -66,6 +76,93 @@
}
/**
+ * Helper function for creating a {@link Policy} for testing.
+ *
+ * @return {@link Policy}
+ */
+ private static Policy createPolicy() {
+ Policy policy = new Policy();
+ policy.minHomeDownlinkBandwidth = 123;
+ policy.minHomeUplinkBandwidth = 345;
+ policy.minRoamingDownlinkBandwidth = 567;
+ policy.minRoamingUplinkBandwidth = 789;
+ policy.maximumBssLoadValue = 12;
+ policy.excludedSsidList = new String[] {"ssid1", "ssid2"};
+ policy.requiredProtoPortMap = new HashMap<>();
+ policy.requiredProtoPortMap.put(12, "23,342,123");
+ policy.requiredProtoPortMap.put(23, "789,372,1235");
+
+ policy.preferredRoamingPartnerList = new ArrayList<>();
+ Policy.RoamingPartner partner1 = new Policy.RoamingPartner();
+ partner1.fqdn = "partner1.com";
+ partner1.fqdnExactMatch = true;
+ partner1.priority = 12;
+ partner1.countries = "us,jp";
+ Policy.RoamingPartner partner2 = new Policy.RoamingPartner();
+ partner2.fqdn = "partner2.com";
+ partner2.fqdnExactMatch = false;
+ partner2.priority = 42;
+ partner2.countries = "ca,fr";
+ policy.preferredRoamingPartnerList.add(partner1);
+ policy.preferredRoamingPartnerList.add(partner2);
+
+ policy.policyUpdate = new UpdateParameter();
+ policy.policyUpdate.updateIntervalInMinutes = 1712;
+ policy.policyUpdate.updateMethod = UpdateParameter.UPDATE_METHOD_OMADM;
+ policy.policyUpdate.restriction = UpdateParameter.UPDATE_RESTRICTION_HOMESP;
+ policy.policyUpdate.serverUri = "policy.update.com";
+ policy.policyUpdate.username = "username";
+ policy.policyUpdate.base64EncodedPassword =
+ Base64.encodeToString("password".getBytes(), Base64.DEFAULT);
+ policy.policyUpdate.trustRootCertUrl = "trust.cert.com";
+ policy.policyUpdate.trustRootCertSha256Fingerprint =
+ new byte[CERTIFICATE_FINGERPRINT_BYTES];
+
+ return policy;
+ }
+
+ private static UpdateParameter createSubscriptionUpdate() {
+ UpdateParameter subUpdate = new UpdateParameter();
+ subUpdate.updateIntervalInMinutes = 9021;
+ subUpdate.updateMethod = UpdateParameter.UPDATE_METHOD_SSP;
+ subUpdate.restriction = UpdateParameter.UPDATE_RESTRICTION_ROAMING_PARTNER;
+ subUpdate.serverUri = "subscription.update.com";
+ subUpdate.username = "subUsername";
+ subUpdate.base64EncodedPassword =
+ Base64.encodeToString("subPassword".getBytes(), Base64.DEFAULT);
+ subUpdate.trustRootCertUrl = "subscription.trust.cert.com";
+ subUpdate.trustRootCertSha256Fingerprint = new byte[CERTIFICATE_FINGERPRINT_BYTES];
+ return subUpdate;
+ }
+ /**
+ * Helper function for creating a {@link PasspointConfiguration} for testing.
+ *
+ * @return {@link PasspointConfiguration}
+ */
+ private static PasspointConfiguration createConfig() {
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.homeSp = createHomeSp();
+ config.credential = createCredential();
+ config.policy = createPolicy();
+ config.subscriptionUpdate = createSubscriptionUpdate();
+ config.trustRootCertList = new HashMap<>();
+ config.trustRootCertList.put("trustRoot.cert1.com",
+ new byte[CERTIFICATE_FINGERPRINT_BYTES]);
+ config.trustRootCertList.put("trustRoot.cert2.com",
+ new byte[CERTIFICATE_FINGERPRINT_BYTES]);
+ config.updateIdentifier = 1;
+ config.credentialPriority = 120;
+ config.subscriptionCreationTimeInMs = 231200;
+ config.subscriptionExpirationTimeInMs = 2134232;
+ config.subscriptionType = "Gold";
+ config.usageLimitUsageTimePeriodInMinutes = 3600;
+ config.usageLimitStartTimeInMs = 124214213;
+ config.usageLimitDataLimit = 14121;
+ config.usageLimitTimeLimitInMinutes = 78912;
+ return config;
+ }
+
+ /**
* Verify parcel write and read consistency for the given configuration.
*
* @param writeConfig The configuration to verify
@@ -92,39 +189,73 @@
}
/**
- * Verify parcel read/write for a configuration that contained both HomeSP and Credential.
+ * Verify parcel read/write for a configuration that contained the full configuration.
*
* @throws Exception
*/
@Test
- public void verifyParcelWithHomeSPAndCredential() throws Exception {
- PasspointConfiguration config = new PasspointConfiguration();
- config.homeSp = createHomeSp();
- config.credential = createCredential();
+ public void verifyParcelWithFullConfiguration() throws Exception {
+ verifyParcel(createConfig());
+ }
+
+ /**
+ * Verify parcel read/write for a configuration that doesn't contain HomeSP.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyParcelWithoutHomeSP() throws Exception {
+ PasspointConfiguration config = createConfig();
+ config.homeSp = null;
verifyParcel(config);
}
/**
- * Verify parcel read/write for a configuration that contained only HomeSP.
+ * Verify parcel read/write for a configuration that doesn't contain Credential.
*
* @throws Exception
*/
@Test
- public void verifyParcelWithHomeSPOnly() throws Exception {
- PasspointConfiguration config = new PasspointConfiguration();
- config.homeSp = createHomeSp();
+ public void verifyParcelWithoutCredential() throws Exception {
+ PasspointConfiguration config = createConfig();
+ config.credential = null;
verifyParcel(config);
}
/**
- * Verify parcel read/write for a configuration that contained only Credential.
+ * Verify parcel read/write for a configuration that doesn't contain Policy.
*
* @throws Exception
*/
@Test
- public void verifyParcelWithCredentialOnly() throws Exception {
- PasspointConfiguration config = new PasspointConfiguration();
- config.credential = createCredential();
+ public void verifyParcelWithoutPolicy() throws Exception {
+ PasspointConfiguration config = createConfig();
+ config.policy = null;
+ verifyParcel(config);
+ }
+
+ /**
+ * Verify parcel read/write for a configuration that doesn't contain subscription update.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyParcelWithoutSubscriptionUpdate() throws Exception {
+ PasspointConfiguration config = createConfig();
+ config.subscriptionUpdate = null;
+ verifyParcel(config);
+ }
+
+ /**
+ * Verify parcel read/write for a configuration that doesn't contain trust root certificate
+ * list.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyParcelWithoutTrustRootCertList() throws Exception {
+ PasspointConfiguration config = createConfig();
+ config.trustRootCertList = null;
verifyParcel(config);
}
@@ -140,43 +271,108 @@
}
/**
+ * Verify that a configuration contained all fields is valid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateFullConfig() throws Exception {
+ PasspointConfiguration config = createConfig();
+ assertTrue(config.validate());
+ }
+
+ /**
* Verify that a configuration without Credential is invalid.
*
* @throws Exception
*/
@Test
public void validateConfigWithoutCredential() throws Exception {
- PasspointConfiguration config = new PasspointConfiguration();
- config.homeSp = createHomeSp();
+ PasspointConfiguration config = createConfig();
+ config.credential = null;
assertFalse(config.validate());
}
/**
- * Verify that a a configuration without HomeSP is invalid.
+ * Verify that a configuration without HomeSP is invalid.
*
* @throws Exception
*/
@Test
public void validateConfigWithoutHomeSp() throws Exception {
- PasspointConfiguration config = new PasspointConfiguration();
- config.credential = createCredential();
+ PasspointConfiguration config = createConfig();
+ config.homeSp = null;
assertFalse(config.validate());
}
/**
- * Verify a valid configuration.
+ * Verify that a configuration without Policy is valid, since Policy configurations
+ * are optional (applied for Hotspot 2.0 Release only).
*
* @throws Exception
*/
@Test
- public void validateValidConfig() throws Exception {
- PasspointConfiguration config = new PasspointConfiguration();
- config.homeSp = createHomeSp();
- config.credential = createCredential();
+ public void validateConfigWithoutPolicy() throws Exception {
+ PasspointConfiguration config = createConfig();
+ config.policy = null;
assertTrue(config.validate());
}
/**
+ * Verify that a configuration without subscription update is valid, since subscription
+ * update configurations are optional (applied for Hotspot 2.0 Release only).
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateConfigWithoutSubscriptionUpdate() throws Exception {
+ PasspointConfiguration config = createConfig();
+ config.subscriptionUpdate = null;
+ assertTrue(config.validate());
+ }
+
+ /**
+ * Verify that a configuration with a trust root certificate URL exceeding the max size
+ * is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateConfigWithInvalidTrustRootCertUrl() throws Exception {
+ PasspointConfiguration config = createConfig();
+ byte[] rawUrlBytes = new byte[MAX_URL_BYTES + 1];
+ Arrays.fill(rawUrlBytes, (byte) 'a');
+ config.trustRootCertList.put(new String(rawUrlBytes, StandardCharsets.UTF_8),
+ new byte[CERTIFICATE_FINGERPRINT_BYTES]);
+ assertFalse(config.validate());
+
+ config.trustRootCertList = new HashMap<>();
+ config.trustRootCertList.put(null, new byte[CERTIFICATE_FINGERPRINT_BYTES]);
+ assertFalse(config.validate());
+ }
+
+ /**
+ * Verify that a configuration with an invalid trust root certificate fingerprint is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateConfigWithInvalidTrustRootCertFingerprint() throws Exception {
+ PasspointConfiguration config = createConfig();
+ config.trustRootCertList = new HashMap<>();
+ config.trustRootCertList.put("test.cert.com", new byte[CERTIFICATE_FINGERPRINT_BYTES + 1]);
+ assertFalse(config.validate());
+
+ config.trustRootCertList = new HashMap<>();
+ config.trustRootCertList.put("test.cert.com", new byte[CERTIFICATE_FINGERPRINT_BYTES - 1]);
+ assertFalse(config.validate());
+
+ config.trustRootCertList = new HashMap<>();
+ config.trustRootCertList.put("test.cert.com", null);
+ assertFalse(config.validate());
+ }
+
+ /**
* Verify that copy constructor works when pass in a null source.
*
* @throws Exception
@@ -195,9 +391,7 @@
*/
@Test
public void validateCopyConstructorWithValidSource() throws Exception {
- PasspointConfiguration sourceConfig = new PasspointConfiguration();
- sourceConfig.homeSp = createHomeSp();
- sourceConfig.credential = createCredential();
+ PasspointConfiguration sourceConfig = createConfig();
PasspointConfiguration copyConfig = new PasspointConfiguration(sourceConfig);
assertTrue(copyConfig.equals(sourceConfig));
}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java
index 1c7508e..055204c 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java
@@ -23,7 +23,10 @@
import android.net.wifi.hotspot2.PasspointConfiguration;
import android.net.wifi.hotspot2.pps.Credential;
import android.net.wifi.hotspot2.pps.HomeSP;
+import android.net.wifi.hotspot2.pps.Policy;
+import android.net.wifi.hotspot2.pps.UpdateParameter;
import android.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
import org.junit.Test;
@@ -33,6 +36,7 @@
import java.io.InputStreamReader;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -81,7 +85,38 @@
* @return {@link PasspointConfiguration}
*/
private PasspointConfiguration generateConfigurationFromPPSMOTree() throws Exception {
+ DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+
PasspointConfiguration config = new PasspointConfiguration();
+ config.updateIdentifier = 12;
+ config.credentialPriority = 99;
+
+ // AAA Server trust root.
+ config.trustRootCertList = new HashMap<>();
+ byte[] certFingerprint = new byte[32];
+ Arrays.fill(certFingerprint, (byte) 0x1f);
+ config.trustRootCertList.put("server1.trust.root.com", certFingerprint);
+
+ // Subscription update.
+ config.subscriptionUpdate = new UpdateParameter();
+ config.subscriptionUpdate.updateIntervalInMinutes = 120;
+ config.subscriptionUpdate.updateMethod = UpdateParameter.UPDATE_METHOD_SSP;
+ config.subscriptionUpdate.restriction = UpdateParameter.UPDATE_RESTRICTION_ROAMING_PARTNER;
+ config.subscriptionUpdate.serverUri = "subscription.update.com";
+ config.subscriptionUpdate.username = "subscriptionUser";
+ config.subscriptionUpdate.base64EncodedPassword = "subscriptionPass";
+ config.subscriptionUpdate.trustRootCertUrl = "subscription.update.cert.com";
+ config.subscriptionUpdate.trustRootCertSha256Fingerprint = new byte[32];
+ Arrays.fill(config.subscriptionUpdate.trustRootCertSha256Fingerprint, (byte) 0x1f);
+
+ // Subscription parameters.
+ config.subscriptionCreationTimeInMs = format.parse("2016-02-01T10:00:00Z").getTime();
+ config.subscriptionExpirationTimeInMs = format.parse("2016-03-01T10:00:00Z").getTime();
+ config.subscriptionType = "Gold";
+ config.usageLimitDataLimit = 921890;
+ config.usageLimitStartTimeInMs = format.parse("2016-12-01T10:00:00Z").getTime();
+ config.usageLimitTimeLimitInMinutes = 120;
+ config.usageLimitUsageTimePeriodInMinutes = 99910;
// HomeSP configuration.
config.homeSp = new HomeSP();
@@ -97,7 +132,6 @@
config.homeSp.otherHomePartners = new String[] {"other.fqdn.com"};
// Credential configuration.
- DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
config.credential = new Credential();
config.credential.creationTimeInMs = format.parse("2016-01-01T10:00:00Z").getTime();
config.credential.expirationTimeInMs = format.parse("2016-02-01T10:00:00Z").getTime();
@@ -118,12 +152,46 @@
config.credential.simCredential = new Credential.SimCredential();
config.credential.simCredential.imsi = "imsi";
config.credential.simCredential.eapType = 24;
+
+ // Policy configuration.
+ config.policy = new Policy();
+ config.policy.preferredRoamingPartnerList = new ArrayList<>();
+ Policy.RoamingPartner partner1 = new Policy.RoamingPartner();
+ partner1.fqdn = "test1.fqdn.com";
+ partner1.fqdnExactMatch = true;
+ partner1.priority = 127;
+ partner1.countries = "us,fr";
+ Policy.RoamingPartner partner2 = new Policy.RoamingPartner();
+ partner2.fqdn = "test2.fqdn.com";
+ partner2.fqdnExactMatch = false;
+ partner2.priority = 200;
+ partner2.countries = "*";
+ config.policy.preferredRoamingPartnerList.add(partner1);
+ config.policy.preferredRoamingPartnerList.add(partner2);
+ config.policy.minHomeDownlinkBandwidth = 23412;
+ config.policy.minHomeUplinkBandwidth = 9823;
+ config.policy.minRoamingDownlinkBandwidth = 9271;
+ config.policy.minRoamingUplinkBandwidth = 2315;
+ config.policy.excludedSsidList = new String[] {"excludeSSID"};
+ config.policy.requiredProtoPortMap = new HashMap<>();
+ config.policy.requiredProtoPortMap.put(12, "34,92,234");
+ config.policy.maximumBssLoadValue = 23;
+ config.policy.policyUpdate = new UpdateParameter();
+ config.policy.policyUpdate.updateIntervalInMinutes = 120;
+ config.policy.policyUpdate.updateMethod = UpdateParameter.UPDATE_METHOD_OMADM;
+ config.policy.policyUpdate.restriction = UpdateParameter.UPDATE_RESTRICTION_HOMESP;
+ config.policy.policyUpdate.serverUri = "policy.update.com";
+ config.policy.policyUpdate.username = "updateUser";
+ config.policy.policyUpdate.base64EncodedPassword = "updatePass";
+ config.policy.policyUpdate.trustRootCertUrl = "update.cert.com";
+ config.policy.policyUpdate.trustRootCertSha256Fingerprint = new byte[32];
+ Arrays.fill(config.policy.policyUpdate.trustRootCertSha256Fingerprint, (byte) 0x1f);
+
return config;
}
/**
- * Parse and verify all supported fields under PPS MO tree (currently only fields under
- * HomeSP and Credential subtree).
+ * Parse and verify all supported fields under PPS MO tree.
*
* @throws Exception
*/
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java
new file mode 100644
index 0000000..c371c49
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2017 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.pps;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Base64;
+
+import org.junit.Test;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Unit tests for {@link android.net.wifi.hotspot2.pps.Policy}.
+ */
+@SmallTest
+public class PolicyTest {
+ private static final int MAX_NUMBER_OF_EXCLUDED_SSIDS = 128;
+ private static final int MAX_SSID_BYTES = 32;
+ private static final int MAX_PORT_STRING_BYTES = 64;
+
+ /**
+ * Helper function for creating a {@link Policy} for testing.
+ *
+ * @return {@link Policy}
+ */
+ private static Policy createPolicy() {
+ Policy policy = new Policy();
+ policy.minHomeDownlinkBandwidth = 123;
+ policy.minHomeUplinkBandwidth = 345;
+ policy.minRoamingDownlinkBandwidth = 567;
+ policy.minRoamingUplinkBandwidth = 789;
+ policy.excludedSsidList = new String[] {"ssid1", "ssid2"};
+ policy.requiredProtoPortMap = new HashMap<>();
+ policy.requiredProtoPortMap.put(12, "23,342,123");
+ policy.requiredProtoPortMap.put(23, "789,372,1235");
+ policy.maximumBssLoadValue = 12;
+
+ policy.preferredRoamingPartnerList = new ArrayList<>();
+ Policy.RoamingPartner partner1 = new Policy.RoamingPartner();
+ partner1.fqdn = "partner1.com";
+ partner1.fqdnExactMatch = true;
+ partner1.priority = 12;
+ partner1.countries = "us,jp";
+ Policy.RoamingPartner partner2 = new Policy.RoamingPartner();
+ partner2.fqdn = "partner2.com";
+ partner2.fqdnExactMatch = false;
+ partner2.priority = 42;
+ partner2.countries = "ca,fr";
+ policy.preferredRoamingPartnerList.add(partner1);
+ policy.preferredRoamingPartnerList.add(partner2);
+
+ policy.policyUpdate = new UpdateParameter();
+ policy.policyUpdate.updateIntervalInMinutes = 1712;
+ policy.policyUpdate.updateMethod = UpdateParameter.UPDATE_METHOD_OMADM;
+ policy.policyUpdate.restriction = UpdateParameter.UPDATE_RESTRICTION_HOMESP;
+ policy.policyUpdate.serverUri = "policy.update.com";
+ policy.policyUpdate.username = "username";
+ policy.policyUpdate.base64EncodedPassword =
+ Base64.encodeToString("password".getBytes(), Base64.DEFAULT);
+ policy.policyUpdate.trustRootCertUrl = "trust.cert.com";
+ policy.policyUpdate.trustRootCertSha256Fingerprint = new byte[32];
+
+ return policy;
+ }
+
+ /**
+ * Helper function for verifying Policy after parcel write then read.
+ * @param policyToWrite
+ * @throws Exception
+ */
+ private static void verifyParcel(Policy policyToWrite) throws Exception {
+ Parcel parcel = Parcel.obtain();
+ policyToWrite.writeToParcel(parcel, 0);
+
+ parcel.setDataPosition(0); // Rewind data position back to the beginning for read.
+ Policy policyFromRead = Policy.CREATOR.createFromParcel(parcel);
+ assertTrue(policyFromRead.equals(policyToWrite));
+ }
+
+ /**
+ * Verify parcel read/write for an empty Policy.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyParcelWithEmptyPolicy() throws Exception {
+ verifyParcel(new Policy());
+ }
+
+ /**
+ * Verify parcel read/write for a Policy with all fields set.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyParcelWithFullPolicy() throws Exception {
+ verifyParcel(createPolicy());
+ }
+
+ /**
+ * Verify parcel read/write for a Policy without protocol port map.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyParcelWithoutProtoPortMap() throws Exception {
+ Policy policy = createPolicy();
+ policy.requiredProtoPortMap = null;
+ verifyParcel(policy);
+ }
+
+ /**
+ * Verify parcel read/write for a Policy without preferred roaming partner list.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyParcelWithoutPreferredRoamingPartnerList() throws Exception {
+ Policy policy = createPolicy();
+ policy.preferredRoamingPartnerList = null;
+ verifyParcel(policy);
+ }
+
+ /**
+ * Verify parcel read/write for a Policy without policy update parameters.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyParcelWithoutPolicyUpdate() throws Exception {
+ Policy policy = createPolicy();
+ policy.policyUpdate = null;
+ verifyParcel(policy);
+ }
+
+ /**
+ * Verify that policy created using copy constructor with null source should be the same
+ * as the policy created using default constructor.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyCopyConstructionWithNullSource() throws Exception {
+ Policy copyPolicy = new Policy(null);
+ Policy defaultPolicy = new Policy();
+ assertTrue(defaultPolicy.equals(copyPolicy));
+ }
+
+ /**
+ * Verify that policy created using copy constructor with a valid source should be the
+ * same as the source.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyCopyConstructionWithFullPolicy() throws Exception {
+ Policy policy = createPolicy();
+ Policy copyPolicy = new Policy(policy);
+ assertTrue(policy.equals(copyPolicy));
+ }
+
+ /**
+ * Verify that a default policy (with no informatio) is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validatePolicyWithDefault() throws Exception {
+ Policy policy = new Policy();
+ assertFalse(policy.validate());
+ }
+
+ /**
+ * Verify that a policy created using {@link #createPolicy} is valid, since all fields are
+ * filled in with valid values.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validatePolicyWithFullPolicy() throws Exception {
+ assertTrue(createPolicy().validate());
+ }
+
+ /**
+ * Verify that a policy without policy update parameters is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validatePolicyWithoutPolicyUpdate() throws Exception {
+ Policy policy = createPolicy();
+ policy.policyUpdate = null;
+ assertFalse(policy.validate());
+ }
+
+ /**
+ * Verify that a policy with invalid policy update parameters is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validatePolicyWithInvalidPolicyUpdate() throws Exception {
+ Policy policy = createPolicy();
+ policy.policyUpdate = new UpdateParameter();
+ assertFalse(policy.validate());
+ }
+
+ /**
+ * Verify that a policy with a preferred roaming partner with FQDN not specified is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validatePolicyWithRoamingPartnerWithoutFQDN() throws Exception {
+ Policy policy = createPolicy();
+ Policy.RoamingPartner partner = new Policy.RoamingPartner();
+ partner.fqdnExactMatch = true;
+ partner.priority = 12;
+ partner.countries = "us,jp";
+ policy.preferredRoamingPartnerList.add(partner);
+ assertFalse(policy.validate());
+ }
+
+ /**
+ * Verify that a policy with a preferred roaming partner with countries not specified is
+ * invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validatePolicyWithRoamingPartnerWithoutCountries() throws Exception {
+ Policy policy = createPolicy();
+ Policy.RoamingPartner partner = new Policy.RoamingPartner();
+ partner.fqdn = "test.com";
+ partner.fqdnExactMatch = true;
+ partner.priority = 12;
+ policy.preferredRoamingPartnerList.add(partner);
+ assertFalse(policy.validate());
+ }
+
+ /**
+ * Verify that a policy with a proto-port tuple that contains an invalid port string is
+ * invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validatePolicyWithInvalidPortStringInProtoPortMap() throws Exception {
+ Policy policy = createPolicy();
+ byte[] rawPortBytes = new byte[MAX_PORT_STRING_BYTES + 1];
+ policy.requiredProtoPortMap.put(324, new String(rawPortBytes, StandardCharsets.UTF_8));
+ assertFalse(policy.validate());
+ }
+
+ /**
+ * Verify that a policy with number of excluded SSIDs exceeded the max is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validatePolicyWithSsidExclusionListSizeExceededMax() throws Exception {
+ Policy policy = createPolicy();
+ policy.excludedSsidList = new String[MAX_NUMBER_OF_EXCLUDED_SSIDS + 1];
+ Arrays.fill(policy.excludedSsidList, "ssid");
+ assertFalse(policy.validate());
+ }
+
+ /**
+ * Verify that a policy with an invalid SSID in the excluded SSID list is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validatePolicyWithInvalidSsid() throws Exception {
+ Policy policy = createPolicy();
+ byte[] rawSsidBytes = new byte[MAX_SSID_BYTES + 1];
+ Arrays.fill(rawSsidBytes, (byte) 'a');
+ policy.excludedSsidList = new String[] {new String(rawSsidBytes, StandardCharsets.UTF_8)};
+ assertFalse(policy.validate());
+ }
+}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java
new file mode 100644
index 0000000..6bf0db1b
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2017 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.pps;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Base64;
+
+import org.junit.Test;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Unit tests for {@link android.net.wifi.hotspot2.pps.UpdateParameter}.
+ */
+@SmallTest
+public class UpdateParameterTest {
+ private static final int MAX_URI_BYTES = 1023;
+ private static final int MAX_URL_BYTES = 1023;
+ private static final int MAX_USERNAME_BYTES = 63;
+ private static final int MAX_PASSWORD_BYTES = 255;
+ private static final int CERTIFICATE_SHA256_BYTES = 32;
+
+ /**
+ * Helper function for creating a {@link UpdateParameter} for testing.
+ *
+ * @return {@link UpdateParameter}
+ */
+ private static UpdateParameter createUpdateParameter() {
+ UpdateParameter updateParam = new UpdateParameter();
+ updateParam.updateIntervalInMinutes = 1712;
+ updateParam.updateMethod = UpdateParameter.UPDATE_METHOD_OMADM;
+ updateParam.restriction = UpdateParameter.UPDATE_RESTRICTION_HOMESP;
+ updateParam.serverUri = "server.pdate.com";
+ updateParam.username = "username";
+ updateParam.base64EncodedPassword =
+ Base64.encodeToString("password".getBytes(), Base64.DEFAULT);
+ updateParam.trustRootCertUrl = "trust.cert.com";
+ updateParam.trustRootCertSha256Fingerprint = new byte[32];
+ return updateParam;
+ }
+
+ /**
+ * Helper function for verifying UpdateParameter after parcel write then read.
+ * @param paramToWrite The UpdateParamter to verify
+ * @throws Exception
+ */
+ private static void verifyParcel(UpdateParameter paramToWrite) throws Exception {
+ Parcel parcel = Parcel.obtain();
+ paramToWrite.writeToParcel(parcel, 0);
+
+ parcel.setDataPosition(0); // Rewind data position back to the beginning for read.
+ UpdateParameter paramFromRead = UpdateParameter.CREATOR.createFromParcel(parcel);
+ assertTrue(paramFromRead.equals(paramToWrite));
+ }
+
+ /**
+ * Verify parcel read/write for an empty UpdateParameter.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyParcelWithEmptyUpdateParameter() throws Exception {
+ verifyParcel(new UpdateParameter());
+ }
+
+ /**
+ * Verify parcel read/write for a UpdateParameter with all fields set.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyParcelWithFullUpdateParameter() throws Exception {
+ verifyParcel(createUpdateParameter());
+ }
+
+ /**
+ * Verify that UpdateParameter created using copy constructor with null source should be the
+ * same as the UpdateParameter created using default constructor.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyCopyConstructionWithNullSource() throws Exception {
+ UpdateParameter copyParam = new UpdateParameter(null);
+ UpdateParameter defaultParam = new UpdateParameter();
+ assertTrue(defaultParam.equals(copyParam));
+ }
+
+ /**
+ * Verify that UpdateParameter created using copy constructor with a valid source should be the
+ * same as the source.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyCopyConstructionWithFullUpdateParameter() throws Exception {
+ UpdateParameter origParam = createUpdateParameter();
+ UpdateParameter copyParam = new UpdateParameter(origParam);
+ assertTrue(origParam.equals(copyParam));
+ }
+
+ /**
+ * Verify that a default UpdateParameter is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithDefault() throws Exception {
+ UpdateParameter updateParam = new UpdateParameter();
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter created using {@link #createUpdateParameter} is valid,
+ * since all fields are filled in with valid values.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithFullPolicy() throws Exception {
+ assertTrue(createUpdateParameter().validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with an unknown update method is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithUnknowMethod() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ updateParam.updateMethod = "adsfasd";
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with an unknown restriction is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithUnknowRestriction() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ updateParam.restriction = "adsfasd";
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with an username exceeding maximum size is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithUsernameExceedingMaxSize() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ byte[] rawUsernameBytes = new byte[MAX_USERNAME_BYTES + 1];
+ Arrays.fill(rawUsernameBytes, (byte) 'a');
+ updateParam.username = new String(rawUsernameBytes, StandardCharsets.UTF_8);
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with an empty username is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithEmptyUsername() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ updateParam.username = null;
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with a password exceeding maximum size is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithPasswordExceedingMaxSize() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ byte[] rawPasswordBytes = new byte[MAX_PASSWORD_BYTES + 1];
+ Arrays.fill(rawPasswordBytes, (byte) 'a');
+ updateParam.base64EncodedPassword = new String(rawPasswordBytes, StandardCharsets.UTF_8);
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with an empty password is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithEmptyPassword() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ updateParam.base64EncodedPassword = null;
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with a Base64 encoded password that contained invalid padding
+ * is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithPasswordContainedInvalidPadding() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ updateParam.base64EncodedPassword = updateParam.base64EncodedPassword + "=";
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter without trust root certificate URL is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithoutTrustRootCertUrl() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ updateParam.trustRootCertUrl = null;
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with invalid trust root certificate URL is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithInvalidTrustRootCertUrl() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ byte[] rawUrlBytes = new byte[MAX_URL_BYTES + 1];
+ Arrays.fill(rawUrlBytes, (byte) 'a');
+ updateParam.trustRootCertUrl = new String(rawUrlBytes, StandardCharsets.UTF_8);
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter without trust root certificate SHA-256 fingerprint is
+ * invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithouttrustRootCertSha256Fingerprint() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ updateParam.trustRootCertSha256Fingerprint = null;
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with an incorrect size trust root certificate SHA-256
+ * fingerprint is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithInvalidtrustRootCertSha256Fingerprint() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ updateParam.trustRootCertSha256Fingerprint = new byte[CERTIFICATE_SHA256_BYTES + 1];
+ assertFalse(updateParam.validate());
+
+ updateParam.trustRootCertSha256Fingerprint = new byte[CERTIFICATE_SHA256_BYTES - 1];
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter without server URI is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithoutServerUri() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ updateParam.serverUri = null;
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with an invalid server URI is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validatePolicyWithInvalidServerUri() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ byte[] rawUriBytes = new byte[MAX_URI_BYTES + 1];
+ Arrays.fill(rawUriBytes, (byte) 'a');
+ updateParam.serverUri = new String(rawUriBytes, StandardCharsets.UTF_8);
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with update interval set to "never" will not perform
+ * validation on other parameters, since update is not applicable in this case.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithNoServerCheck() throws Exception {
+ UpdateParameter updateParam = new UpdateParameter();
+ updateParam.updateIntervalInMinutes = UpdateParameter.UPDATE_CHECK_INTERVAL_NEVER;
+ updateParam.username = null;
+ updateParam.base64EncodedPassword = null;
+ updateParam.updateMethod = null;
+ updateParam.restriction = null;
+ updateParam.serverUri = null;
+ updateParam.trustRootCertUrl = null;
+ updateParam.trustRootCertSha256Fingerprint = null;
+ assertTrue(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with unset update interval is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithoutUpdateInterval() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ updateParam.updateIntervalInMinutes = Long.MIN_VALUE;
+ assertFalse(updateParam.validate());
+ }
+}