Merge "Restore TimePickerDialog#onClick behavior."
diff --git a/api/current.txt b/api/current.txt
index 97a6794..ef50a19 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1052,6 +1052,7 @@
field public static final int ratingBarStyleSmall = 16842877; // 0x101007d
field public static final int readPermission = 16842759; // 0x1010007
field public static final int recognitionService = 16843932; // 0x101049c
+ field public static final int recycleEnabled = 16844124; // 0x101055c
field public static final int relinquishTaskIdentity = 16843894; // 0x1010476
field public static final int reparent = 16843964; // 0x10104bc
field public static final int reparentWithOverlay = 16843965; // 0x10104bd
@@ -6565,7 +6566,7 @@
public static class AssistStructure.ViewNode {
method public float getAlpha();
- method public int getAutoFillHint();
+ method public java.lang.String[] getAutoFillHint();
method public android.view.autofill.AutofillId getAutofillId();
method public java.lang.String[] getAutofillOptions();
method public int getAutofillType();
@@ -8822,7 +8823,9 @@
method public abstract deprecated android.graphics.drawable.Drawable peekWallpaper();
method public void registerComponentCallbacks(android.content.ComponentCallbacks);
method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
+ method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, boolean);
method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler);
+ method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler, boolean);
method public abstract deprecated void removeStickyBroadcast(android.content.Intent);
method public abstract deprecated void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public abstract void revokeUriPermission(android.net.Uri, int);
@@ -9011,7 +9014,9 @@
method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory, android.database.DatabaseErrorHandler);
method public deprecated android.graphics.drawable.Drawable peekWallpaper();
method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
+ method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, boolean);
method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler);
+ method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler, boolean);
method public deprecated void removeStickyBroadcast(android.content.Intent);
method public deprecated void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public void revokeUriPermission(android.net.Uri, int);
@@ -9514,6 +9519,7 @@
field public static final int FLAG_RECEIVER_NO_ABORT = 134217728; // 0x8000000
field public static final int FLAG_RECEIVER_REGISTERED_ONLY = 1073741824; // 0x40000000
field public static final int FLAG_RECEIVER_REPLACE_PENDING = 536870912; // 0x20000000
+ field public static final int FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS = 2097152; // 0x200000
field public static final java.lang.String METADATA_DOCK_HOME = "android.dock_home";
field public static final int URI_ALLOW_UNSAFE = 4; // 0x4
field public static final int URI_ANDROID_APP_SCHEME = 2; // 0x2
@@ -25187,8 +25193,8 @@
method public void reportNetworkConnectivity(android.net.Network, boolean);
method public boolean requestBandwidthUpdate(android.net.Network);
method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
- method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback);
method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
+ method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback);
method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
method public deprecated void setNetworkPreference(int);
@@ -26169,6 +26175,16 @@
package android.net.wifi {
+ public final class IconInfo implements android.os.Parcelable {
+ ctor public IconInfo(java.lang.String, byte[]);
+ ctor public IconInfo(android.net.wifi.IconInfo);
+ method public int describeContents();
+ method public byte[] getData();
+ method public java.lang.String getFilename();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.net.wifi.IconInfo> CREATOR;
+ }
+
public class ScanResult implements android.os.Parcelable {
method public int describeContents();
method public boolean is80211mcResponder();
@@ -26368,7 +26384,7 @@
public class WifiManager {
method public int addNetwork(android.net.wifi.WifiConfiguration);
- method public boolean addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration);
+ method public void addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration);
method public static int calculateSignalLevel(int, int);
method public void cancelWps(android.net.wifi.WifiManager.WpsCallback);
method public static int compareSignalLevel(int, int);
@@ -26397,7 +26413,7 @@
method public boolean reassociate();
method public boolean reconnect();
method public boolean removeNetwork(int);
- method public boolean removePasspointConfiguration(java.lang.String);
+ method public void removePasspointConfiguration(java.lang.String);
method public deprecated boolean saveConfiguration();
method public void setTdlsEnabled(java.net.InetAddress, boolean);
method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean);
@@ -26412,26 +26428,21 @@
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
+ field public static final java.lang.String EXTRA_ANQP_ELEMENT_DATA = "android.net.wifi.extra.ANQP_ELEMENT_DATA";
field public static final java.lang.String EXTRA_BSSID = "bssid";
+ field public static final java.lang.String EXTRA_BSSID_LONG = "android.net.wifi.extra.BSSID_LONG";
+ field public static final java.lang.String EXTRA_DELAY = "android.net.wifi.extra.DELAY";
+ field public static final java.lang.String EXTRA_ESS = "android.net.wifi.extra.ESS";
+ field public static final java.lang.String EXTRA_ICON_INFO = "android.net.wifi.extra.ICON_INFO";
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_SUBSCRIPTION_REMEDIATION_METHOD = "android.net.wifi.extra.SUBSCRIPTION_REMEDIATION_METHOD";
field public static final java.lang.String EXTRA_SUPPLICANT_CONNECTED = "connected";
field public static final java.lang.String EXTRA_SUPPLICANT_ERROR = "supplicantError";
+ field public static final java.lang.String EXTRA_URL = "android.net.wifi.extra.URL";
field public static final java.lang.String EXTRA_WIFI_INFO = "wifiInfo";
field public static final java.lang.String EXTRA_WIFI_STATE = "wifi_state";
field public static final java.lang.String NETWORK_IDS_CHANGED_ACTION = "android.net.wifi.NETWORK_IDS_CHANGED";
@@ -26650,7 +26661,6 @@
method public void setUsageLimitStartTimeInMs(long);
method public void setUsageLimitTimeLimitInMinutes(long);
method public void setUsageLimitUsageTimePeriodInMinutes(long);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.PasspointConfiguration> CREATOR;
}
@@ -26691,7 +26701,6 @@
method public void setRealm(java.lang.String);
method public void setSimCredential(android.net.wifi.hotspot2.pps.Credential.SimCredential);
method public void setUserCredential(android.net.wifi.hotspot2.pps.Credential.UserCredential);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential> CREATOR;
}
@@ -26704,7 +26713,6 @@
method public java.lang.String getCertType();
method public void setCertSha256Fingerprint(byte[]);
method public void setCertType(java.lang.String);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.CertificateCredential> CREATOR;
}
@@ -26717,7 +26725,6 @@
method public java.lang.String getImsi();
method public void setEapType(int);
method public void setImsi(java.lang.String);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.SimCredential> CREATOR;
}
@@ -26740,7 +26747,6 @@
method public void setPassword(java.lang.String);
method public void setSoftTokenApp(java.lang.String);
method public void setUsername(java.lang.String);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.UserCredential> CREATOR;
}
@@ -26765,7 +26771,6 @@
method public void setMatchAnyOis(long[]);
method public void setOtherHomePartners(java.lang.String[]);
method public void setRoamingConsortiumOis(long[]);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.HomeSp> CREATOR;
}
@@ -26792,7 +26797,6 @@
method public void setPolicyUpdate(android.net.wifi.hotspot2.pps.UpdateParameter);
method public void setPreferredRoamingPartnerList(java.util.List<android.net.wifi.hotspot2.pps.Policy.RoamingPartner>);
method public void setRequiredProtoPortMap(java.util.Map<java.lang.Integer, java.lang.String>);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Policy> CREATOR;
}
@@ -26809,7 +26813,6 @@
method public void setFqdn(java.lang.String);
method public void setFqdnExactMatch(boolean);
method public void setPriority(int);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Policy.RoamingPartner> CREATOR;
}
@@ -26834,7 +26837,6 @@
method public void setUpdateIntervalInMinutes(long);
method public void setUpdateMethod(java.lang.String);
method public void setUsername(java.lang.String);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.UpdateParameter> CREATOR;
field public static final long UPDATE_CHECK_INTERVAL_NEVER = 4294967295L; // 0xffffffffL
@@ -31915,6 +31917,7 @@
method public boolean hasKey();
method public boolean isEnabled();
method public boolean isPersistent();
+ method public boolean isRecycleEnabled();
method public boolean isSelectable();
method protected void notifyChanged();
method public void notifyDependencyChange(boolean);
@@ -31954,6 +31957,7 @@
method public void setOrder(int);
method public void setPersistent(boolean);
method public void setPreferenceDataStore(android.preference.PreferenceDataStore);
+ method public void setRecycleEnabled(boolean);
method public void setSelectable(boolean);
method public void setShouldDisableView(boolean);
method public void setSummary(java.lang.CharSequence);
@@ -34123,21 +34127,21 @@
method public static android.net.Uri buildRootsUri(java.lang.String);
method public static android.net.Uri buildSearchDocumentsUri(java.lang.String, java.lang.String, java.lang.String);
method public static android.net.Uri buildTreeDocumentUri(java.lang.String, java.lang.String);
- method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri);
- method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String);
- method public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle);
- method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri);
- method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri);
+ method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
+ method public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException;
+ method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
method public static java.lang.String getDocumentId(android.net.Uri);
- method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal);
+ method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public static java.lang.String getRootId(android.net.Uri);
method public static java.lang.String getSearchDocumentsQuery(android.net.Uri);
method public static java.lang.String getTreeDocumentId(android.net.Uri);
method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
method public static boolean isTreeUri(android.net.Uri);
- method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri);
- method public static boolean removeDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri);
- method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String);
+ method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static boolean removeDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
field public static final java.lang.String ACTION_DOCUMENT_SETTINGS = "android.provider.action.DOCUMENT_SETTINGS";
field public static final java.lang.String EXTRA_ERROR = "error";
field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
@@ -34645,6 +34649,7 @@
field public static final java.lang.String ACTION_NFCSHARING_SETTINGS = "android.settings.NFCSHARING_SETTINGS";
field public static final java.lang.String ACTION_NFC_PAYMENT_SETTINGS = "android.settings.NFC_PAYMENT_SETTINGS";
field public static final java.lang.String ACTION_NFC_SETTINGS = "android.settings.NFC_SETTINGS";
+ field public static final java.lang.String ACTION_NIGHT_DISPLAY_SETTINGS = "android.settings.NIGHT_DISPLAY_SETTINGS";
field public static final java.lang.String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS";
field public static final java.lang.String ACTION_PRINT_SETTINGS = "android.settings.ACTION_PRINT_SETTINGS";
@@ -39173,6 +39178,7 @@
method public android.os.PersistableBundle getConfigForSubId(int);
method public void notifyConfigChangedForSubId(int);
field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
+ field public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe
field public static final java.lang.String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
field public static final java.lang.String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
field public static final java.lang.String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
@@ -39215,6 +39221,8 @@
field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string";
field public static final java.lang.String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING = "config_ims_package_override_string";
field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
+ field public static final java.lang.String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
+ field public static final java.lang.String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG = "data_warning_threshold_bytes_long";
field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
field public static final java.lang.String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
field public static final java.lang.String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array";
@@ -39270,6 +39278,7 @@
field public static final java.lang.String KEY_MMS_UA_PROF_TAG_NAME_STRING = "uaProfTagName";
field public static final java.lang.String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl";
field public static final java.lang.String KEY_MMS_USER_AGENT_STRING = "userAgent";
+ field public static final java.lang.String KEY_MONTHLY_DATA_CYCLE_DAY_INT = "monthly_data_cycle_day_int";
field public static final java.lang.String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
field public static final java.lang.String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
field public static final java.lang.String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
@@ -40498,7 +40507,9 @@
method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory, android.database.DatabaseErrorHandler);
method public android.graphics.drawable.Drawable peekWallpaper();
method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
+ method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, boolean);
method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler);
+ method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler, boolean);
method public void removeStickyBroadcast(android.content.Intent);
method public void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public void revokeUriPermission(android.net.Uri, int);
@@ -45091,7 +45102,7 @@
method public float getAlpha();
method public android.view.animation.Animation getAnimation();
method public android.os.IBinder getApplicationWindowToken();
- method public int getAutofillHint();
+ method public java.lang.String[] getAutofillHint();
method public int getAutofillMode();
method public int getAutofillType();
method public android.view.autofill.AutofillValue getAutofillValue();
@@ -45412,7 +45423,7 @@
method public void setActivated(boolean);
method public void setAlpha(float);
method public void setAnimation(android.view.animation.Animation);
- method public void setAutofillHint(int);
+ method public void setAutofillHint(java.lang.String[]);
method public void setAutofillMode(int);
method public void setBackground(android.graphics.drawable.Drawable);
method public void setBackgroundColor(int);
@@ -45555,20 +45566,19 @@
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 AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE = 512; // 0x200
- field public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY = 4096; // 0x1000
- field public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH = 1024; // 0x400
- field public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR = 2048; // 0x800
- field public static final int AUTOFILL_HINT_CREDIT_CARD_NUMBER = 128; // 0x80
- field public static final int AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE = 256; // 0x100
- field public static final int AUTOFILL_HINT_EMAIL_ADDRESS = 1; // 0x1
- field public static final int AUTOFILL_HINT_NAME = 2; // 0x2
- field public static final int AUTOFILL_HINT_NONE = 0; // 0x0
- field public static final int AUTOFILL_HINT_PASSWORD = 8; // 0x8
- field public static final int AUTOFILL_HINT_PHONE = 16; // 0x10
- field public static final int AUTOFILL_HINT_POSTAL_ADDRESS = 32; // 0x20
- field public static final int AUTOFILL_HINT_POSTAL_CODE = 64; // 0x40
- field public static final int AUTOFILL_HINT_USERNAME = 4; // 0x4
+ field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE = "creditCardExpirationDate";
+ field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY = "creditCardExpirationDay";
+ field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH = "creditCardExpirationMonth";
+ field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR = "creditCardExpirationYear";
+ field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_NUMBER = "creditCardNumber";
+ field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE = "creditCardSecurityCode";
+ field public static final java.lang.String AUTOFILL_HINT_EMAIL_ADDRESS = "emailAddress";
+ field public static final java.lang.String AUTOFILL_HINT_NAME = "name";
+ field public static final java.lang.String AUTOFILL_HINT_PASSWORD = "password";
+ field public static final java.lang.String AUTOFILL_HINT_PHONE = "phone";
+ field public static final java.lang.String AUTOFILL_HINT_POSTAL_ADDRESS = "postalAddress";
+ field public static final java.lang.String AUTOFILL_HINT_POSTAL_CODE = "postalCode";
+ field public static final java.lang.String AUTOFILL_HINT_USERNAME = "username";
field public static final int AUTOFILL_MODE_AUTO = 1; // 0x1
field public static final int AUTOFILL_MODE_INHERIT = 0; // 0x0
field public static final int AUTOFILL_MODE_MANUAL = 2; // 0x2
@@ -46230,7 +46240,7 @@
method public abstract void setAccessibilityFocused(boolean);
method public abstract void setActivated(boolean);
method public abstract void setAlpha(float);
- method public abstract void setAutofillHint(int);
+ method public abstract void setAutofillHint(java.lang.String[]);
method public abstract void setAutofillOptions(java.lang.String[]);
method public abstract void setAutofillType(int);
method public abstract void setAutofillValue(android.view.autofill.AutofillValue);
@@ -46241,6 +46251,7 @@
method public abstract void setClickable(boolean);
method public abstract void setContentDescription(java.lang.CharSequence);
method public abstract void setContextClickable(boolean);
+ method public abstract void setDataIsSensitive(boolean);
method public abstract void setDimens(int, int, int, int, int, int);
method public abstract void setElevation(float);
method public abstract void setEnabled(boolean);
@@ -46251,7 +46262,6 @@
method public abstract void setInputType(int);
method public abstract void setLongClickable(boolean);
method public abstract void setOpaque(boolean);
- method public abstract void setSanitized(boolean);
method public abstract void setSelected(boolean);
method public abstract void setText(java.lang.CharSequence);
method public abstract void setText(java.lang.CharSequence, int, int);
@@ -47494,15 +47504,16 @@
}
public final class AutofillManager {
+ method public void cancel();
+ method public void commit();
+ method public void notifyValueChanged(android.view.View);
+ method public void notifyViewEntered(android.view.View);
+ method public void notifyViewExited(android.view.View);
+ method public void notifyVirtualValueChanged(android.view.View, int, android.view.autofill.AutofillValue);
+ method public void notifyVirtualViewEntered(android.view.View, int, android.graphics.Rect);
+ method public void notifyVirtualViewExited(android.view.View, int);
method public void registerCallback(android.view.autofill.AutofillManager.AutofillCallback);
- method public void reset();
- method public void startAutofillRequest(android.view.View);
- method public void startAutofillRequestOnVirtualView(android.view.View, int, android.graphics.Rect);
- method public void stopAutofillRequest(android.view.View);
- method public void stopAutofillRequestOnVirtualView(android.view.View, int);
method public void unregisterCallback(android.view.autofill.AutofillManager.AutofillCallback);
- method public void valueChanged(android.view.View);
- method public void virtualValueChanged(android.view.View, int, android.view.autofill.AutofillValue);
field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
}
@@ -47525,6 +47536,10 @@
method public int getListValue();
method public java.lang.CharSequence getTextValue();
method public boolean getToggleValue();
+ method public boolean isDate();
+ method public boolean isList();
+ method public boolean isText();
+ method public boolean isToggle();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.autofill.AutofillValue> CREATOR;
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 517c748..1e62eb1 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1165,6 +1165,7 @@
field public static final int ratingBarStyleSmall = 16842877; // 0x101007d
field public static final int readPermission = 16842759; // 0x1010007
field public static final int recognitionService = 16843932; // 0x101049c
+ field public static final int recycleEnabled = 16844124; // 0x101055c
field public static final int relinquishTaskIdentity = 16843894; // 0x1010476
field public static final int reparent = 16843964; // 0x10104bc
field public static final int reparentWithOverlay = 16843965; // 0x10104bd
@@ -6815,7 +6816,7 @@
public static class AssistStructure.ViewNode {
method public float getAlpha();
- method public int getAutoFillHint();
+ method public java.lang.String[] getAutoFillHint();
method public android.view.autofill.AutofillId getAutofillId();
method public java.lang.String[] getAutofillOptions();
method public int getAutofillType();
@@ -9327,7 +9328,9 @@
method public abstract deprecated android.graphics.drawable.Drawable peekWallpaper();
method public void registerComponentCallbacks(android.content.ComponentCallbacks);
method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
+ method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, boolean);
method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler);
+ method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler, boolean);
method public abstract deprecated void removeStickyBroadcast(android.content.Intent);
method public abstract deprecated void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public abstract void revokeUriPermission(android.net.Uri, int);
@@ -9530,7 +9533,9 @@
method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory, android.database.DatabaseErrorHandler);
method public deprecated android.graphics.drawable.Drawable peekWallpaper();
method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
+ method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, boolean);
method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler);
+ method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler, boolean);
method public deprecated void removeStickyBroadcast(android.content.Intent);
method public deprecated void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public void revokeUriPermission(android.net.Uri, int);
@@ -10055,6 +10060,7 @@
field public static final int FLAG_RECEIVER_NO_ABORT = 134217728; // 0x8000000
field public static final int FLAG_RECEIVER_REGISTERED_ONLY = 1073741824; // 0x40000000
field public static final int FLAG_RECEIVER_REPLACE_PENDING = 536870912; // 0x20000000
+ field public static final int FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS = 2097152; // 0x200000
field public static final java.lang.String METADATA_DOCK_HOME = "android.dock_home";
field public static final int URI_ALLOW_UNSAFE = 4; // 0x4
field public static final int URI_ANDROID_APP_SCHEME = 2; // 0x2
@@ -27306,8 +27312,8 @@
method public void reportNetworkConnectivity(android.net.Network, boolean);
method public boolean requestBandwidthUpdate(android.net.Network);
method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
- method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback);
method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
+ method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback);
method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
method public deprecated void setNetworkPreference(int);
@@ -27715,7 +27721,6 @@
method public boolean clearScores() throws java.lang.SecurityException;
method public void disableScoring() throws java.lang.SecurityException;
method public java.lang.String getActiveScorerPackage();
- method public android.net.RecommendationResult requestRecommendation(android.net.RecommendationRequest) throws java.lang.SecurityException;
method public boolean setActiveScorer(java.lang.String) throws java.lang.SecurityException;
method public boolean updateScores(android.net.ScoredNetwork[]) throws java.lang.SecurityException;
field public static final java.lang.String ACTION_CHANGE_ACTIVE = "android.net.scoring.CHANGE_ACTIVE";
@@ -28458,6 +28463,16 @@
field public boolean truncated;
}
+ public final class IconInfo implements android.os.Parcelable {
+ ctor public IconInfo(java.lang.String, byte[]);
+ ctor public IconInfo(android.net.wifi.IconInfo);
+ method public int describeContents();
+ method public byte[] getData();
+ method public java.lang.String getFilename();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.net.wifi.IconInfo> CREATOR;
+ }
+
public class RttManager {
method public void disableResponder(android.net.wifi.RttManager.ResponderCallback);
method public void enableResponder(android.net.wifi.RttManager.ResponderCallback);
@@ -28884,7 +28899,7 @@
public class WifiManager {
method public int addNetwork(android.net.wifi.WifiConfiguration);
- method public boolean addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration);
+ method public void addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration);
method public static int calculateSignalLevel(int, int);
method public void cancelWps(android.net.wifi.WifiManager.WpsCallback);
method public static int compareSignalLevel(int, int);
@@ -28924,7 +28939,7 @@
method public boolean reassociate();
method public boolean reconnect();
method public boolean removeNetwork(int);
- method public boolean removePasspointConfiguration(java.lang.String);
+ method public void removePasspointConfiguration(java.lang.String);
method public deprecated boolean saveConfiguration();
method public void setTdlsEnabled(java.net.InetAddress, boolean);
method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean);
@@ -28947,29 +28962,24 @@
field public static final int CHANGE_REASON_REMOVED = 1; // 0x1
field public static final java.lang.String CONFIGURED_NETWORKS_CHANGED_ACTION = "android.net.wifi.CONFIGURED_NETWORKS_CHANGE";
field public static final int ERROR_AUTHENTICATING = 1; // 0x1
+ field public static final java.lang.String EXTRA_ANQP_ELEMENT_DATA = "android.net.wifi.extra.ANQP_ELEMENT_DATA";
field public static final java.lang.String EXTRA_BSSID = "bssid";
+ field public static final java.lang.String EXTRA_BSSID_LONG = "android.net.wifi.extra.BSSID_LONG";
field public static final java.lang.String EXTRA_CHANGE_REASON = "changeReason";
+ field public static final java.lang.String EXTRA_DELAY = "android.net.wifi.extra.DELAY";
+ field public static final java.lang.String EXTRA_ESS = "android.net.wifi.extra.ESS";
+ field public static final java.lang.String EXTRA_ICON_INFO = "android.net.wifi.extra.ICON_INFO";
field public static final java.lang.String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
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";
+ field public static final java.lang.String EXTRA_SUBSCRIPTION_REMEDIATION_METHOD = "android.net.wifi.extra.SUBSCRIPTION_REMEDIATION_METHOD";
field public static final java.lang.String EXTRA_SUPPLICANT_CONNECTED = "connected";
field public static final java.lang.String EXTRA_SUPPLICANT_ERROR = "supplicantError";
+ field public static final java.lang.String EXTRA_URL = "android.net.wifi.extra.URL";
field public static final java.lang.String EXTRA_WIFI_AP_STATE = "wifi_state";
field public static final java.lang.String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration";
field public static final java.lang.String EXTRA_WIFI_CREDENTIAL_EVENT_TYPE = "et";
@@ -29348,7 +29358,6 @@
method public void setUsageLimitStartTimeInMs(long);
method public void setUsageLimitTimeLimitInMinutes(long);
method public void setUsageLimitUsageTimePeriodInMinutes(long);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.PasspointConfiguration> CREATOR;
}
@@ -29389,7 +29398,6 @@
method public void setRealm(java.lang.String);
method public void setSimCredential(android.net.wifi.hotspot2.pps.Credential.SimCredential);
method public void setUserCredential(android.net.wifi.hotspot2.pps.Credential.UserCredential);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential> CREATOR;
}
@@ -29402,7 +29410,6 @@
method public java.lang.String getCertType();
method public void setCertSha256Fingerprint(byte[]);
method public void setCertType(java.lang.String);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.CertificateCredential> CREATOR;
}
@@ -29415,7 +29422,6 @@
method public java.lang.String getImsi();
method public void setEapType(int);
method public void setImsi(java.lang.String);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.SimCredential> CREATOR;
}
@@ -29438,7 +29444,6 @@
method public void setPassword(java.lang.String);
method public void setSoftTokenApp(java.lang.String);
method public void setUsername(java.lang.String);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.UserCredential> CREATOR;
}
@@ -29463,7 +29468,6 @@
method public void setMatchAnyOis(long[]);
method public void setOtherHomePartners(java.lang.String[]);
method public void setRoamingConsortiumOis(long[]);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.HomeSp> CREATOR;
}
@@ -29490,7 +29494,6 @@
method public void setPolicyUpdate(android.net.wifi.hotspot2.pps.UpdateParameter);
method public void setPreferredRoamingPartnerList(java.util.List<android.net.wifi.hotspot2.pps.Policy.RoamingPartner>);
method public void setRequiredProtoPortMap(java.util.Map<java.lang.Integer, java.lang.String>);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Policy> CREATOR;
}
@@ -29507,7 +29510,6 @@
method public void setFqdn(java.lang.String);
method public void setFqdnExactMatch(boolean);
method public void setPriority(int);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Policy.RoamingPartner> CREATOR;
}
@@ -29532,7 +29534,6 @@
method public void setUpdateIntervalInMinutes(long);
method public void setUpdateMethod(java.lang.String);
method public void setUsername(java.lang.String);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.UpdateParameter> CREATOR;
field public static final long UPDATE_CHECK_INTERVAL_NEVER = 4294967295L; // 0xffffffffL
@@ -34764,6 +34765,7 @@
method public boolean hasKey();
method public boolean isEnabled();
method public boolean isPersistent();
+ method public boolean isRecycleEnabled();
method public boolean isSelectable();
method protected void notifyChanged();
method public void notifyDependencyChange(boolean);
@@ -34803,6 +34805,7 @@
method public void setOrder(int);
method public void setPersistent(boolean);
method public void setPreferenceDataStore(android.preference.PreferenceDataStore);
+ method public void setRecycleEnabled(boolean);
method public void setSelectable(boolean);
method public void setShouldDisableView(boolean);
method public void setSummary(java.lang.CharSequence);
@@ -37028,21 +37031,21 @@
method public static android.net.Uri buildRootsUri(java.lang.String);
method public static android.net.Uri buildSearchDocumentsUri(java.lang.String, java.lang.String, java.lang.String);
method public static android.net.Uri buildTreeDocumentUri(java.lang.String, java.lang.String);
- method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri);
- method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String);
- method public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle);
- method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri);
- method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri);
+ method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
+ method public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException;
+ method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
method public static java.lang.String getDocumentId(android.net.Uri);
- method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal);
+ method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public static java.lang.String getRootId(android.net.Uri);
method public static java.lang.String getSearchDocumentsQuery(android.net.Uri);
method public static java.lang.String getTreeDocumentId(android.net.Uri);
method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
method public static boolean isTreeUri(android.net.Uri);
- method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri);
- method public static boolean removeDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri);
- method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String);
+ method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static boolean removeDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
field public static final java.lang.String ACTION_DOCUMENT_SETTINGS = "android.provider.action.DOCUMENT_SETTINGS";
field public static final java.lang.String EXTRA_ERROR = "error";
field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
@@ -37654,6 +37657,7 @@
field public static final java.lang.String ACTION_NFCSHARING_SETTINGS = "android.settings.NFCSHARING_SETTINGS";
field public static final java.lang.String ACTION_NFC_PAYMENT_SETTINGS = "android.settings.NFC_PAYMENT_SETTINGS";
field public static final java.lang.String ACTION_NFC_SETTINGS = "android.settings.NFC_SETTINGS";
+ field public static final java.lang.String ACTION_NIGHT_DISPLAY_SETTINGS = "android.settings.NIGHT_DISPLAY_SETTINGS";
field public static final java.lang.String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS";
field public static final java.lang.String ACTION_PRINT_SETTINGS = "android.settings.ACTION_PRINT_SETTINGS";
@@ -42532,6 +42536,7 @@
method public void notifyConfigChangedForSubId(int);
method public void updateConfigForPhoneId(int, java.lang.String);
field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
+ field public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe
field public static final java.lang.String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
field public static final java.lang.String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
field public static final java.lang.String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
@@ -42574,6 +42579,8 @@
field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string";
field public static final java.lang.String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING = "config_ims_package_override_string";
field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
+ field public static final java.lang.String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
+ field public static final java.lang.String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG = "data_warning_threshold_bytes_long";
field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
field public static final java.lang.String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
field public static final java.lang.String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array";
@@ -42629,6 +42636,7 @@
field public static final java.lang.String KEY_MMS_UA_PROF_TAG_NAME_STRING = "uaProfTagName";
field public static final java.lang.String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl";
field public static final java.lang.String KEY_MMS_USER_AGENT_STRING = "userAgent";
+ field public static final java.lang.String KEY_MONTHLY_DATA_CYCLE_DAY_INT = "monthly_data_cycle_day_int";
field public static final java.lang.String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
field public static final java.lang.String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
field public static final java.lang.String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
@@ -43939,7 +43947,9 @@
method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory, android.database.DatabaseErrorHandler);
method public android.graphics.drawable.Drawable peekWallpaper();
method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
+ method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, boolean);
method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler);
+ method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler, boolean);
method public void removeStickyBroadcast(android.content.Intent);
method public void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public void revokeUriPermission(android.net.Uri, int);
@@ -48551,7 +48561,7 @@
method public float getAlpha();
method public android.view.animation.Animation getAnimation();
method public android.os.IBinder getApplicationWindowToken();
- method public int getAutofillHint();
+ method public java.lang.String[] getAutofillHint();
method public int getAutofillMode();
method public int getAutofillType();
method public android.view.autofill.AutofillValue getAutofillValue();
@@ -48872,7 +48882,7 @@
method public void setActivated(boolean);
method public void setAlpha(float);
method public void setAnimation(android.view.animation.Animation);
- method public void setAutofillHint(int);
+ method public void setAutofillHint(java.lang.String[]);
method public void setAutofillMode(int);
method public void setBackground(android.graphics.drawable.Drawable);
method public void setBackgroundColor(int);
@@ -49015,20 +49025,19 @@
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 AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE = 512; // 0x200
- field public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY = 4096; // 0x1000
- field public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH = 1024; // 0x400
- field public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR = 2048; // 0x800
- field public static final int AUTOFILL_HINT_CREDIT_CARD_NUMBER = 128; // 0x80
- field public static final int AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE = 256; // 0x100
- field public static final int AUTOFILL_HINT_EMAIL_ADDRESS = 1; // 0x1
- field public static final int AUTOFILL_HINT_NAME = 2; // 0x2
- field public static final int AUTOFILL_HINT_NONE = 0; // 0x0
- field public static final int AUTOFILL_HINT_PASSWORD = 8; // 0x8
- field public static final int AUTOFILL_HINT_PHONE = 16; // 0x10
- field public static final int AUTOFILL_HINT_POSTAL_ADDRESS = 32; // 0x20
- field public static final int AUTOFILL_HINT_POSTAL_CODE = 64; // 0x40
- field public static final int AUTOFILL_HINT_USERNAME = 4; // 0x4
+ field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE = "creditCardExpirationDate";
+ field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY = "creditCardExpirationDay";
+ field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH = "creditCardExpirationMonth";
+ field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR = "creditCardExpirationYear";
+ field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_NUMBER = "creditCardNumber";
+ field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE = "creditCardSecurityCode";
+ field public static final java.lang.String AUTOFILL_HINT_EMAIL_ADDRESS = "emailAddress";
+ field public static final java.lang.String AUTOFILL_HINT_NAME = "name";
+ field public static final java.lang.String AUTOFILL_HINT_PASSWORD = "password";
+ field public static final java.lang.String AUTOFILL_HINT_PHONE = "phone";
+ field public static final java.lang.String AUTOFILL_HINT_POSTAL_ADDRESS = "postalAddress";
+ field public static final java.lang.String AUTOFILL_HINT_POSTAL_CODE = "postalCode";
+ field public static final java.lang.String AUTOFILL_HINT_USERNAME = "username";
field public static final int AUTOFILL_MODE_AUTO = 1; // 0x1
field public static final int AUTOFILL_MODE_INHERIT = 0; // 0x0
field public static final int AUTOFILL_MODE_MANUAL = 2; // 0x2
@@ -49690,7 +49699,7 @@
method public abstract void setAccessibilityFocused(boolean);
method public abstract void setActivated(boolean);
method public abstract void setAlpha(float);
- method public abstract void setAutofillHint(int);
+ method public abstract void setAutofillHint(java.lang.String[]);
method public abstract void setAutofillOptions(java.lang.String[]);
method public abstract void setAutofillType(int);
method public abstract void setAutofillValue(android.view.autofill.AutofillValue);
@@ -49701,6 +49710,7 @@
method public abstract void setClickable(boolean);
method public abstract void setContentDescription(java.lang.CharSequence);
method public abstract void setContextClickable(boolean);
+ method public abstract void setDataIsSensitive(boolean);
method public abstract void setDimens(int, int, int, int, int, int);
method public abstract void setElevation(float);
method public abstract void setEnabled(boolean);
@@ -49711,7 +49721,6 @@
method public abstract void setInputType(int);
method public abstract void setLongClickable(boolean);
method public abstract void setOpaque(boolean);
- method public abstract void setSanitized(boolean);
method public abstract void setSelected(boolean);
method public abstract void setText(java.lang.CharSequence);
method public abstract void setText(java.lang.CharSequence, int, int);
@@ -50957,15 +50966,16 @@
}
public final class AutofillManager {
+ method public void cancel();
+ method public void commit();
+ method public void notifyValueChanged(android.view.View);
+ method public void notifyViewEntered(android.view.View);
+ method public void notifyViewExited(android.view.View);
+ method public void notifyVirtualValueChanged(android.view.View, int, android.view.autofill.AutofillValue);
+ method public void notifyVirtualViewEntered(android.view.View, int, android.graphics.Rect);
+ method public void notifyVirtualViewExited(android.view.View, int);
method public void registerCallback(android.view.autofill.AutofillManager.AutofillCallback);
- method public void reset();
- method public void startAutofillRequest(android.view.View);
- method public void startAutofillRequestOnVirtualView(android.view.View, int, android.graphics.Rect);
- method public void stopAutofillRequest(android.view.View);
- method public void stopAutofillRequestOnVirtualView(android.view.View, int);
method public void unregisterCallback(android.view.autofill.AutofillManager.AutofillCallback);
- method public void valueChanged(android.view.View);
- method public void virtualValueChanged(android.view.View, int, android.view.autofill.AutofillValue);
field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
}
@@ -50988,6 +50998,10 @@
method public int getListValue();
method public java.lang.CharSequence getTextValue();
method public boolean getToggleValue();
+ method public boolean isDate();
+ method public boolean isList();
+ method public boolean isText();
+ method public boolean isToggle();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.autofill.AutofillValue> CREATOR;
}
diff --git a/api/test-current.txt b/api/test-current.txt
index bcd1614..185afcb 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1052,6 +1052,7 @@
field public static final int ratingBarStyleSmall = 16842877; // 0x101007d
field public static final int readPermission = 16842759; // 0x1010007
field public static final int recognitionService = 16843932; // 0x101049c
+ field public static final int recycleEnabled = 16844124; // 0x101055c
field public static final int relinquishTaskIdentity = 16843894; // 0x1010476
field public static final int reparent = 16843964; // 0x10104bc
field public static final int reparentWithOverlay = 16843965; // 0x10104bd
@@ -6592,7 +6593,7 @@
public static class AssistStructure.ViewNode {
method public float getAlpha();
- method public int getAutoFillHint();
+ method public java.lang.String[] getAutoFillHint();
method public android.view.autofill.AutofillId getAutofillId();
method public java.lang.String[] getAutofillOptions();
method public int getAutofillType();
@@ -8851,7 +8852,9 @@
method public abstract deprecated android.graphics.drawable.Drawable peekWallpaper();
method public void registerComponentCallbacks(android.content.ComponentCallbacks);
method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
+ method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, boolean);
method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler);
+ method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler, boolean);
method public abstract deprecated void removeStickyBroadcast(android.content.Intent);
method public abstract deprecated void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public abstract void revokeUriPermission(android.net.Uri, int);
@@ -9041,7 +9044,9 @@
method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory, android.database.DatabaseErrorHandler);
method public deprecated android.graphics.drawable.Drawable peekWallpaper();
method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
+ method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, boolean);
method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler);
+ method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler, boolean);
method public deprecated void removeStickyBroadcast(android.content.Intent);
method public deprecated void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public void revokeUriPermission(android.net.Uri, int);
@@ -9544,6 +9549,7 @@
field public static final int FLAG_RECEIVER_NO_ABORT = 134217728; // 0x8000000
field public static final int FLAG_RECEIVER_REGISTERED_ONLY = 1073741824; // 0x40000000
field public static final int FLAG_RECEIVER_REPLACE_PENDING = 536870912; // 0x20000000
+ field public static final int FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS = 2097152; // 0x200000
field public static final java.lang.String METADATA_DOCK_HOME = "android.dock_home";
field public static final int URI_ALLOW_UNSAFE = 4; // 0x4
field public static final int URI_ANDROID_APP_SCHEME = 2; // 0x2
@@ -25288,8 +25294,8 @@
method public void reportNetworkConnectivity(android.net.Network, boolean);
method public boolean requestBandwidthUpdate(android.net.Network);
method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
- method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback);
method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
+ method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback);
method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
method public deprecated void setNetworkPreference(int);
@@ -26270,6 +26276,16 @@
package android.net.wifi {
+ public final class IconInfo implements android.os.Parcelable {
+ ctor public IconInfo(java.lang.String, byte[]);
+ ctor public IconInfo(android.net.wifi.IconInfo);
+ method public int describeContents();
+ method public byte[] getData();
+ method public java.lang.String getFilename();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.net.wifi.IconInfo> CREATOR;
+ }
+
public class ScanResult implements android.os.Parcelable {
method public int describeContents();
method public boolean is80211mcResponder();
@@ -26469,7 +26485,7 @@
public class WifiManager {
method public int addNetwork(android.net.wifi.WifiConfiguration);
- method public boolean addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration);
+ method public void addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration);
method public static int calculateSignalLevel(int, int);
method public void cancelWps(android.net.wifi.WifiManager.WpsCallback);
method public static int compareSignalLevel(int, int);
@@ -26498,7 +26514,7 @@
method public boolean reassociate();
method public boolean reconnect();
method public boolean removeNetwork(int);
- method public boolean removePasspointConfiguration(java.lang.String);
+ method public void removePasspointConfiguration(java.lang.String);
method public deprecated boolean saveConfiguration();
method public void setTdlsEnabled(java.net.InetAddress, boolean);
method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean);
@@ -26513,26 +26529,21 @@
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
+ field public static final java.lang.String EXTRA_ANQP_ELEMENT_DATA = "android.net.wifi.extra.ANQP_ELEMENT_DATA";
field public static final java.lang.String EXTRA_BSSID = "bssid";
+ field public static final java.lang.String EXTRA_BSSID_LONG = "android.net.wifi.extra.BSSID_LONG";
+ field public static final java.lang.String EXTRA_DELAY = "android.net.wifi.extra.DELAY";
+ field public static final java.lang.String EXTRA_ESS = "android.net.wifi.extra.ESS";
+ field public static final java.lang.String EXTRA_ICON_INFO = "android.net.wifi.extra.ICON_INFO";
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_SUBSCRIPTION_REMEDIATION_METHOD = "android.net.wifi.extra.SUBSCRIPTION_REMEDIATION_METHOD";
field public static final java.lang.String EXTRA_SUPPLICANT_CONNECTED = "connected";
field public static final java.lang.String EXTRA_SUPPLICANT_ERROR = "supplicantError";
+ field public static final java.lang.String EXTRA_URL = "android.net.wifi.extra.URL";
field public static final java.lang.String EXTRA_WIFI_INFO = "wifiInfo";
field public static final java.lang.String EXTRA_WIFI_STATE = "wifi_state";
field public static final java.lang.String NETWORK_IDS_CHANGED_ACTION = "android.net.wifi.NETWORK_IDS_CHANGED";
@@ -26751,7 +26762,6 @@
method public void setUsageLimitStartTimeInMs(long);
method public void setUsageLimitTimeLimitInMinutes(long);
method public void setUsageLimitUsageTimePeriodInMinutes(long);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.PasspointConfiguration> CREATOR;
}
@@ -26792,7 +26802,6 @@
method public void setRealm(java.lang.String);
method public void setSimCredential(android.net.wifi.hotspot2.pps.Credential.SimCredential);
method public void setUserCredential(android.net.wifi.hotspot2.pps.Credential.UserCredential);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential> CREATOR;
}
@@ -26805,7 +26814,6 @@
method public java.lang.String getCertType();
method public void setCertSha256Fingerprint(byte[]);
method public void setCertType(java.lang.String);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.CertificateCredential> CREATOR;
}
@@ -26818,7 +26826,6 @@
method public java.lang.String getImsi();
method public void setEapType(int);
method public void setImsi(java.lang.String);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.SimCredential> CREATOR;
}
@@ -26841,7 +26848,6 @@
method public void setPassword(java.lang.String);
method public void setSoftTokenApp(java.lang.String);
method public void setUsername(java.lang.String);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.UserCredential> CREATOR;
}
@@ -26866,7 +26872,6 @@
method public void setMatchAnyOis(long[]);
method public void setOtherHomePartners(java.lang.String[]);
method public void setRoamingConsortiumOis(long[]);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.HomeSp> CREATOR;
}
@@ -26893,7 +26898,6 @@
method public void setPolicyUpdate(android.net.wifi.hotspot2.pps.UpdateParameter);
method public void setPreferredRoamingPartnerList(java.util.List<android.net.wifi.hotspot2.pps.Policy.RoamingPartner>);
method public void setRequiredProtoPortMap(java.util.Map<java.lang.Integer, java.lang.String>);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Policy> CREATOR;
}
@@ -26910,7 +26914,6 @@
method public void setFqdn(java.lang.String);
method public void setFqdnExactMatch(boolean);
method public void setPriority(int);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Policy.RoamingPartner> CREATOR;
}
@@ -26935,7 +26938,6 @@
method public void setUpdateIntervalInMinutes(long);
method public void setUpdateMethod(java.lang.String);
method public void setUsername(java.lang.String);
- method public boolean validate();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.UpdateParameter> CREATOR;
field public static final long UPDATE_CHECK_INTERVAL_NEVER = 4294967295L; // 0xffffffffL
@@ -32039,6 +32041,7 @@
method public boolean hasKey();
method public boolean isEnabled();
method public boolean isPersistent();
+ method public boolean isRecycleEnabled();
method public boolean isSelectable();
method protected void notifyChanged();
method public void notifyDependencyChange(boolean);
@@ -32078,6 +32081,7 @@
method public void setOrder(int);
method public void setPersistent(boolean);
method public void setPreferenceDataStore(android.preference.PreferenceDataStore);
+ method public void setRecycleEnabled(boolean);
method public void setSelectable(boolean);
method public void setShouldDisableView(boolean);
method public void setSummary(java.lang.CharSequence);
@@ -34250,21 +34254,21 @@
method public static android.net.Uri buildRootsUri(java.lang.String);
method public static android.net.Uri buildSearchDocumentsUri(java.lang.String, java.lang.String, java.lang.String);
method public static android.net.Uri buildTreeDocumentUri(java.lang.String, java.lang.String);
- method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri);
- method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String);
- method public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle);
- method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri);
- method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri);
+ method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
+ method public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException;
+ method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
method public static java.lang.String getDocumentId(android.net.Uri);
- method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal);
+ method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public static java.lang.String getRootId(android.net.Uri);
method public static java.lang.String getSearchDocumentsQuery(android.net.Uri);
method public static java.lang.String getTreeDocumentId(android.net.Uri);
method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
method public static boolean isTreeUri(android.net.Uri);
- method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri);
- method public static boolean removeDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri);
- method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String);
+ method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static boolean removeDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
field public static final java.lang.String ACTION_DOCUMENT_SETTINGS = "android.provider.action.DOCUMENT_SETTINGS";
field public static final java.lang.String EXTRA_ERROR = "error";
field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
@@ -34773,6 +34777,7 @@
field public static final java.lang.String ACTION_NFCSHARING_SETTINGS = "android.settings.NFCSHARING_SETTINGS";
field public static final java.lang.String ACTION_NFC_PAYMENT_SETTINGS = "android.settings.NFC_PAYMENT_SETTINGS";
field public static final java.lang.String ACTION_NFC_SETTINGS = "android.settings.NFC_SETTINGS";
+ field public static final java.lang.String ACTION_NIGHT_DISPLAY_SETTINGS = "android.settings.NIGHT_DISPLAY_SETTINGS";
field public static final java.lang.String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS";
field public static final java.lang.String ACTION_PRINT_SETTINGS = "android.settings.ACTION_PRINT_SETTINGS";
@@ -39362,6 +39367,7 @@
method public android.os.PersistableBundle getConfigForSubId(int);
method public void notifyConfigChangedForSubId(int);
field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
+ field public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe
field public static final java.lang.String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
field public static final java.lang.String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
field public static final java.lang.String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
@@ -39404,6 +39410,8 @@
field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string";
field public static final java.lang.String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING = "config_ims_package_override_string";
field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
+ field public static final java.lang.String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
+ field public static final java.lang.String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG = "data_warning_threshold_bytes_long";
field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
field public static final java.lang.String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
field public static final java.lang.String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array";
@@ -39459,6 +39467,7 @@
field public static final java.lang.String KEY_MMS_UA_PROF_TAG_NAME_STRING = "uaProfTagName";
field public static final java.lang.String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl";
field public static final java.lang.String KEY_MMS_USER_AGENT_STRING = "userAgent";
+ field public static final java.lang.String KEY_MONTHLY_DATA_CYCLE_DAY_INT = "monthly_data_cycle_day_int";
field public static final java.lang.String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
field public static final java.lang.String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
field public static final java.lang.String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
@@ -40688,7 +40697,9 @@
method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory, android.database.DatabaseErrorHandler);
method public android.graphics.drawable.Drawable peekWallpaper();
method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
+ method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, boolean);
method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler);
+ method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler, boolean);
method public void removeStickyBroadcast(android.content.Intent);
method public void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public void revokeUriPermission(android.net.Uri, int);
@@ -45451,7 +45462,7 @@
method public float getAlpha();
method public android.view.animation.Animation getAnimation();
method public android.os.IBinder getApplicationWindowToken();
- method public int getAutofillHint();
+ method public java.lang.String[] getAutofillHint();
method public int getAutofillMode();
method public int getAutofillType();
method public android.view.autofill.AutofillValue getAutofillValue();
@@ -45775,7 +45786,7 @@
method public void setActivated(boolean);
method public void setAlpha(float);
method public void setAnimation(android.view.animation.Animation);
- method public void setAutofillHint(int);
+ method public void setAutofillHint(java.lang.String[]);
method public void setAutofillMode(int);
method public void setBackground(android.graphics.drawable.Drawable);
method public void setBackgroundColor(int);
@@ -45918,20 +45929,19 @@
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 AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE = 512; // 0x200
- field public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY = 4096; // 0x1000
- field public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH = 1024; // 0x400
- field public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR = 2048; // 0x800
- field public static final int AUTOFILL_HINT_CREDIT_CARD_NUMBER = 128; // 0x80
- field public static final int AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE = 256; // 0x100
- field public static final int AUTOFILL_HINT_EMAIL_ADDRESS = 1; // 0x1
- field public static final int AUTOFILL_HINT_NAME = 2; // 0x2
- field public static final int AUTOFILL_HINT_NONE = 0; // 0x0
- field public static final int AUTOFILL_HINT_PASSWORD = 8; // 0x8
- field public static final int AUTOFILL_HINT_PHONE = 16; // 0x10
- field public static final int AUTOFILL_HINT_POSTAL_ADDRESS = 32; // 0x20
- field public static final int AUTOFILL_HINT_POSTAL_CODE = 64; // 0x40
- field public static final int AUTOFILL_HINT_USERNAME = 4; // 0x4
+ field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE = "creditCardExpirationDate";
+ field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY = "creditCardExpirationDay";
+ field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH = "creditCardExpirationMonth";
+ field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR = "creditCardExpirationYear";
+ field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_NUMBER = "creditCardNumber";
+ field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE = "creditCardSecurityCode";
+ field public static final java.lang.String AUTOFILL_HINT_EMAIL_ADDRESS = "emailAddress";
+ field public static final java.lang.String AUTOFILL_HINT_NAME = "name";
+ field public static final java.lang.String AUTOFILL_HINT_PASSWORD = "password";
+ field public static final java.lang.String AUTOFILL_HINT_PHONE = "phone";
+ field public static final java.lang.String AUTOFILL_HINT_POSTAL_ADDRESS = "postalAddress";
+ field public static final java.lang.String AUTOFILL_HINT_POSTAL_CODE = "postalCode";
+ field public static final java.lang.String AUTOFILL_HINT_USERNAME = "username";
field public static final int AUTOFILL_MODE_AUTO = 1; // 0x1
field public static final int AUTOFILL_MODE_INHERIT = 0; // 0x0
field public static final int AUTOFILL_MODE_MANUAL = 2; // 0x2
@@ -46597,7 +46607,7 @@
method public abstract void setAccessibilityFocused(boolean);
method public abstract void setActivated(boolean);
method public abstract void setAlpha(float);
- method public abstract void setAutofillHint(int);
+ method public abstract void setAutofillHint(java.lang.String[]);
method public abstract void setAutofillOptions(java.lang.String[]);
method public abstract void setAutofillType(int);
method public abstract void setAutofillValue(android.view.autofill.AutofillValue);
@@ -46608,6 +46618,7 @@
method public abstract void setClickable(boolean);
method public abstract void setContentDescription(java.lang.CharSequence);
method public abstract void setContextClickable(boolean);
+ method public abstract void setDataIsSensitive(boolean);
method public abstract void setDimens(int, int, int, int, int, int);
method public abstract void setElevation(float);
method public abstract void setEnabled(boolean);
@@ -46618,7 +46629,6 @@
method public abstract void setInputType(int);
method public abstract void setLongClickable(boolean);
method public abstract void setOpaque(boolean);
- method public abstract void setSanitized(boolean);
method public abstract void setSelected(boolean);
method public abstract void setText(java.lang.CharSequence);
method public abstract void setText(java.lang.CharSequence, int, int);
@@ -47863,15 +47873,16 @@
}
public final class AutofillManager {
+ method public void cancel();
+ method public void commit();
+ method public void notifyValueChanged(android.view.View);
+ method public void notifyViewEntered(android.view.View);
+ method public void notifyViewExited(android.view.View);
+ method public void notifyVirtualValueChanged(android.view.View, int, android.view.autofill.AutofillValue);
+ method public void notifyVirtualViewEntered(android.view.View, int, android.graphics.Rect);
+ method public void notifyVirtualViewExited(android.view.View, int);
method public void registerCallback(android.view.autofill.AutofillManager.AutofillCallback);
- method public void reset();
- method public void startAutofillRequest(android.view.View);
- method public void startAutofillRequestOnVirtualView(android.view.View, int, android.graphics.Rect);
- method public void stopAutofillRequest(android.view.View);
- method public void stopAutofillRequestOnVirtualView(android.view.View, int);
method public void unregisterCallback(android.view.autofill.AutofillManager.AutofillCallback);
- method public void valueChanged(android.view.View);
- method public void virtualValueChanged(android.view.View, int, android.view.autofill.AutofillValue);
field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
}
@@ -47894,6 +47905,10 @@
method public int getListValue();
method public java.lang.CharSequence getTextValue();
method public boolean getToggleValue();
+ method public boolean isDate();
+ method public boolean isList();
+ method public boolean isText();
+ method public boolean isToggle();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.autofill.AutofillValue> CREATOR;
}
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
index b0ab235..6538515 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
@@ -101,7 +101,7 @@
IWindowManager.Stub.asInterface(ServiceManager.getService(Context.WINDOW_SERVICE));
int ret = -1;
try {
- ret = wm.getRotation();
+ ret = wm.getDefaultDisplayRotation();
} catch (RemoteException e) {
Log.e(LOG_TAG, "Error getting screen rotation", e);
throw new RuntimeException(e);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 1969f8b..78c29e8 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1783,7 +1783,7 @@
mTranslucentCallback = null;
mCalled = true;
if (isFinishing() && mAutoFillResetNeeded) {
- getSystemService(AutofillManager.class).reset();
+ getSystemService(AutofillManager.class).commit();
}
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index fa64a0f..b36b664 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -25,6 +25,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.service.voice.IVoiceInteractionSession;
+import android.util.SparseIntArray;
import com.android.internal.app.IVoiceInteractor;
@@ -47,9 +48,9 @@
/**
* Type for {@link #notifyAppTransitionStarting}: The transition was started because we drew
- * the starting window.
+ * the splash screen.
*/
- public static final int APP_TRANSITION_STARTING_WINDOW = 1;
+ public static final int APP_TRANSITION_SPLASH_SCREEN = 1;
/**
* Type for {@link #notifyAppTransitionStarting}: The transition was started because we all
@@ -64,6 +65,12 @@
public static final int APP_TRANSITION_TIMEOUT = 3;
/**
+ * Type for {@link #notifyAppTransitionStarting}: The transition was started because of a
+ * we drew a task snapshot.
+ */
+ public static final int APP_TRANSITION_SNAPSHOT = 4;
+
+ /**
* Grant Uri permissions from one app to another. This method only extends
* permission grants if {@code callingUid} has permission to them.
*/
@@ -122,19 +129,13 @@
IVoiceInteractor mInteractor);
/**
- * Callback for window manager to let activity manager know that the starting window has been
- * drawn
- */
- public abstract void notifyStartingWindowDrawn();
-
- /**
* Callback for window manager to let activity manager know that we are finally starting the
* app transition;
*
- * @param reason The reason why the app transition started. Must be one of the APP_TRANSITION_*
- * values.
+ * @param reasons A map from stack id to a reason integer why the transition was started,, which
+ * must be one of the APP_TRANSITION_* values.
*/
- public abstract void notifyAppTransitionStarting(int reason);
+ public abstract void notifyAppTransitionStarting(SparseIntArray reasons);
/**
* Callback for window manager to let activity manager know that the app transition was
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index a512350..21a7ca7 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -28,6 +28,7 @@
import android.transition.TransitionSet;
import android.transition.Visibility;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.view.GhostView;
import android.view.View;
import android.view.ViewGroup;
@@ -394,6 +395,60 @@
return transition;
}
+ /**
+ * Looks through the transition to see which Views have been included and which have been
+ * excluded. {@code views} will be modified to contain only those Views that are included
+ * in the transition. If {@code transition} is a TransitionSet, it will search through all
+ * contained Transitions to find targeted Views.
+ *
+ * @param transition The transition to look through for inclusion of Views
+ * @param views The list of Views that are to be checked for inclusion. Will be modified
+ * to remove all excluded Views, possibly leaving an empty list.
+ */
+ protected static void removeExcludedViews(Transition transition, ArrayList<View> views) {
+ ArraySet<View> included = new ArraySet<>();
+ findIncludedViews(transition, views, included);
+ views.clear();
+ views.addAll(included);
+ }
+
+ /**
+ * Looks through the transition to see which Views have been included. Only {@code views}
+ * will be examined for inclusion. If {@code transition} is a TransitionSet, it will search
+ * through all contained Transitions to find targeted Views.
+ *
+ * @param transition The transition to look through for inclusion of Views
+ * @param views The list of Views that are to be checked for inclusion.
+ * @param included Modified to contain all Views in views that have at least one Transition
+ * that affects it.
+ */
+ private static void findIncludedViews(Transition transition, ArrayList<View> views,
+ ArraySet<View> included) {
+ if (transition instanceof TransitionSet) {
+ TransitionSet set = (TransitionSet) transition;
+ ArrayList<View> includedViews = new ArrayList<>();
+ final int numViews = views.size();
+ for (int i = 0; i < numViews; i++) {
+ final View view = views.get(i);
+ if (transition.isValidTarget(view)) {
+ includedViews.add(view);
+ }
+ }
+ final int count = set.getTransitionCount();
+ for (int i = 0; i < count; i++) {
+ findIncludedViews(set.getTransitionAt(i), includedViews, included);
+ }
+ } else {
+ final int numViews = views.size();
+ for (int i = 0; i < numViews; i++) {
+ final View view = views.get(i);
+ if (transition.isValidTarget(view)) {
+ included.add(view);
+ }
+ }
+ }
+ }
+
protected static Transition mergeTransitions(Transition transition1, Transition transition2) {
if (transition1 == null) {
return transition2;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 0ab4b80..ede9281 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1327,21 +1327,34 @@
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+ boolean visibleToInstantApps) {
+ return registerReceiver(receiver, filter, null, null, visibleToInstantApps);
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler) {
return registerReceiverInternal(receiver, getUserId(),
- filter, broadcastPermission, scheduler, getOuterContext());
+ filter, broadcastPermission, scheduler, getOuterContext(), false);
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+ String broadcastPermission, Handler scheduler, boolean visibleToInstantApps) {
+ return registerReceiverInternal(receiver, getUserId(),
+ filter, broadcastPermission, scheduler, getOuterContext(), visibleToInstantApps);
}
@Override
public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
IntentFilter filter, String broadcastPermission, Handler scheduler) {
return registerReceiverInternal(receiver, user.getIdentifier(),
- filter, broadcastPermission, scheduler, getOuterContext());
+ filter, broadcastPermission, scheduler, getOuterContext(), false);
}
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
- Handler scheduler, Context context) {
+ Handler scheduler, Context context, boolean visibleToInstantApps) {
IIntentReceiver rd = null;
if (receiver != null) {
if (mPackageInfo != null && context != null) {
@@ -1362,7 +1375,7 @@
try {
final Intent intent = ActivityManager.getService().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
- broadcastPermission, userId);
+ broadcastPermission, userId, visibleToInstantApps);
if (intent != null) {
intent.setExtrasClassLoader(getClassLoader());
intent.prepareToEnterProcess();
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 445b687..ab847fd 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -132,7 +132,9 @@
super.viewsReady(sharedElements);
mIsReadyForTransition = true;
hideViews(mSharedElements);
- if (getViewsTransition() != null && mTransitioningViews != null) {
+ Transition viewsTransition = getViewsTransition();
+ if (viewsTransition != null && mTransitioningViews != null) {
+ removeExcludedViews(viewsTransition, mTransitioningViews);
stripOffscreenViews();
hideViews(mTransitioningViews);
}
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index 29e10d8..df31da9 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -321,6 +321,10 @@
Transition viewsTransition = null;
if (mTransitioningViews != null && !mTransitioningViews.isEmpty()) {
viewsTransition = configureTransition(getViewsTransition(), true);
+ removeExcludedViews(viewsTransition, mTransitioningViews);
+ if (mTransitioningViews.isEmpty()) {
+ viewsTransition = null;
+ }
}
if (viewsTransition == null) {
viewsTransitionComplete();
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 081dae2..77edaea 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -100,7 +100,7 @@
boolean finishActivity(in IBinder token, int code, in Intent data, int finishTask);
Intent registerReceiver(in IApplicationThread caller, in String callerPackage,
in IIntentReceiver receiver, in IntentFilter filter,
- in String requiredPermission, int userId);
+ in String requiredPermission, int userId, boolean visibleToInstantApps);
void unregisterReceiver(in IIntentReceiver receiver);
int broadcastIntent(in IApplicationThread caller, in Intent intent,
in String resolvedType, in IIntentReceiver resultTo, int resultCode,
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index d37e209..2296838 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -424,7 +424,8 @@
* This is a no-op for channels that already exist.
*
* @param channel the channel to create. Note that the created channel may differ from this
- * value. If the channel already exists, it will not be modified.
+ * value. If the provided channel is malformed, a RemoteException will be
+ * thrown. If the channel already exists, it will not be modified.
*/
public void createNotificationChannel(@NonNull NotificationChannel channel) {
createNotificationChannels(Arrays.asList(channel));
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 2c1ee8e..9960df6 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -367,7 +367,7 @@
if (mWindowManager.isRotationFrozen()) {
// Calling out with a lock held is fine since if the system
// process is gone the client calling in will be killed.
- mInitialFrozenRotation = mWindowManager.getRotation();
+ mInitialFrozenRotation = mWindowManager.getDefaultDisplayRotation();
}
} catch (RemoteException re) {
/* ignore */
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 1f2ed00..b1fbc8f 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -1,5 +1,6 @@
package android.app.assist;
+import android.annotation.Nullable;
import android.app.Activity;
import android.content.ComponentName;
import android.graphics.Matrix;
@@ -590,7 +591,7 @@
// fields (viewId and childId) of the field.
AutofillId mAutofillId;
@View.AutofillType int mAutofillType;
- @View.AutofillHint int mAutofillHint;
+ @Nullable String[] mAutofillHint;
AutofillValue mAutofillValue;
String[] mAutofillOptions;
boolean mSanitized;
@@ -676,7 +677,7 @@
mSanitized = in.readInt() == 1;
mAutofillId = in.readParcelable(null);
mAutofillType = in.readInt();
- mAutofillHint = in.readInt();
+ mAutofillHint = in.readStringArray();
mAutofillValue = in.readParcelable(null);
mAutofillOptions = in.readStringArray();
}
@@ -810,7 +811,7 @@
out.writeInt(mSanitized ? 1 : 0);
out.writeParcelable(mAutofillId, 0);
out.writeInt(mAutofillType);
- out.writeInt(mAutofillHint);
+ out.writeStringArray(mAutofillHint);
final AutofillValue sanitizedValue = writeSensitive ? mAutofillValue : null;
out.writeParcelable(sanitizedValue, 0);
out.writeStringArray(mAutofillOptions);
@@ -949,7 +950,7 @@
*
* @return The hint for this view
*/
- @View.AutofillHint public int getAutoFillHint() {
+ @Nullable public String[] getAutoFillHint() {
return mAutofillHint;
}
@@ -1012,9 +1013,8 @@
mAutofillValue = value;
// TODO(b/33197203, b/33802548): decide whether to set text as well (so it would work
// with "legacy" views) or just the autofill value
- final CharSequence text = value.getTextValue();
- if (text != null) {
- mText.mText = text;
+ if (value.isText()) {
+ mText.mText = value.getTextValue();
}
}
@@ -1663,7 +1663,7 @@
}
@Override
- public void setAutofillHint(@View.AutofillHint int hint) {
+ public void setAutofillHint(@Nullable String[] hint) {
mNode.mAutofillHint = hint;
}
@@ -1683,8 +1683,8 @@
}
@Override
- public void setSanitized(boolean sanitized) {
- mNode.mSanitized = sanitized;
+ public void setDataIsSensitive(boolean sensitive) {
+ mNode.mSanitized = !sensitive;
}
@Override
@@ -1812,7 +1812,7 @@
+ ", type=" + node.getAutofillType()
+ ", options=" + Arrays.toString(node.getAutofillOptions())
+ ", inputType=" + node.getInputType()
- + ", hint=" + Integer.toHexString(node.getAutoFillHint())
+ + ", hint=" + Arrays.toString(node.getAutoFillHint())
+ ", value=" + node.getAutofillValue()
+ ", sanitized=" + node.isSanitized());
}
diff --git a/core/java/android/app/usage/IStorageStatsManager.aidl b/core/java/android/app/usage/IStorageStatsManager.aidl
index 76c0293..5d1550f 100644
--- a/core/java/android/app/usage/IStorageStatsManager.aidl
+++ b/core/java/android/app/usage/IStorageStatsManager.aidl
@@ -24,6 +24,7 @@
boolean isQuotaSupported(String volumeUuid, String callingPackage);
long getTotalBytes(String volumeUuid, String callingPackage);
long getFreeBytes(String volumeUuid, String callingPackage);
+ long getCacheQuotaBytes(String volumeUuid, int uid, String callingPackage);
StorageStats queryStatsForPackage(String volumeUuid, String packageName, int userId, String callingPackage);
StorageStats queryStatsForUid(String volumeUuid, int uid, String callingPackage);
StorageStats queryStatsForUser(String volumeUuid, int userId, String callingPackage);
diff --git a/core/java/android/app/usage/StorageStatsManager.java b/core/java/android/app/usage/StorageStatsManager.java
index 081ccd9..8276229 100644
--- a/core/java/android/app/usage/StorageStatsManager.java
+++ b/core/java/android/app/usage/StorageStatsManager.java
@@ -195,4 +195,13 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /** {@hide} */
+ public long getCacheQuotaBytes(String volumeUuid, int uid) {
+ try {
+ return mService.getCacheQuotaBytes(volumeUuid, uid, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 5579c9a..98ae132 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -2835,6 +2835,11 @@
}
}
+ /** {@hide} */
+ public int getTargetSdkVersion() {
+ return mTargetSdkVersion;
+ }
+
/**
* Returns sampling percentage for a given duration.
*
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 3cf96ed..3a8a420 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2383,6 +2383,38 @@
IntentFilter filter);
/**
+ * Register to receive intent broadcasts, with the receiver optionally being
+ * exposed to Instant Apps. See
+ * {@link #registerReceiver(BroadcastReceiver, IntentFilter)} for more
+ * information. By default Instant Apps cannot interact with receivers in other
+ * applications, this allows you to expose a receiver that Instant Apps can
+ * interact with.
+ *
+ * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+ *
+ * <p>As of {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, receivers
+ * registered with this method will correctly respect the
+ * {@link Intent#setPackage(String)} specified for an Intent being broadcast.
+ * Prior to that, it would be ignored and delivered to all matching registered
+ * receivers. Be careful if using this for security.</p>
+ *
+ * @param receiver The BroadcastReceiver to handle the broadcast.
+ * @param filter Selects the Intent broadcasts to be received.
+ * @param visibleToInstantApps If the receiver accepts broadcasts from Instant Apps.
+ *
+ * @return The first sticky intent found that matches <var>filter</var>,
+ * or null if there are none.
+ *
+ * @see #registerReceiver(BroadcastReceiver, IntentFilter)
+ * @see #sendBroadcast
+ * @see #unregisterReceiver
+ */
+ @Nullable
+ public abstract Intent registerReceiver(@Nullable BroadcastReceiver receiver,
+ IntentFilter filter,
+ boolean visibleToInstantApps);
+
+ /**
* Register to receive intent broadcasts, to run in the context of
* <var>scheduler</var>. See
* {@link #registerReceiver(BroadcastReceiver, IntentFilter)} for more
@@ -2419,6 +2451,43 @@
@Nullable Handler scheduler);
/**
+ * Register to receive intent broadcasts, with the receiver optionally being
+ * exposed to Instant Apps. See
+ * {@link #registerReceiver(BroadcastReceiver, IntentFilter, boolean)} and
+ * {@link #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)}
+ * for more information.
+ *
+ * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+ *
+ * <p>As of {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, receivers
+ * registered with this method will correctly respect the
+ * {@link Intent#setPackage(String)} specified for an Intent being broadcast.
+ * Prior to that, it would be ignored and delivered to all matching registered
+ * receivers. Be careful if using this for security.</p>
+ *
+ * @param receiver The BroadcastReceiver to handle the broadcast.
+ * @param filter Selects the Intent broadcasts to be received.
+ * @param broadcastPermission String naming a permissions that a
+ * broadcaster must hold in order to send an Intent to you. If null,
+ * no permission is required.
+ * @param scheduler Handler identifying the thread that will receive
+ * the Intent. If null, the main thread of the process will be used.
+ * @param visibleToInstantApps If the receiver accepts broadcasts from Instant Apps.
+ *
+ * @return The first sticky intent found that matches <var>filter</var>,
+ * or null if there are none.
+ *
+ * @see #registerReceiver(BroadcastReceiver, IntentFilter, boolean)
+ * @see #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)
+ * @see #sendBroadcast
+ * @see #unregisterReceiver
+ */
+ @Nullable
+ public abstract Intent registerReceiver(BroadcastReceiver receiver,
+ IntentFilter filter, @Nullable String broadcastPermission,
+ @Nullable Handler scheduler, boolean visibleToInstantApps);
+
+ /**
* @hide
* Same as {@link #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)
* but for a specific user. This receiver will receiver broadcasts that
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index c932b23..6b0bbfa 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -605,12 +605,26 @@
@Override
public Intent registerReceiver(
+ BroadcastReceiver receiver, IntentFilter filter, boolean visibleToInstantApps) {
+ return mBase.registerReceiver(receiver, filter, visibleToInstantApps);
+ }
+
+ @Override
+ public Intent registerReceiver(
BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler) {
return mBase.registerReceiver(receiver, filter, broadcastPermission,
scheduler);
}
+ @Override
+ public Intent registerReceiver(
+ BroadcastReceiver receiver, IntentFilter filter,
+ String broadcastPermission, Handler scheduler, boolean visibleToInstantApps) {
+ return mBase.registerReceiver(receiver, filter, broadcastPermission,
+ scheduler, visibleToInstantApps);
+ }
+
/** @hide */
@Override
public Intent registerReceiverAsUser(
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index f62621b..1f01e28 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -5005,6 +5005,14 @@
public static final int FLAG_RECEIVER_FROM_SHELL = 0x00400000;
/**
+ * If set, the broadcast will be visible to receivers in Instant Apps. By default Instant Apps
+ * will not receive broadcasts.
+ *
+ * <em>This flag has no effect when used by an Instant App.</em>
+ */
+ public static final int FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS = 0x00200000;
+
+ /**
* @hide Flags that can't be changed with PendingIntent.
*/
public static final int IMMUTABLE_FLAGS = FLAG_GRANT_READ_URI_PERMISSION
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 76c29bc..320c733 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -36,7 +36,9 @@
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.RemoteException;
-import android.annotation.IntRange;
+import android.os.SystemProperties;
+import android.system.ErrnoException;
+import android.system.Os;
import android.util.ExceptionUtils;
import com.android.internal.util.IndentingPrintWriter;
@@ -79,6 +81,10 @@
public class PackageInstaller {
private static final String TAG = "PackageInstaller";
+ /** {@hide} */
+ public static final boolean ENABLE_REVOCABLE_FD =
+ SystemProperties.getBoolean("fw.revocable_fd", false);
+
/**
* Activity Action: Show details about a particular install session. This
* may surface actions such as pause, resume, or cancel.
@@ -753,15 +759,21 @@
public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes,
long lengthBytes) throws IOException {
try {
- final ParcelFileDescriptor clientSocket = mSession.openWrite(name,
- offsetBytes, lengthBytes);
- return new FileBridge.FileBridgeOutputStream(clientSocket);
+ if (ENABLE_REVOCABLE_FD) {
+ return new ParcelFileDescriptor.AutoCloseOutputStream(
+ mSession.openWrite(name, offsetBytes, lengthBytes));
+ } else {
+ final ParcelFileDescriptor clientSocket = mSession.openWrite(name,
+ offsetBytes, lengthBytes);
+ return new FileBridge.FileBridgeOutputStream(clientSocket);
+ }
} catch (RuntimeException e) {
ExceptionUtils.maybeUnwrapIOException(e);
throw e;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+
}
/**
@@ -770,10 +782,22 @@
* {@link #openWrite(String, long, long)}.
*/
public void fsync(@NonNull OutputStream out) throws IOException {
- if (out instanceof FileBridge.FileBridgeOutputStream) {
- ((FileBridge.FileBridgeOutputStream) out).fsync();
+ if (ENABLE_REVOCABLE_FD) {
+ if (out instanceof ParcelFileDescriptor.AutoCloseOutputStream) {
+ try {
+ Os.fsync(((ParcelFileDescriptor.AutoCloseOutputStream) out).getFD());
+ } catch (ErrnoException e) {
+ throw e.rethrowAsIOException();
+ }
+ } else {
+ throw new IllegalArgumentException("Unrecognized stream");
+ }
} else {
- throw new IllegalArgumentException("Unrecognized stream");
+ if (out instanceof FileBridge.FileBridgeOutputStream) {
+ ((FileBridge.FileBridgeOutputStream) out).fsync();
+ } else {
+ throw new IllegalArgumentException("Unrecognized stream");
+ }
}
}
diff --git a/core/java/android/hardware/LegacySensorManager.java b/core/java/android/hardware/LegacySensorManager.java
index f959093..f5cf3f7 100644
--- a/core/java/android/hardware/LegacySensorManager.java
+++ b/core/java/android/hardware/LegacySensorManager.java
@@ -16,6 +16,8 @@
package android.hardware;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import android.os.RemoteException;
import android.os.ServiceManager;
import android.view.IRotationWatcher;
@@ -57,8 +59,7 @@
public void onRotationChanged(int rotation) {
LegacySensorManager.onRotationChanged(rotation);
}
- }
- );
+ }, DEFAULT_DISPLAY);
} catch (RemoteException e) {
}
}
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index e6fe0d0..70ecf89 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -419,6 +419,7 @@
* to connect to
* @throws SecurityException if the caller does not hold the
* {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} permission.
+ * @hide
*/
public RecommendationResult requestRecommendation(RecommendationRequest request)
throws SecurityException {
diff --git a/core/java/android/os/FileBridge.java b/core/java/android/os/FileBridge.java
index 0acf24b..3ac88c5 100644
--- a/core/java/android/os/FileBridge.java
+++ b/core/java/android/os/FileBridge.java
@@ -41,7 +41,9 @@
* hands-off.
*
* @hide
+ * @deprecated replaced by {@link RevocableFileDescriptor}
*/
+@Deprecated
public class FileBridge extends Thread {
private static final String TAG = "FileBridge";
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index 9db58ee..6656b00 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -18,6 +18,8 @@
import android.util.ArrayMap;
+import java.util.function.Consumer;
+
/**
* Takes care of the grunt work of maintaining a list of remote interfaces,
* typically for the use of performing callbacks from a
@@ -308,6 +310,23 @@
}
/**
+ * Performs {@code action} on each callback, calling
+ * {@link #beginBroadcast()}/{@link #finishBroadcast()} before/after looping
+ *
+ * @hide
+ */
+ public void broadcast(Consumer<E> action) {
+ int itemCount = beginBroadcast();
+ try {
+ for (int i = 0; i < itemCount; i++) {
+ action.accept(getBroadcastItem(i));
+ }
+ } finally {
+ finishBroadcast();
+ }
+ }
+
+ /**
* Returns the number of registered callbacks. Note that the number of registered
* callbacks may differ from the value returned by {@link #beginBroadcast()} since
* the former returns the number of callbacks registered at the time of the call
diff --git a/core/java/android/os/RevocableFileDescriptor.java b/core/java/android/os/RevocableFileDescriptor.java
new file mode 100644
index 0000000..a750ce6
--- /dev/null
+++ b/core/java/android/os/RevocableFileDescriptor.java
@@ -0,0 +1,161 @@
+/*
+ * 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.os;
+
+import android.content.Context;
+import android.os.storage.StorageManager;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.util.Slog;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+
+/**
+ * Variant of {@link FileDescriptor} that allows its creator to revoke all
+ * access to the underlying resource.
+ * <p>
+ * This is useful when the code that originally opened a file needs to strongly
+ * assert that any clients are completely hands-off for security purposes.
+ *
+ * @hide
+ */
+public class RevocableFileDescriptor {
+ private static final String TAG = "RevocableFileDescriptor";
+ private static final boolean DEBUG = true;
+
+ private FileDescriptor mInner;
+ private ParcelFileDescriptor mOuter;
+
+ private volatile boolean mRevoked;
+
+ /** {@hide} */
+ public RevocableFileDescriptor() {
+ }
+
+ /**
+ * Create an instance that references the given {@link File}.
+ */
+ public RevocableFileDescriptor(Context context, File file) throws IOException {
+ try {
+ init(context, Os.open(file.getAbsolutePath(),
+ OsConstants.O_CREAT | OsConstants.O_RDWR, 0700));
+ } catch (ErrnoException e) {
+ throw e.rethrowAsIOException();
+ }
+ }
+
+ /**
+ * Create an instance that references the given {@link FileDescriptor}.
+ */
+ public RevocableFileDescriptor(Context context, FileDescriptor fd) throws IOException {
+ init(context, fd);
+ }
+
+ /** {@hide} */
+ public void init(Context context, FileDescriptor fd) throws IOException {
+ mInner = fd;
+ mOuter = context.getSystemService(StorageManager.class)
+ .openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_WRITE, mCallback);
+ }
+
+ /**
+ * Return a {@link ParcelFileDescriptor} which can safely be passed to an
+ * untrusted process. After {@link #revoke()} is called, all operations will
+ * fail with {@link OsConstants#EPERM}.
+ */
+ public ParcelFileDescriptor getRevocableFileDescriptor() {
+ return mOuter;
+ }
+
+ /**
+ * Revoke all future access to the {@link ParcelFileDescriptor} returned by
+ * {@link #getRevocableFileDescriptor()}. From this point forward, all
+ * operations will fail with {@link OsConstants#EPERM}.
+ */
+ public void revoke() {
+ mRevoked = true;
+ IoUtils.closeQuietly(mInner);
+ }
+
+ public boolean isRevoked() {
+ return mRevoked;
+ }
+
+ private final ProxyFileDescriptorCallback mCallback = new ProxyFileDescriptorCallback() {
+ private void checkRevoked() throws ErrnoException {
+ if (mRevoked) {
+ throw new ErrnoException(TAG, OsConstants.EPERM);
+ }
+ }
+
+ @Override
+ public long onGetSize() throws ErrnoException {
+ checkRevoked();
+ return Os.fstat(mInner).st_size;
+ }
+
+ @Override
+ public int onRead(long offset, int size, byte[] data) throws ErrnoException {
+ checkRevoked();
+ int n = 0;
+ while (n < size) {
+ try {
+ n += Os.pread(mInner, data, n, size - n, offset + n);
+ break;
+ } catch (InterruptedIOException e) {
+ n += e.bytesTransferred;
+ }
+ }
+ return n;
+ }
+
+ @Override
+ public int onWrite(long offset, int size, byte[] data) throws ErrnoException {
+ checkRevoked();
+ int n = 0;
+ while (n < size) {
+ try {
+ n += Os.pwrite(mInner, data, n, size - n, offset + n);
+ break;
+ } catch (InterruptedIOException e) {
+ n += e.bytesTransferred;
+ }
+ }
+ return n;
+ }
+
+ @Override
+ public void onFsync() throws ErrnoException {
+ if (DEBUG) Slog.v(TAG, "onFsync()");
+ checkRevoked();
+ Os.fsync(mInner);
+ }
+
+ @Override
+ public void onRelease() {
+ if (DEBUG) Slog.v(TAG, "onRelease()");
+ mRevoked = true;
+ IoUtils.closeQuietly(mInner);
+ }
+ };
+}
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 46f2d38..1fc0b82 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -373,8 +373,7 @@
}
/** {@hide} */
- // TODO(b/26742218): find out where toString() is called internally and replace these calls by
- // dump().
+ // TODO: find out where toString() is called internally and replace these calls by dump().
public String dump() {
final CharArrayWriter writer = new CharArrayWriter();
dump(new IndentingPrintWriter(writer, " ", 80));
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index 443a3e9..d9c749a 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -81,6 +81,7 @@
* @attr ref android.R.styleable#Preference_persistent
* @attr ref android.R.styleable#Preference_defaultValue
* @attr ref android.R.styleable#Preference_shouldDisableView
+ * @attr ref android.R.styleable#Preference_recycleEnabled
*/
public class Preference implements Comparable<Preference> {
/**
@@ -131,6 +132,7 @@
private Object mDefaultValue;
private boolean mDependencyMet = true;
private boolean mParentDependencyMet = true;
+ private boolean mRecycleEnabled = true;
/**
* @see #setShouldDisableView(boolean)
@@ -139,7 +141,6 @@
private int mLayoutResId = com.android.internal.R.layout.preference;
private int mWidgetLayoutResId;
- private boolean mCanRecycleLayout = true;
private OnPreferenceChangeInternalListener mListener;
@@ -291,15 +292,13 @@
case com.android.internal.R.styleable.Preference_shouldDisableView:
mShouldDisableView = a.getBoolean(attr, mShouldDisableView);
break;
+
+ case com.android.internal.R.styleable.Preference_recycleEnabled:
+ mRecycleEnabled = a.getBoolean(attr, mRecycleEnabled);
+ break;
}
}
a.recycle();
-
- if (!getClass().getName().startsWith("android.preference")
- && !getClass().getName().startsWith("com.android")) {
- // For non-framework subclasses, assume the worst and don't cache views.
- mCanRecycleLayout = false;
- }
}
/**
@@ -458,8 +457,8 @@
}
/**
- * Return the extras Bundle object associated with this preference,
- * returning null if there is not currently one.
+ * Return the extras Bundle object associated with this preference, returning {@code null} if
+ * there is not currently one.
*/
public Bundle peekExtras() {
return mExtras;
@@ -482,7 +481,7 @@
public void setLayoutResource(@LayoutRes int layoutResId) {
if (layoutResId != mLayoutResId) {
// Layout changed
- mCanRecycleLayout = false;
+ mRecycleEnabled = false;
}
mLayoutResId = layoutResId;
@@ -511,7 +510,7 @@
public void setWidgetLayoutResource(@LayoutRes int widgetLayoutResId) {
if (widgetLayoutResId != mWidgetLayoutResId) {
// Layout changed
- mCanRecycleLayout = false;
+ mRecycleEnabled = false;
}
mWidgetLayoutResId = widgetLayoutResId;
}
@@ -653,15 +652,13 @@
}
/**
- * Sets the order of this Preference with respect to other
- * Preference objects on the same level. If this is not specified, the
- * default behavior is to sort alphabetically. The
- * {@link PreferenceGroup#setOrderingAsAdded(boolean)} can be used to order
- * Preference objects based on the order they appear in the XML.
+ * Sets the order of this Preference with respect to other Preference objects on the same level.
+ * If this is not specified, the default behavior is to sort alphabetically. The
+ * {@link PreferenceGroup#setOrderingAsAdded(boolean)} can be used to order Preference objects
+ * based on the order they appear in the XML.
*
- * @param order The order for this Preference. A lower value will be shown
- * first. Use {@link #DEFAULT_ORDER} to sort alphabetically or
- * allow ordering from XML.
+ * @param order the order for this Preference. A lower value will be shown first. Use
+ * {@link #DEFAULT_ORDER} to sort alphabetically or allow ordering from XML
* @see PreferenceGroup#setOrderingAsAdded(boolean)
* @see #DEFAULT_ORDER
*/
@@ -669,16 +666,15 @@
if (order != mOrder) {
mOrder = order;
- // Reorder the list
+ // Reorder the list
notifyHierarchyChanged();
}
}
/**
- * Gets the order of this Preference with respect to other Preference objects
- * on the same level.
+ * Gets the order of this Preference with respect to other Preference objects on the same level.
*
- * @return The order of this Preference.
+ * @return the order of this Preference
* @see #setOrder(int)
*/
public int getOrder() {
@@ -686,12 +682,10 @@
}
/**
- * Sets the title for this Preference with a CharSequence.
- * This title will be placed into the ID
- * {@link android.R.id#title} within the View created by
- * {@link #onCreateView(ViewGroup)}.
+ * Sets the title for this Preference with a CharSequence. This title will be placed into the ID
+ * {@link android.R.id#title} within the View created by {@link #onCreateView(ViewGroup)}.
*
- * @param title The title for this Preference.
+ * @param title the title for this Preference
*/
public void setTitle(CharSequence title) {
if (title == null && mTitle != null || title != null && !title.equals(mTitle)) {
@@ -702,10 +696,10 @@
}
/**
- * Sets the title for this Preference with a resource ID.
+ * Sets the title for this Preference with a resource ID.
*
* @see #setTitle(CharSequence)
- * @param titleResId The title as a resource ID.
+ * @param titleResId the title as a resource ID
*/
public void setTitle(@StringRes int titleResId) {
setTitle(mContext.getString(titleResId));
@@ -713,10 +707,10 @@
}
/**
- * Returns the title resource ID of this Preference. If the title did
- * not come from a resource, 0 is returned.
+ * Returns the title resource ID of this Preference. If the title did not come from a resource,
+ * {@code 0} is returned.
*
- * @return The title resource.
+ * @return the title resource
* @see #setTitle(int)
*/
@StringRes
@@ -727,7 +721,7 @@
/**
* Returns the title of this Preference.
*
- * @return The title.
+ * @return the title
* @see #setTitle(CharSequence)
*/
public CharSequence getTitle() {
@@ -735,12 +729,10 @@
}
/**
- * Sets the icon for this Preference with a Drawable.
- * This icon will be placed into the ID
- * {@link android.R.id#icon} within the View created by
- * {@link #onCreateView(ViewGroup)}.
+ * Sets the icon for this Preference with a Drawable. This icon will be placed into the ID
+ * {@link android.R.id#icon} within the View created by {@link #onCreateView(ViewGroup)}.
*
- * @param icon The optional icon for this Preference.
+ * @param icon the optional icon for this Preference
*/
public void setIcon(Drawable icon) {
if ((icon == null && mIcon != null) || (icon != null && mIcon != icon)) {
@@ -751,10 +743,10 @@
}
/**
- * Sets the icon for this Preference with a resource ID.
+ * Sets the icon for this Preference with a resource ID.
*
* @see #setIcon(Drawable)
- * @param iconResId The icon as a resource ID.
+ * @param iconResId the icon as a resource ID
*/
public void setIcon(@DrawableRes int iconResId) {
if (mIconResId != iconResId) {
@@ -766,7 +758,7 @@
/**
* Returns the icon of this Preference.
*
- * @return The icon.
+ * @return the icon
* @see #setIcon(Drawable)
*/
public Drawable getIcon() {
@@ -779,7 +771,7 @@
/**
* Returns the summary of this Preference.
*
- * @return The summary.
+ * @return the summary
* @see #setSummary(CharSequence)
*/
public CharSequence getSummary() {
@@ -787,9 +779,9 @@
}
/**
- * Sets the summary for this Preference with a CharSequence.
+ * Sets the summary for this Preference with a CharSequence.
*
- * @param summary The summary for the preference.
+ * @param summary the summary for the preference
*/
public void setSummary(CharSequence summary) {
if (summary == null && mSummary != null || summary != null && !summary.equals(mSummary)) {
@@ -799,10 +791,10 @@
}
/**
- * Sets the summary for this Preference with a resource ID.
+ * Sets the summary for this Preference with a resource ID.
*
* @see #setSummary(CharSequence)
- * @param summaryResId The summary as a resource.
+ * @param summaryResId the summary as a resource
*/
public void setSummary(@StringRes int summaryResId) {
setSummary(mContext.getString(summaryResId));
@@ -812,7 +804,7 @@
* Sets whether this Preference is enabled. If disabled, it will
* not handle clicks.
*
- * @param enabled Set true to enable it.
+ * @param enabled set {@code true} to enable it
*/
public void setEnabled(boolean enabled) {
if (mEnabled != enabled) {
@@ -828,7 +820,7 @@
/**
* Checks whether this Preference should be enabled in the list.
*
- * @return True if this Preference is enabled, false otherwise.
+ * @return {@code true} if this Preference is enabled, false otherwise
*/
public boolean isEnabled() {
return mEnabled && mDependencyMet && mParentDependencyMet;
@@ -837,7 +829,7 @@
/**
* Sets whether this Preference is selectable.
*
- * @param selectable Set true to make it selectable.
+ * @param selectable set {@code true} to make it selectable
*/
public void setSelectable(boolean selectable) {
if (mSelectable != selectable) {
@@ -849,22 +841,21 @@
/**
* Checks whether this Preference should be selectable in the list.
*
- * @return True if it is selectable, false otherwise.
+ * @return {@code true} if it is selectable, {@code false} otherwise
*/
public boolean isSelectable() {
return mSelectable;
}
/**
- * Sets whether this Preference should disable its view when it gets
- * disabled.
- * <p>
- * For example, set this and {@link #setEnabled(boolean)} to false for
- * preferences that are only displaying information and 1) should not be
- * clickable 2) should not have the view set to the disabled state.
+ * Sets whether this Preference should disable its view when it gets disabled.
*
- * @param shouldDisableView Set true if this preference should disable its view
- * when the preference is disabled.
+ * <p>For example, set this and {@link #setEnabled(boolean)} to false for preferences that are
+ * only displaying information and 1) should not be clickable 2) should not have the view set to
+ * the disabled state.
+ *
+ * @param shouldDisableView set {@code true} if this preference should disable its view when
+ * the preference is disabled
*/
public void setShouldDisableView(boolean shouldDisableView) {
mShouldDisableView = shouldDisableView;
@@ -873,14 +864,45 @@
/**
* Checks whether this Preference should disable its view when it's action is disabled.
+ *
* @see #setShouldDisableView(boolean)
- * @return True if it should disable the view.
+ * @return {@code true} if it should disable the view
*/
public boolean getShouldDisableView() {
return mShouldDisableView;
}
/**
+ * Sets whether this Preference has enabled to have its view recycled when used in the list
+ * view. By default the recycling is enabled.
+ *
+ * <p>The value can be changed only before this preference is added to the preference hierarchy.
+ *
+ * <p>If view recycling is not allowed then each time the list view populates this preference
+ * the {@link #getView(View, ViewGroup)} method receives a {@code null} convert view and needs
+ * to recreate the view. Otherwise view gets recycled and only {@link #onBindView(View)} gets
+ * called.
+ *
+ * @param enabled set {@code true} if this preference view should be recycled
+ */
+ @CallSuper
+ public void setRecycleEnabled(boolean enabled) {
+ mRecycleEnabled = enabled;
+ notifyChanged();
+ }
+
+ /**
+ * Checks whether this Preference has enabled to have its view recycled when used in the list
+ * view.
+ *
+ * @see #setRecycleEnabled(boolean)
+ * @return {@code true} if this preference view should be recycled
+ */
+ public boolean isRecycleEnabled() {
+ return mRecycleEnabled;
+ }
+
+ /**
* Returns a unique ID for this Preference. This ID should be unique across all
* Preference objects in a hierarchy.
*
@@ -974,7 +996,7 @@
* the persistent {@link SharedPreferences} storage by default or into
* {@link PreferenceDataStore} if assigned.
*
- * @param persistent Set true if it should store its value(s) into the {@link SharedPreferences}.
+ * @param persistent set {@code true} if it should store its value(s) into the storage.
*/
public void setPersistent(boolean persistent) {
mPersistent = persistent;
@@ -1035,7 +1057,7 @@
*
* @param preferenceScreen A {@link PreferenceScreen} whose hierarchy click
* listener should be called in the proper order (between other
- * processing). May be null.
+ * processing). May be {@code null}.
* @hide
*/
public void performClick(PreferenceScreen preferenceScreen) {
@@ -1102,9 +1124,9 @@
* {@link SharedPreferences}, this is intended behavior to improve
* performance.
*
- * @return The {@link SharedPreferences} where this Preference reads its value(s), or null if it
- * isn't attached to a Preference hierarchy or if {@link PreferenceDataStore} is used
- * instead.
+ * @return the {@link SharedPreferences} where this Preference reads its value(s). If
+ * this preference isn't attached to a Preference hierarchy or if
+ * a {@link PreferenceDataStore} has been set, this method returns {@code null}.
* @see #getEditor()
* @see #setPreferenceDataStore(PreferenceDataStore)
*/
@@ -1129,9 +1151,9 @@
* not show up in the SharedPreferences, this is intended behavior to
* improve performance.
*
- * @return A {@link SharedPreferences.Editor} where this preference saves its value(s), or null
- * if it isn't attached to a Preference hierarchy or if {@link PreferenceDataStore} is
- * used instead.
+ * @return a {@link SharedPreferences.Editor} where this preference saves its value(s). If
+ * this preference isn't attached to a Preference hierarchy or if
+ * a {@link PreferenceDataStore} has been set, this method returns {@code null}.
* @see #shouldCommit()
* @see #getSharedPreferences()
* @see #setPreferenceDataStore(PreferenceDataStore)
@@ -1149,7 +1171,7 @@
* {@link #getEditor()}. This may return false in situations where batch
* committing is being done (by the manager) to improve performance.
*
- * <p>If this preference is using {@link PreferenceDataStore} this value should be irrelevant.
+ * <p>If this preference is using {@link PreferenceDataStore} this value is irrelevant.
*
* @return Whether the Preference should commit its saved value(s).
* @see #getEditor()
@@ -1253,10 +1275,10 @@
}
/**
- * Assigns a {@link PreferenceGroup} as the parent of this Preference. Set null to remove
- * the current parent.
+ * Assigns a {@link PreferenceGroup} as the parent of this Preference. Set {@code null} to
+ * remove the current parent.
*
- * @param parentGroup Parent preference group of this Preference or null if none.
+ * @param parentGroup Parent preference group of this Preference or {@code null} if none.
*/
void assignParent(@Nullable PreferenceGroup parentGroup) {
mParentGroup = parentGroup;
@@ -1425,10 +1447,10 @@
}
/**
- * Returns the {@link PreferenceGroup} which is this Preference assigned to or null if this
- * preference is not assigned to any group or is a root Preference.
+ * Returns the {@link PreferenceGroup} which is this Preference assigned to or {@code null} if
+ * this preference is not assigned to any group or is a root Preference.
*
- * @return The parent PreferenceGroup or null if not attached to any.
+ * @return the parent PreferenceGroup or {@code null} if not attached to any
*/
@Nullable
public PreferenceGroup getParent() {
@@ -1483,7 +1505,7 @@
* if {@link #shouldPersist()} is true).
*
* <p>In case of using {@link PreferenceDataStore}, the <var>restorePersistedValue</var> is
- * always false. But the default value (if provided) is set.
+ * always {@code true}. But the default value (if provided) is set.
*
* <p>This may not always be called. One example is if it should not persist
* but there is no default value given.
@@ -1831,10 +1853,6 @@
return mPreferenceManager.getSharedPreferences().getBoolean(mKey, defaultReturnValue);
}
- boolean canRecycleLayout() {
- return mCanRecycleLayout;
- }
-
@Override
public String toString() {
return getFilterableStringBuilder().toString();
@@ -1910,9 +1928,9 @@
* state. This state should only contain information that is not persistent
* or can be reconstructed later.
*
- * @return A Parcelable object containing the current dynamic state of
- * this Preference, or null if there is nothing interesting to save.
- * The default implementation returns null.
+ * @return A Parcelable object containing the current dynamic state of this Preference, or
+ * {@code null} if there is nothing interesting to save. The default implementation
+ * returns {@code null}.
* @see #onRestoreInstanceState
* @see #saveHierarchyState
*/
@@ -1958,9 +1976,9 @@
}
/**
- * Hook allowing a Preference to re-apply a representation of its internal
- * state that had previously been generated by {@link #onSaveInstanceState}.
- * This function will never be called with a null state.
+ * Hook allowing a Preference to re-apply a representation of its internal state that had
+ * previously been generated by {@link #onSaveInstanceState}. This function will never be called
+ * with a {@code null} state.
*
* @param state The saved state that had previously been returned by
* {@link #onSaveInstanceState}.
diff --git a/core/java/android/preference/PreferenceGroupAdapter.java b/core/java/android/preference/PreferenceGroupAdapter.java
index 5a0b9e9..bee45ab 100644
--- a/core/java/android/preference/PreferenceGroupAdapter.java
+++ b/core/java/android/preference/PreferenceGroupAdapter.java
@@ -164,7 +164,7 @@
preferences.add(preference);
- if (!mHasReturnedViewTypeCount && preference.canRecycleLayout()) {
+ if (!mHasReturnedViewTypeCount && preference.isRecycleEnabled()) {
addPreferenceClassName(preference);
}
@@ -296,7 +296,7 @@
}
final Preference preference = this.getItem(position);
- if (!preference.canRecycleLayout()) {
+ if (!preference.isRecycleEnabled()) {
return IGNORE_ITEM_VIEW_TYPE;
}
diff --git a/core/java/android/preference/PreferenceManager.java b/core/java/android/preference/PreferenceManager.java
index 14b748a..ea32dfd 100644
--- a/core/java/android/preference/PreferenceManager.java
+++ b/core/java/android/preference/PreferenceManager.java
@@ -93,8 +93,8 @@
private SharedPreferences mSharedPreferences;
/**
- * Data store to be used by the Preferences or null if {@link android.content.SharedPreferences}
- * should be used.
+ * Data store to be used by the Preferences or {@code null} if
+ * {@link android.content.SharedPreferences} should be used.
*/
@Nullable
private PreferenceDataStore mPreferenceDataStore;
@@ -484,11 +484,16 @@
/**
* Gets a {@link SharedPreferences} instance that preferences managed by this will use.
*
- * @return A {@link SharedPreferences} instance pointing to the file that contains the values of
- * preferences that are managed by this or null if {@link PreferenceDataStore} is used instead.
+ * @return a {@link SharedPreferences} instance pointing to the file that contains the values of
+ * preferences that are managed by this PreferenceManager. If a
+ * {@link PreferenceDataStore} has been set, this method returns {@code null}.
*/
public SharedPreferences getSharedPreferences() {
- if (mSharedPreferences == null && getPreferenceDataStore() == null) {
+ if (mPreferenceDataStore != null) {
+ return null;
+ }
+
+ if (mSharedPreferences == null) {
final Context storageContext;
switch (mStorage) {
case STORAGE_DEVICE_PROTECTED:
@@ -564,8 +569,8 @@
/**
* Finds a {@link Preference} based on its key.
*
- * @param key The key of the preference to retrieve.
- * @return The {@link Preference} with the key, or null.
+ * @param key the key of the preference to retrieve
+ * @return the {@link Preference} with the key, or {@code null}
* @see PreferenceGroup#findPreference(CharSequence)
*/
@Nullable
@@ -659,11 +664,11 @@
/**
* Returns an editor to use when modifying the shared preferences.
- * <p>
- * Do NOT commit unless {@link #shouldCommit()} returns true.
*
- * @return An editor to use to write to shared preferences or null if
- * {@link PreferenceDataStore} is used instead.
+ * <p>Do NOT commit unless {@link #shouldCommit()} returns true.
+ *
+ * @return an editor to use to write to shared preferences. If a {@link PreferenceDataStore}
+ * has been set, this method returns {@code null}.
* @see #shouldCommit()
*/
SharedPreferences.Editor getEditor() {
@@ -687,6 +692,8 @@
* {@link #getEditor()}. This will return false in cases where the writes
* should be batched, for example when inflating preferences from XML.
*
+ * <p>If preferences are using {@link PreferenceDataStore} this value is irrelevant.
+ *
* @return Whether the client should commit.
*/
boolean shouldCommit() {
@@ -711,8 +718,8 @@
* Returns the activity that shows the preferences. This is useful for doing
* managed queries, but in most cases the use of {@link #getContext()} is
* preferred.
- * <p>
- * This will return null if this class was instantiated with a Context
+ *
+ * <p>This will return {@code null} if this class was instantiated with a Context
* instead of Activity. For example, when setting the default values.
*
* @return The activity that shows the preferences.
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index a95ccbf..56d4ff7 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -18,7 +18,6 @@
import static android.net.TrafficStats.KB_IN_BYTES;
import static android.system.OsConstants.SEEK_SET;
-
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
import static com.android.internal.util.Preconditions.checkCollectionNotEmpty;
@@ -29,6 +28,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
@@ -38,6 +38,7 @@
import android.graphics.Point;
import android.media.ExifInterface;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.OperationCanceledException;
@@ -45,6 +46,7 @@
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.OnCloseListener;
import android.os.Parcelable;
+import android.os.ParcelableException;
import android.os.RemoteException;
import android.os.storage.StorageVolume;
import android.system.ErrnoException;
@@ -147,9 +149,9 @@
/**
* Action of intent issued by DocumentsUI when user wishes to open/configure/manage a particular
* document in the provider application.
- *
+ *
* <p>When issued, the intent will include the URI of the document as the intent data.
- *
+ *
* <p>A provider wishing to provide support for this action should do two things.
* <li>Add an {@code <intent-filter>} matching this action.
* <li>When supplying information in {@link DocumentsProvider#queryChildDocuments}, include
@@ -1023,7 +1025,8 @@
* android.os.CancellationSignal)
*/
public static Bitmap getDocumentThumbnail(
- ContentResolver resolver, Uri documentUri, Point size, CancellationSignal signal) {
+ ContentResolver resolver, Uri documentUri, Point size, CancellationSignal signal)
+ throws FileNotFoundException {
final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
documentUri.getAuthority());
try {
@@ -1032,6 +1035,7 @@
if (!(e instanceof OperationCanceledException)) {
Log.w(TAG, "Failed to load thumbnail for " + documentUri + ": " + e);
}
+ rethrowIfNecessary(resolver, e);
return null;
} finally {
ContentProviderClient.releaseQuietly(client);
@@ -1113,20 +1117,20 @@
/**
* Create a new document with given MIME type and display name.
*
- * @param parentDocumentUri directory with
- * {@link Document#FLAG_DIR_SUPPORTS_CREATE}
+ * @param parentDocumentUri directory with {@link Document#FLAG_DIR_SUPPORTS_CREATE}
* @param mimeType MIME type of new document
* @param displayName name of new document
* @return newly created document, or {@code null} if failed
*/
public static Uri createDocument(ContentResolver resolver, Uri parentDocumentUri,
- String mimeType, String displayName) {
+ String mimeType, String displayName) throws FileNotFoundException {
final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
parentDocumentUri.getAuthority());
try {
return createDocument(client, parentDocumentUri, mimeType, displayName);
} catch (Exception e) {
Log.w(TAG, "Failed to create document", e);
+ rethrowIfNecessary(resolver, e);
return null;
} finally {
ContentProviderClient.releaseQuietly(client);
@@ -1177,13 +1181,14 @@
* failed.
*/
public static Uri renameDocument(ContentResolver resolver, Uri documentUri,
- String displayName) {
+ String displayName) throws FileNotFoundException {
final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
documentUri.getAuthority());
try {
return renameDocument(client, documentUri, displayName);
} catch (Exception e) {
Log.w(TAG, "Failed to rename document", e);
+ rethrowIfNecessary(resolver, e);
return null;
} finally {
ContentProviderClient.releaseQuietly(client);
@@ -1208,7 +1213,8 @@
* @param documentUri document with {@link Document#FLAG_SUPPORTS_DELETE}
* @return if the document was deleted successfully.
*/
- public static boolean deleteDocument(ContentResolver resolver, Uri documentUri) {
+ public static boolean deleteDocument(ContentResolver resolver, Uri documentUri)
+ throws FileNotFoundException {
final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
documentUri.getAuthority());
try {
@@ -1216,6 +1222,7 @@
return true;
} catch (Exception e) {
Log.w(TAG, "Failed to delete document", e);
+ rethrowIfNecessary(resolver, e);
return false;
} finally {
ContentProviderClient.releaseQuietly(client);
@@ -1240,13 +1247,14 @@
* @return the copied document, or {@code null} if failed.
*/
public static Uri copyDocument(ContentResolver resolver, Uri sourceDocumentUri,
- Uri targetParentDocumentUri) {
+ Uri targetParentDocumentUri) throws FileNotFoundException {
final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
sourceDocumentUri.getAuthority());
try {
return copyDocument(client, sourceDocumentUri, targetParentDocumentUri);
} catch (Exception e) {
Log.w(TAG, "Failed to copy document", e);
+ rethrowIfNecessary(resolver, e);
return null;
} finally {
ContentProviderClient.releaseQuietly(client);
@@ -1274,7 +1282,7 @@
* @return the moved document, or {@code null} if failed.
*/
public static Uri moveDocument(ContentResolver resolver, Uri sourceDocumentUri,
- Uri sourceParentDocumentUri, Uri targetParentDocumentUri) {
+ Uri sourceParentDocumentUri, Uri targetParentDocumentUri) throws FileNotFoundException {
final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
sourceDocumentUri.getAuthority());
try {
@@ -1282,6 +1290,7 @@
targetParentDocumentUri);
} catch (Exception e) {
Log.w(TAG, "Failed to move document", e);
+ rethrowIfNecessary(resolver, e);
return null;
} finally {
ContentProviderClient.releaseQuietly(client);
@@ -1311,7 +1320,7 @@
* @return true if the document was removed successfully.
*/
public static boolean removeDocument(ContentResolver resolver, Uri documentUri,
- Uri parentDocumentUri) {
+ Uri parentDocumentUri) throws FileNotFoundException {
final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
documentUri.getAuthority());
try {
@@ -1319,6 +1328,7 @@
return true;
} catch (Exception e) {
Log.w(TAG, "Failed to remove document", e);
+ rethrowIfNecessary(resolver, e);
return false;
} finally {
ContentProviderClient.releaseQuietly(client);
@@ -1379,7 +1389,8 @@
* @return the path of the document, or {@code null} if failed.
* @see DocumentsProvider#findDocumentPath(String, String)
*/
- public static Path findDocumentPath(ContentResolver resolver, Uri treeUri) {
+ public static Path findDocumentPath(ContentResolver resolver, Uri treeUri)
+ throws FileNotFoundException {
checkArgument(isTreeUri(treeUri), treeUri + " is not a tree uri.");
final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
@@ -1388,6 +1399,7 @@
return findDocumentPath(client, treeUri);
} catch (Exception e) {
Log.w(TAG, "Failed to find path", e);
+ rethrowIfNecessary(resolver, e);
return null;
} finally {
ContentProviderClient.releaseQuietly(client);
@@ -1473,13 +1485,14 @@
* @see Intent#EXTRA_EMAIL
*/
public static IntentSender createWebLinkIntent(ContentResolver resolver, Uri uri,
- Bundle options) {
+ Bundle options) throws FileNotFoundException {
final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
uri.getAuthority());
try {
return createWebLinkIntent(client, uri, options);
} catch (Exception e) {
Log.w(TAG, "Failed to create a web link intent", e);
+ rethrowIfNecessary(resolver, e);
return null;
} finally {
ContentProviderClient.releaseQuietly(client);
@@ -1544,6 +1557,20 @@
return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH, extras);
}
+ private static void rethrowIfNecessary(ContentResolver resolver, Exception e)
+ throws FileNotFoundException {
+ // We only want to throw applications targetting O and above
+ if (resolver.getTargetSdkVersion() >= Build.VERSION_CODES.O) {
+ if (e instanceof ParcelableException) {
+ ((ParcelableException) e).maybeRethrow(FileNotFoundException.class);
+ } else if (e instanceof RemoteException) {
+ ((RemoteException) e).rethrowAsRuntimeException();
+ } else if (e instanceof RuntimeException) {
+ throw (RuntimeException) e;
+ }
+ }
+ }
+
/**
* Holds a path from a document to a particular document under it. It
* may also contains the root ID where the path resides.
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 89a80f0..620d33a5 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -38,6 +38,7 @@
import android.Manifest;
import android.annotation.CallSuper;
import android.annotation.Nullable;
+import android.app.RecoverableSecurityException;
import android.content.ClipDescription;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -57,6 +58,7 @@
import android.os.OperationCanceledException;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.OnCloseListener;
+import android.os.ParcelableException;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Path;
import android.provider.DocumentsContract.Root;
@@ -233,6 +235,10 @@
* {@link Document#COLUMN_DOCUMENT_ID}. You must allocate a new
* {@link Document#COLUMN_DOCUMENT_ID} to represent the document, which must
* not change once returned.
+ * <p>
+ * {@link RecoverableSecurityException} can be thrown if more input is required
+ * from the user (such as insufficient permission), but it is not guaranteed that
+ * the client will handle this properly.
*
* @param parentDocumentId the parent directory to create the new document
* under.
@@ -256,6 +262,10 @@
* URI permission grants will be updated to point at the new document. If
* the original {@link Document#COLUMN_DOCUMENT_ID} is still valid after the
* rename, return {@code null}.
+ * <p>
+ * {@link RecoverableSecurityException} can be thrown if more input is required
+ * from the user (such as insufficient permission), but it is not guaranteed that
+ * the client will handle this properly.
*
* @param documentId the document to rename.
* @param displayName the updated display name of the document. The provider
@@ -276,6 +286,10 @@
* call (such as documents inside a directory) the implementor is
* responsible for revoking those permissions using
* {@link #revokeDocumentPermission(String)}.
+ * <p>
+ * {@link RecoverableSecurityException} can be thrown if more input is required
+ * from the user (such as insufficient permission), but it is not guaranteed that
+ * the client will handle this properly.
*
* @param documentId the document to delete.
*/
@@ -291,6 +305,10 @@
* the same document provider. Upon completion returns the document id of
* the copied document at the target destination. {@code null} must never
* be returned.
+ * <p>
+ * {@link RecoverableSecurityException} can be thrown if more input is required
+ * from the user (such as insufficient permission), but it is not guaranteed that
+ * the client will handle this properly.
*
* @param sourceDocumentId the document to copy.
* @param targetParentDocumentId the target document to be copied into as a child.
@@ -311,6 +329,10 @@
*
* <p>It's the responsibility of the provider to revoke grants if the document
* is no longer accessible using <code>sourceDocumentId</code>.
+ * <p>
+ * {@link RecoverableSecurityException} can be thrown if more input is required
+ * from the user (such as insufficient permission), but it is not guaranteed that
+ * the client will handle this properly.
*
* @param sourceDocumentId the document to move.
* @param sourceParentDocumentId the parent of the document to move.
@@ -333,6 +355,9 @@
* <p>It's the responsibility of the provider to revoke grants if the document is
* removed from the last parent, and effectively the document is deleted.
*
+ * <p>{@link RecoverableSecurityException} can be thrown if more input is required
+ * from the user (such as insufficient permission), but it is not guaranteed that
+ * the client will handle this properly.
* @param documentId the document to remove.
* @param parentDocumentId the parent of the document to move.
*/
@@ -352,6 +377,10 @@
* <p>This API assumes that document ID has enough info to infer the root.
* Different roots should use different document ID to refer to the same
* document.
+ * <p>{@link RecoverableSecurityException} can be thrown if more input is required
+ * from the user (such as insufficient permission), but it is not guaranteed that
+ * the client will handle this properly.perly.
+ *
*
* @param parentDocumentId the document from which the path starts if not null,
* or null to indicate a path from the root is requested.
@@ -368,9 +397,11 @@
/**
* Creates an intent sender for a web link, if the document is web linkable.
* <p>
- * Before any new permissions are granted for the linked document, a visible
- * UI must be shown, so the user can explicitly confirm whether the permission
- * grants are expected. The user must be able to cancel the operation.
+ * {@link RecoverableSecurityException} can be thrown if user does not have
+ * sufficient permission for the linked document. Before any new permissions
+ * are granted for the linked document, a visible UI must be shown, so the
+ * user can explicitly confirm whether the permission grants are expected.
+ * The user must be able to cancel the operation.
* <p>
* Options passed as an argument may include a list of recipients, such
* as email addresses. The provider should reflect these options if possible,
@@ -404,6 +435,10 @@
* If this set of roots changes, you must call {@link ContentResolver#notifyChange(Uri,
* android.database.ContentObserver, boolean)} with
* {@link DocumentsContract#buildRootsUri(String)} to notify the system.
+ * <p>
+ * {@link RecoverableSecurityException} can be thrown if more input is required
+ * from the user (such as insufficient permission), or returned as part of
+ * Cursor's bundle. It is not guaranteed that the client will handle this properly.
*
* @param projection list of {@link Root} columns to put into the cursor. If
* {@code null} all supported columns should be included.
@@ -417,6 +452,10 @@
* sorted by {@link Document#COLUMN_LAST_MODIFIED} in descending order, and
* limited to only return the 64 most recently modified documents.
* <p>
+ * {@link RecoverableSecurityException} can be thrown if more input is required
+ * from the user (such as insufficient permission), or returned as part of
+ * Cursor's bundle. It is not guaranteed that the client will handle this properly.
+ * <p>
* Recent documents do not support change notifications.
*
* @param projection list of {@link Document} columns to put into the
@@ -433,6 +472,11 @@
/**
* Return metadata for the single requested document. You should avoid
* making network requests to keep this request fast.
+ * <p>
+ * {@link RecoverableSecurityException} can be thrown if more input is required
+ * from the user (such as insufficient permission), or returned as part of
+ * Cursor's bundle. It is not guaranteed that the client will handle this properly.
+ *
*
* @param documentId the document to return.
* @param projection list of {@link Document} columns to put into the
@@ -465,6 +509,11 @@
* you can call {@link ContentResolver#notifyChange(Uri,
* android.database.ContentObserver, boolean)} with that Uri to send change
* notifications.
+ * <p>
+ * {@link RecoverableSecurityException} can be thrown if more input is required
+ * from the user (such as insufficient permission), or returned as part of
+ * Cursor's bundle. It is not guaranteed that the client will handle this properly.
+ *
*
* @param parentDocumentId the directory to return children for.
* @param projection list of {@link Document} columns to put into the
@@ -503,6 +552,10 @@
* you can call {@link ContentResolver#notifyChange(Uri,
* android.database.ContentObserver, boolean)} with that Uri to send change
* notifications.
+ * <p>
+ * {@link RecoverableSecurityException} can be thrown if more input is required
+ * from the user (such as insufficient permission), or returned as part of
+ * Cursor's bundle. It is not guaranteed that the client will handle this properly.
*
* @param parentDocumentId the directory to return children for.
* @param projection list of {@link Document} columns to put into the
@@ -556,6 +609,10 @@
* String, String)}. Then you can call {@link ContentResolver#notifyChange(Uri,
* android.database.ContentObserver, boolean)} with that Uri to send change
* notifications.
+ * <p>
+ * {@link RecoverableSecurityException} can be thrown if more input is required
+ * from the user (such as insufficient permission), or returned as part of
+ * Cursor's bundle. It is not guaranteed that the client will handle this properly.
*
* @param rootId the root to search under.
* @param query string to match documents against.
@@ -583,6 +640,10 @@
* of {@link Document#COLUMN_MIME_TYPE} for this document. The default
* implementation queries {@link #queryDocument(String, String[])}, so
* providers may choose to override this as an optimization.
+ * <p>
+ * {@link RecoverableSecurityException} can be thrown if more input is required
+ * from the user (such as insufficient permission), but it is not guaranteed that
+ * the client will handle this properly.
*/
public String getDocumentType(String documentId) throws FileNotFoundException {
final Cursor cursor = queryDocument(documentId, null);
@@ -608,6 +669,10 @@
* <p>
* If you block while downloading content, you should periodically check
* {@link CancellationSignal#isCanceled()} to abort abandoned open requests.
+ * <p>
+ * {@link RecoverableSecurityException} can be thrown if more input is required
+ * from the user (such as insufficient permission), but it is not guaranteed that
+ * the client will handle this properly.
*
* @param documentId the document to return.
* @param mode the mode to open with, such as 'r', 'w', or 'rw'.
@@ -632,6 +697,10 @@
* If you perform expensive operations to download or generate a thumbnail,
* you should periodically check {@link CancellationSignal#isCanceled()} to
* abort abandoned thumbnail requests.
+ * <p>
+ * {@link RecoverableSecurityException} can be thrown if more input is required
+ * from the user (such as insufficient permission), but it is not guaranteed that
+ * the client will handle this properly.
*
* @param documentId the document to return.
* @param sizeHint hint of the optimal thumbnail dimensions.
@@ -654,6 +723,10 @@
* matching the specified MIME type filter.
* <p>
* Virtual documents must have at least one streamable format.
+ * <p>
+ * {@link RecoverableSecurityException} can be thrown if more input is required
+ * from the user (such as insufficient permission), but it is not guaranteed that
+ * the client will handle this properly.
*
* @param documentId the document to return.
* @param mimeTypeFilter the MIME type filter for the requested format. May
@@ -877,7 +950,7 @@
try {
return callUnchecked(method, arg, extras);
} catch (FileNotFoundException e) {
- throw new IllegalStateException("Failed call " + method, e);
+ throw new ParcelableException(e);
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 52aa1d5..8a2a14c 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -506,8 +506,6 @@
* Input: Nothing.
* <p>
* Output: Nothing.
- *
- * @hide
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_NIGHT_DISPLAY_SETTINGS =
@@ -3852,6 +3850,17 @@
};
/**
+ * Setting to determine whether or not to show the battery percentage in the status bar.
+ * 0 - Don't show percentage
+ * 1 - Show percentage
+ * @hide
+ */
+ public static final String SHOW_BATTERY_PERCENT = "status_bar_show_battery_percent";
+
+ /** @hide */
+ private static final Validator SHOW_BATTERY_PERCENT_VALIDATOR = sBooleanValidator;
+
+ /**
* IMPORTANT: If you add a new public settings you also have to add it to
* PUBLIC_SETTINGS below. If the new setting is hidden you have to add
* it to PRIVATE_SETTINGS below. Also add a validator that can validate
@@ -3913,7 +3922,8 @@
RINGTONE,
LOCK_TO_APP_ENABLED,
NOTIFICATION_SOUND,
- ACCELEROMETER_ROTATION
+ ACCELEROMETER_ROTATION,
+ SHOW_BATTERY_PERCENT
};
/**
@@ -4013,6 +4023,7 @@
PRIVATE_SETTINGS.add(POINTER_SPEED);
PRIVATE_SETTINGS.add(LOCK_TO_APP_ENABLED);
PRIVATE_SETTINGS.add(EGG_MODE);
+ PRIVATE_SETTINGS.add(SHOW_BATTERY_PERCENT);
}
/**
@@ -4090,6 +4101,7 @@
VALIDATORS.put(WIFI_STATIC_NETMASK, WIFI_STATIC_NETMASK_VALIDATOR);
VALIDATORS.put(WIFI_STATIC_DNS1, WIFI_STATIC_DNS1_VALIDATOR);
VALIDATORS.put(WIFI_STATIC_DNS2, WIFI_STATIC_DNS2_VALIDATOR);
+ VALIDATORS.put(SHOW_BATTERY_PERCENT, SHOW_BATTERY_PERCENT_VALIDATOR);
}
/**
@@ -7654,12 +7666,14 @@
public static final String HDMI_CONTROL_ENABLED = "hdmi_control_enabled";
/**
- * Whether HDMI system audio is enabled. If enabled, TV internal speaker is muted,
- * and the output is redirected to AV Receiver connected via
- * {@Global#HDMI_SYSTEM_AUDIO_OUTPUT}.
+ * Whether HDMI System Audio Control feature is enabled. If enabled, TV will try to turn on
+ * system audio mode if there's a connected CEC-enabled AV Receiver. Then audio stream will
+ * be played on AVR instead of TV spaeker. If disabled, the system audio mode will never be
+ * activated.
* @hide
*/
- public static final String HDMI_SYSTEM_AUDIO_ENABLED = "hdmi_system_audio_enabled";
+ public static final String HDMI_SYSTEM_AUDIO_CONTROL_ENABLED =
+ "hdmi_system_audio_control_enabled";
/**
* Whether TV will automatically turn on upon reception of the CEC command
diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java
index d220052..f6d40db 100644
--- a/core/java/android/service/autofill/AutofillServiceInfo.java
+++ b/core/java/android/service/autofill/AutofillServiceInfo.java
@@ -78,14 +78,12 @@
// TODO(b/35956626): inline newSettingsActivity once clients migrate
final String newSettingsActivity =
metaDataArray.getString(R.styleable.AutofillService_settingsActivity);
- System.out.println(">>> NEW CRAP MAN: " + newSettingsActivity); // TODO(felipeal): tmp
if (newSettingsActivity != null) {
mSettingsActivity = newSettingsActivity;
} else {
mSettingsActivity =
metaDataArray.getString(R.styleable.AutoFillService_settingsActivity);
}
- System.out.println(">>> FINAL CRAP MAN: " + mSettingsActivity); // TODO(felipeal): tmp
metaDataArray.recycle();
} else {
mSettingsActivity = null;
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index a8e3ff8..1bd88c7 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -198,6 +198,26 @@
return this;
}
+
+ /**
+ * @hide
+ */
+ // TODO(b/33197203): temporary fix to runtime crash
+ public @NonNull Builder addSavableIds(@Nullable AutoFillId... ids) {
+ throwIfDestroyed();
+
+ if (ids == null) {
+ return this;
+ }
+ for (AutoFillId id : ids) {
+ if (mSavableIds == null) {
+ mSavableIds = new ArraySet<>();
+ }
+ mSavableIds.add(id.getDaRealId());
+ }
+ return this;
+ }
+
/**
* Sets an optional description to be shown in the UI when the user is asked to save.
*
diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java
index c2508a6..ea1100e 100644
--- a/core/java/android/text/Hyphenator.java
+++ b/core/java/android/text/Hyphenator.java
@@ -135,6 +135,10 @@
private static Hyphenator loadHyphenator(HyphenationData data) {
String patternFilename = "hyph-" + data.mLanguageTag.toLowerCase(Locale.US) + ".hyb";
File patternFile = new File(getSystemHyphenatorLocation(), patternFilename);
+ if (!patternFile.canRead()) {
+ Log.e(TAG, "hyphenation patterns for " + patternFile + " not found or unreadable");
+ return null;
+ }
try {
RandomAccessFile f = new RandomAccessFile(patternFile, "r");
try {
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 94c463c..353dfed 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -1031,6 +1031,7 @@
float avail, TextUtils.TruncateAt where,
int line, float textWidth, TextPaint paint,
boolean forceEllipsis) {
+ avail -= getTotalInsets(line);
if (textWidth <= avail && !forceEllipsis) {
// Everything fits!
mLines[mColumns * line + ELLIPSIS_START] = 0;
@@ -1134,6 +1135,17 @@
mLines[mColumns * line + ELLIPSIS_COUNT] = ellipsisCount;
}
+ private float getTotalInsets(int line) {
+ int totalIndent = 0;
+ if (mLeftIndents != null) {
+ totalIndent = mLeftIndents[Math.min(line, mLeftIndents.length - 1)];
+ }
+ if (mRightIndents != null) {
+ totalIndent += mRightIndents[Math.min(line, mRightIndents.length - 1)];
+ }
+ return totalIndent;
+ }
+
// Override the base class so we can directly access our members,
// rather than relying on member functions.
// The logic mirrors that of Layout.getLineForVertical
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index af2547e..255a029 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -799,8 +799,10 @@
* targetId list. If the target parameter is null, then the target list
* is not checked (this is in the case of ListView items, where the
* views are ignored and only the ids are used).
+ *
+ * @hide
*/
- boolean isValidTarget(View target) {
+ public boolean isValidTarget(View target) {
if (target == null) {
return false;
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index dd76fcf..586b3b2 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -201,16 +201,18 @@
void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout);
/**
- * Retrieve the current screen orientation, constants as per
- * {@link android.view.Surface}.
+ * Retrieve the current orientation of the primary screen.
+ * @return Constant as per {@link android.view.Surface.Rotation}.
+ *
+ * @see android.view.Display#DEFAULT_DISPLAY
*/
- int getRotation();
+ int getDefaultDisplayRotation();
/**
- * Watch the rotation of the screen. Returns the current rotation,
+ * Watch the rotation of the specified screen. Returns the current rotation,
* calls back when it changes.
*/
- int watchRotation(IRotationWatcher watcher);
+ int watchRotation(IRotationWatcher watcher, int displayId);
/**
* Remove a rotation watcher set using watchRotation.
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 61b1247..6d320ef 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -500,7 +500,12 @@
// While creating the surface, we will set it's initial
// geometry. Outside of that though, we should generally
// leave it to the RenderThread.
- if (creating || !mRtHandlingPositionUpdates) {
+ //
+ // There is one more case when the buffer size changes we aren't yet
+ // prepared to sync (as even following the transaction applying
+ // we still need to latch a buffer).
+ // b/28866173
+ if (sizeChanged || creating || !mRtHandlingPositionUpdates) {
mSurfaceControl.setPosition(mScreenRect.left, mScreenRect.top);
mSurfaceControl.setMatrix(mScreenRect.width() / (float) mSurfaceWidth,
0.0f, 0.0f,
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index aa1cbf2..80f6c32 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -978,142 +978,123 @@
*/
public static final int AUTOFILL_MODE_MANUAL = 2;
- /** @hide */
- @IntDef({
- AUTOFILL_HINT_NONE,
- AUTOFILL_HINT_EMAIL_ADDRESS,
- AUTOFILL_HINT_NAME,
- AUTOFILL_HINT_POSTAL_ADDRESS,
- AUTOFILL_HINT_PASSWORD,
- AUTOFILL_HINT_PHONE,
- AUTOFILL_HINT_USERNAME,
- AUTOFILL_HINT_POSTAL_CODE,
- AUTOFILL_HINT_CREDIT_CARD_NUMBER,
- AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE,
- AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE,
- AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH,
- AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR,
- AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface AutofillHint {}
-
- /**
- * No autofill hint is set.
- *
- * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
- * {@code android:autofillHint}.
- */
- public static final int AUTOFILL_HINT_NONE = 0;
-
/**
* This view contains an email address.
*
- * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
- * {@code android:autofillHint}.
+ * Use with {@link #setAutofillHint(String[])}, or set "{@value #AUTOFILL_HINT_EMAIL_ADDRESS}"
+ * to <a href="#attr_android:autofillHint"> {@code android:autofillHint}.
*/
- public static final int AUTOFILL_HINT_EMAIL_ADDRESS = 0x1;
+ public static final String AUTOFILL_HINT_EMAIL_ADDRESS = "emailAddress";
/**
* The view contains a real name.
*
- * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
- * {@code android:autofillHint}.
+ * Use with {@link #setAutofillHint(String[])}, or set "{@value #AUTOFILL_HINT_NAME}" to
+ * <a href="#attr_android:autofillHint"> {@code android:autofillHint}.
*/
- public static final int AUTOFILL_HINT_NAME = 0x2;
+ public static final String AUTOFILL_HINT_NAME = "name";
/**
* The view contains a user name.
*
- * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
- * {@code android:autofillHint}.
+ * Use with {@link #setAutofillHint(String[])}, or set "{@value #AUTOFILL_HINT_USERNAME}" to
+ * <a href="#attr_android:autofillHint"> {@code android:autofillHint}.
*/
- public static final int AUTOFILL_HINT_USERNAME = 0x4;
+ public static final String AUTOFILL_HINT_USERNAME = "username";
/**
* The view contains a password.
*
- * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
- * {@code android:autofillHint}.
+ * Use with {@link #setAutofillHint(String[])}, or set "{@value #AUTOFILL_HINT_PASSWORD}" to
+ * <a href="#attr_android:autofillHint"> {@code android:autofillHint}.
*/
- public static final int AUTOFILL_HINT_PASSWORD = 0x8;
+ public static final String AUTOFILL_HINT_PASSWORD = "password";
/**
* The view contains a phone number.
*
- * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
- * {@code android:autofillHint}.
+ * Use with {@link #setAutofillHint(String[])}, or set "{@value #AUTOFILL_HINT_PHONE}" to
+ * <a href="#attr_android:autofillHint"> {@code android:autofillHint}.
*/
- public static final int AUTOFILL_HINT_PHONE = 0x10;
+ public static final String AUTOFILL_HINT_PHONE = "phone";
/**
* The view contains a postal address.
*
- * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
- * {@code android:autofillHint}.
+ * Use with {@link #setAutofillHint(String[])}, or set "{@value #AUTOFILL_HINT_POSTAL_ADDRESS}"
+ * to <a href="#attr_android:autofillHint"> {@code android:autofillHint}.
*/
- public static final int AUTOFILL_HINT_POSTAL_ADDRESS = 0x20;
+ public static final String AUTOFILL_HINT_POSTAL_ADDRESS = "postalAddress";
/**
* The view contains a postal code.
*
- * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
- * {@code android:autofillHint}.
+ * Use with {@link #setAutofillHint(String[])}, or set "{@value #AUTOFILL_HINT_POSTAL_CODE}" to
+ * <a href="#attr_android:autofillHint"> {@code android:autofillHint}.
*/
- public static final int AUTOFILL_HINT_POSTAL_CODE = 0x40;
+ public static final String AUTOFILL_HINT_POSTAL_CODE = "postalCode";
/**
* The view contains a credit card number.
*
- * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
- * {@code android:autofillHint}.
+ * Use with {@link #setAutofillHint(String[])}, or set "{@value
+ * #AUTOFILL_HINT_CREDIT_CARD_NUMBER}" to <a href="#attr_android:autofillHint"> {@code
+ * android:autofillHint}.
*/
- public static final int AUTOFILL_HINT_CREDIT_CARD_NUMBER = 0x80;
+ public static final String AUTOFILL_HINT_CREDIT_CARD_NUMBER = "creditCardNumber";
/**
* The view contains a credit card security code.
*
- * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
- * {@code android:autofillHint}.
+ * Use with {@link #setAutofillHint(String[])}, or set "{@value
+ * #AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE}" to <a href="#attr_android:autofillHint"> {@code
+ * android:autofillHint}.
*/
- public static final int AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE = 0x100;
+ public static final String AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE = "creditCardSecurityCode";
/**
* The view contains a credit card expiration date.
*
- * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
- * {@code android:autofillHint}.
+ * Use with {@link #setAutofillHint(String[])}, or set "{@value
+ * #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE}" to <a href="#attr_android:autofillHint"> {@code
+ * android:autofillHint}.
*/
- public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE = 0x200;
+ public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE =
+ "creditCardExpirationDate";
/**
* The view contains the month a credit card expires.
*
- * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
- * {@code android:autofillHint}.
+ * Use with {@link #setAutofillHint(String[])}, or set "{@value
+ * #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH}" to <a href="#attr_android:autofillHint"> {@code
+ * android:autofillHint}.
*/
- public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH = 0x400;
+ public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH =
+ "creditCardExpirationMonth";
/**
* The view contains the year a credit card expires.
*
- * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
- * {@code android:autofillHint}.
+ * Use with {@link #setAutofillHint(String[])}, or set "{@value
+ * #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR}" to <a href="#attr_android:autofillHint"> {@code
+ * android:autofillHint}.
*/
- public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR = 0x800;
+ public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR =
+ "creditCardExpirationYear";
/**
* The view contains the day a credit card expires.
*
- * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
- * {@code android:autofillHint}.
+ * Use with {@link #setAutofillHint(String[])}, or set "{@value
+ * #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY}" to <a href="#attr_android:autofillHint"> {@code
+ * android:autofillHint}.
*/
- public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY = 0x1000;
+ public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY = "creditCardExpirationDay";
/**
- * Hint for the autofill services that describes the content of the view.
+ * Hintd for the autofill services that describes the content of the view.
*/
- @AutofillHint private int mAutofillHint;
+ private @Nullable String[] mAutofillHint;
/** @hide */
@IntDef({
@@ -5049,7 +5030,37 @@
break;
case R.styleable.View_autofillHint:
if (a.peekValue(attr) != null) {
- setAutofillHint(a.getInt(attr, AUTOFILL_HINT_NONE));
+ CharSequence[] rawHints = null;
+ String rawString = null;
+
+ if (a.getType(attr) == TypedValue.TYPE_REFERENCE) {
+ int resId = a.getResourceId(attr, 0);
+
+ try {
+ rawHints = a.getTextArray(attr);
+ } catch (NullPointerException e) {
+ rawString = getResources().getString(resId);
+ }
+ } else {
+ rawString = a.getString(attr);
+ }
+
+ if (rawHints == null) {
+ if (rawString == null) {
+ throw new IllegalArgumentException(
+ "Could not resolve autofillHint");
+ } else {
+ rawHints = rawString.split(",");
+ }
+ }
+
+ String[] hints = new String[rawHints.length];
+
+ int numHints = rawHints.length;
+ for (int rawHintNum = 0; rawHintNum < numHints; rawHintNum++) {
+ hints[rawHintNum] = rawHints[rawHintNum].toString().trim();
+ }
+ setAutofillHint(hints);
}
break;
case R.styleable.View_importantForAutofill:
@@ -6132,6 +6143,9 @@
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
+
+ notifyEnterOrExitForAutoFillIfNeeded(true);
+
return result;
}
@@ -6791,14 +6805,18 @@
mAttachInfo.mKeyDispatchState.reset(this);
}
+ notifyEnterOrExitForAutoFillIfNeeded(gainFocus);
+ }
+
+ private void notifyEnterOrExitForAutoFillIfNeeded(boolean enter) {
if (isAutofillable() && isAttachedToWindow()
&& getResolvedAutofillMode() == AUTOFILL_MODE_AUTO) {
AutofillManager afm = getAutofillManager();
if (afm != null) {
- if (gainFocus) {
- afm.startAutofillRequest(this);
- } else {
- afm.stopAutofillRequest(this);
+ if (enter && hasWindowFocus() && isFocused()) {
+ afm.notifyViewEntered(this);
+ } else if (!hasWindowFocus() || !isFocused()) {
+ afm.notifyViewExited(this);
}
}
}
@@ -7250,9 +7268,10 @@
* Called when assist structure is being retrieved from a view as part of an autofill request.
*
* <p>This method already provides most of what's needed for autofill, but should be overridden
+ * when:
* <ol>
* <li>The view contents does not include PII (Personally Identifiable Information), so it
- * can call {@link ViewStructure#setSanitized(boolean)} passing {@code true}.
+ * can call {@link ViewStructure#setDataIsSensitive(boolean)} passing {@code false}.
* <li>It must set fields such {@link ViewStructure#setText(CharSequence)},
* {@link ViewStructure#setAutofillOptions(String[])}, or {@link ViewStructure#setUrl(String)}.
* </ol>
@@ -7368,13 +7387,17 @@
* <li>Also implement {@link #autofillVirtual(int, AutofillValue)} to autofill the virtual
* children.
* <li>Call
- * {@link android.view.autofill.AutofillManager#startAutofillRequestOnVirtualView} and
- * {@link android.view.autofill.AutofillManager#stopAutofillRequestOnVirtualView(View, int)}
+ * {@link android.view.autofill.AutofillManager#notifyVirtualViewEntered} and
+ * {@link android.view.autofill.AutofillManager#notifyVirtualViewExited(View, int)}
* when the focus inside the view changed.
- * <li>Call {@link android.view.autofill.AutofillManager#virtualValueChanged(View, int,
+ * <li>Call {@link android.view.autofill.AutofillManager#notifyVirtualValueChanged(View, int,
* AutofillValue)} when the value of a child changed.
- * <li>Call {@link android.view.autofill.AutofillManager#reset()} when the autofill context
- * of the view structure changed.
+ * <li>Call {@link AutofillManager#commit()} when the autofill context
+ * of the view structure changed and you want the current autofill interaction if such
+ * to be commited.
+ * <li>Call {@link AutofillManager#cancel()} ()} when the autofill context
+ * of the view structure changed and you want the current autofill interaction if such
+ * to be cancelled.
* </ol>
*
* @param structure Fill in with structured view data.
@@ -7453,12 +7476,12 @@
/**
* Describes the content of a view so that a autofill service can fill in the appropriate data.
*
- * @return The hint set via the attribute
+ * @return The hint set via the attribute or {@code null} if no hint it set.
*
* @attr ref android.R.styleable#View_autofillHint
*/
@ViewDebug.ExportedProperty()
- @AutofillHint public int getAutofillHint() {
+ @Nullable public String[] getAutofillHint() {
return mAutofillHint;
}
@@ -9088,11 +9111,15 @@
* Sets the a hint that helps the autofill service to select the appropriate data to fill the
* view.
*
- * @param autofillHint The autofill hint to set
+ * @param autofillHint The autofill hint to set. If the array is emtpy, {@code null} is set.
* @attr ref android.R.styleable#View_autofillHint
*/
- public void setAutofillHint(@AutofillHint int autofillHint) {
- mAutofillHint = autofillHint;
+ public void setAutofillHint(@Nullable String... autofillHint) {
+ if (autofillHint == null || autofillHint.length == 0) {
+ mAutofillHint = null;
+ } else {
+ mAutofillHint = autofillHint;
+ }
}
/**
@@ -11085,6 +11112,7 @@
@CallSuper
public void dispatchStartTemporaryDetach() {
mPrivateFlags3 |= PFLAG3_TEMPORARY_DETACH;
+ notifyEnterOrExitForAutoFillIfNeeded(false);
onStartTemporaryDetach();
}
@@ -11110,6 +11138,7 @@
if (hasWindowFocus() && hasFocus()) {
InputMethodManager.getInstance().focusIn(this);
}
+ notifyEnterOrExitForAutoFillIfNeeded(true);
}
/**
@@ -11512,6 +11541,9 @@
} else if (imm != null && (mPrivateFlags & PFLAG_FOCUSED) != 0) {
imm.focusIn(this);
}
+
+ notifyEnterOrExitForAutoFillIfNeeded(hasWindowFocus);
+
refreshDrawableState();
}
@@ -16879,12 +16911,7 @@
}
needGlobalAttributesUpdate(false);
- if (isAutofillable() && isFocused() && getResolvedAutofillMode() == AUTOFILL_MODE_AUTO) {
- AutofillManager afm = getAutofillManager();
- if (afm != null) {
- afm.startAutofillRequest(this);
- }
- }
+ notifyEnterOrExitForAutoFillIfNeeded(true);
}
void dispatchDetachedFromWindow() {
@@ -16932,12 +16959,7 @@
mOverlay.getOverlayView().dispatchDetachedFromWindow();
}
- if (isAutofillable() && isFocused() && getResolvedAutofillMode() == AUTOFILL_MODE_AUTO) {
- AutofillManager afm = getAutofillManager();
- if (afm != null) {
- afm.stopAutofillRequest(this);
- }
- }
+ notifyEnterOrExitForAutoFillIfNeeded(false);
}
/**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 9fa9985e..de0ec40 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -46,6 +46,7 @@
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Pools;
import android.util.Pools.SynchronizedPool;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -3327,7 +3328,74 @@
@Override
public void dispatchProvideStructure(ViewStructure structure) {
super.dispatchProvideStructure(structure);
- dispatchProvideStructureForAssistOrAutoFill(structure, false);
+ if (isAssistBlocked() || structure.getChildCount() != 0) {
+ return;
+ }
+ final int childrenCount = mChildrenCount;
+ if (childrenCount <= 0) {
+ return;
+ }
+ structure.setChildCount(childrenCount);
+ ArrayList<View> preorderedList = buildOrderedChildList();
+ boolean customOrder = preorderedList == null
+ && isChildrenDrawingOrderEnabled();
+ for (int i = 0; i < childrenCount; i++) {
+ int childIndex;
+ try {
+ childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
+ } catch (IndexOutOfBoundsException e) {
+ childIndex = i;
+ if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.M) {
+ Log.w(TAG, "Bad getChildDrawingOrder while collecting assist @ "
+ + i + " of " + childrenCount, e);
+ // At least one app is failing when we call getChildDrawingOrder
+ // at this point, so deal semi-gracefully with it by falling back
+ // on the basic order.
+ customOrder = false;
+ if (i > 0) {
+ // If we failed at the first index, there really isn't
+ // anything to do -- we will just proceed with the simple
+ // sequence order.
+ // Otherwise, we failed in the middle, so need to come up
+ // with an order for the remaining indices and use that.
+ // Failed at the first one, easy peasy.
+ int[] permutation = new int[childrenCount];
+ SparseBooleanArray usedIndices = new SparseBooleanArray();
+ // Go back and collected the indices we have done so far.
+ for (int j = 0; j < i; j++) {
+ permutation[j] = getChildDrawingOrder(childrenCount, j);
+ usedIndices.put(permutation[j], true);
+ }
+ // Fill in the remaining indices with indices that have not
+ // yet been used.
+ int nextIndex = 0;
+ for (int j = i; j < childrenCount; j++) {
+ while (usedIndices.get(nextIndex, false)) {
+ nextIndex++;
+ }
+ permutation[j] = nextIndex;
+ nextIndex++;
+ }
+ // Build the final view list.
+ preorderedList = new ArrayList<>(childrenCount);
+ for (int j = 0; j < childrenCount; j++) {
+ final int index = permutation[j];
+ final View child = mChildren[index];
+ preorderedList.add(child);
+ }
+ }
+ } else {
+ throw e;
+ }
+ }
+ final View child = getAndVerifyPreorderedView(preorderedList, mChildren,
+ childIndex);
+ final ViewStructure cstructure = structure.newChild(i);
+ child.dispatchProvideStructure(cstructure);
+ }
+ if (preorderedList != null) {
+ preorderedList.clear();
+ }
}
/**
@@ -3339,21 +3407,44 @@
@Override
public void dispatchProvideAutofillStructure(ViewStructure structure, int flags) {
super.dispatchProvideAutofillStructure(structure, flags);
- dispatchProvideStructureForAssistOrAutoFill(structure, true);
+ if (isAutofillBlocked() || structure.getChildCount() != 0) {
+ return;
+ }
+ final ChildListForAutoFill children = getChildrenForAutofill();
+ final int childrenCount = children.size();
+ structure.setChildCount(childrenCount);
+ for (int i = 0; i < childrenCount; i++) {
+ final View child = children.get(i);
+ final ViewStructure cstructure = structure.newChild(i);
+ child.dispatchProvideAutofillStructure(cstructure, flags);
+ }
+ children.recycle();
}
- /** @hide */
- private ArrayList<View> getChildrenForAutofill() {
- final ArrayList<View> list = new ArrayList<>();
- populateChildrenForAutofill(list);
- return list;
+ /**
+ * Gets the children for autofill. Children for autofill are the first
+ * level descendants that are important for autofill. The returned
+ * child list object is pooled and the caller must recycle it once done.
+ * @hide */
+ private @NonNull ChildListForAutoFill getChildrenForAutofill() {
+ final ChildListForAutoFill children = ChildListForAutoFill.obtain();
+ populateChildrenForAutofill(children);
+ return children;
}
/** @hide */
private void populateChildrenForAutofill(ArrayList<View> list) {
- final int count = mChildrenCount;
- for (int i = 0; i < count; i++) {
- final View child = mChildren[i];
+ final int childrenCount = mChildrenCount;
+ if (childrenCount <= 0) {
+ return;
+ }
+ final ArrayList<View> preorderedList = buildOrderedChildList();
+ final boolean customOrder = preorderedList == null
+ && isChildrenDrawingOrderEnabled();
+ for (int i = 0; i < childrenCount; i++) {
+ final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
+ final View child = (preorderedList == null)
+ ? mChildren[childIndex] : preorderedList.get(childIndex);
if (child.isImportantForAutofill()) {
list.add(child);
} else if (child instanceof ViewGroup) {
@@ -3362,106 +3453,6 @@
}
}
- private void dispatchProvideStructureForAssistOrAutoFill(ViewStructure structure,
- boolean forAutofill) {
- boolean blocked = forAutofill ? isAutofillBlocked() : isAssistBlocked();
- if (blocked || structure.getChildCount() != 0) {
- return;
- }
- final View[] childrenArray;
- final ArrayList<View> childrenList;
- final int childrenCount;
-
- if (forAutofill) {
- childrenArray = null;
- // TODO(b/33197203): the current algorithm allocates a new list for each children that
- // is a view group; ideally, we should use mAttachInfo.mTempArrayList instead, but that
- // would complicated the algorithm a lot...
- childrenList = getChildrenForAutofill();
-
- childrenCount = childrenList.size();
- } else {
- childrenArray = mChildren;
- childrenList = null;
- childrenCount = getChildCount();
- }
-
- if (childrenCount > 0) {
- structure.setChildCount(childrenCount);
- ArrayList<View> preorderedList = buildOrderedChildList();
- boolean customOrder = preorderedList == null
- && isChildrenDrawingOrderEnabled();
- for (int i = 0; i < childrenCount; i++) {
- int childIndex;
- try {
- childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
- } catch (IndexOutOfBoundsException e) {
- childIndex = i;
- if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.M) {
- Log.w(TAG, "Bad getChildDrawingOrder while collecting assist @ "
- + i + " of " + childrenCount, e);
- // At least one app is failing when we call getChildDrawingOrder
- // at this point, so deal semi-gracefully with it by falling back
- // on the basic order.
- customOrder = false;
- if (i > 0) {
- // If we failed at the first index, there really isn't
- // anything to do -- we will just proceed with the simple
- // sequence order.
- // Otherwise, we failed in the middle, so need to come up
- // with an order for the remaining indices and use that.
- // Failed at the first one, easy peasy.
- int[] permutation = new int[childrenCount];
- SparseBooleanArray usedIndices = new SparseBooleanArray();
- // Go back and collected the indices we have done so far.
- for (int j = 0; j < i; j++) {
- permutation[j] = getChildDrawingOrder(childrenCount, j);
- usedIndices.put(permutation[j], true);
- }
- // Fill in the remaining indices with indices that have not
- // yet been used.
- int nextIndex = 0;
- for (int j = i; j < childrenCount; j++) {
- while (usedIndices.get(nextIndex, false)) {
- nextIndex++;
- }
- permutation[j] = nextIndex;
- nextIndex++;
- }
- // Build the final view list.
- preorderedList = new ArrayList<>(childrenCount);
- for (int j = 0; j < childrenCount; j++) {
- final int index = permutation[j];
- final View child = forAutofill
- ? childrenList.get(index)
- : childrenArray[index];
- preorderedList.add(child);
- }
- }
- } else {
- throw e;
- }
- }
-
- final View child = forAutofill
- ? getAndVerifyPreorderedView(preorderedList, childrenList, childIndex)
- : getAndVerifyPreorderedView(preorderedList, childrenArray, childIndex);
- final ViewStructure cstructure = structure.newChild(i);
-
- // Must explicitly check which recursive method to call.
- if (forAutofill) {
- // NOTE: flags are not currently supported, hence 0
- child.dispatchProvideAutofillStructure(cstructure, 0);
- } else {
- child.dispatchProvideStructure(cstructure);
- }
- }
- if (preorderedList != null) {
- preorderedList.clear();
- }
- }
- }
-
private static View getAndVerifyPreorderedView(ArrayList<View> preorderedList, View[] children,
int childIndex) {
final View child;
@@ -3477,21 +3468,6 @@
return child;
}
- private static View getAndVerifyPreorderedView(ArrayList<View> preorderedList,
- ArrayList<View> children, int childIndex) {
- final View child;
- if (preorderedList != null) {
- child = preorderedList.get(childIndex);
- if (child == null) {
- throw new RuntimeException("Invalid preorderedList contained null child at index "
- + childIndex);
- }
- } else {
- child = children.get(childIndex);
- }
- return child;
- }
-
/** @hide */
@Override
public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
@@ -8192,6 +8168,29 @@
}
/**
+ * Pooled class that to hold the children for autifill.
+ */
+ static class ChildListForAutoFill extends ArrayList<View> {
+ private static final int MAX_POOL_SIZE = 32;
+
+ private static final Pools.SimplePool<ChildListForAutoFill> sPool =
+ new Pools.SimplePool<>(MAX_POOL_SIZE);
+
+ public static ChildListForAutoFill obtain() {
+ ChildListForAutoFill list = sPool.acquire();
+ if (list == null) {
+ list = new ChildListForAutoFill();
+ }
+ return list;
+ }
+
+ public void recycle() {
+ clear();
+ sPool.release(this);
+ }
+ }
+
+ /**
* Pooled class that orderes the children of a ViewGroup from start
* to end based on how they are laid out and the layout direction.
*/
@@ -8228,10 +8227,6 @@
return mChildren.get(index);
}
- public int getChildIndex(View child) {
- return mChildren.indexOf(child);
- }
-
private void init(ViewGroup parent, boolean sort) {
ArrayList<View> children = mChildren;
final int childCount = parent.getChildCount();
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index bccaca2..a71b899 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.Nullable;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Bundle;
@@ -323,7 +324,7 @@
* Sets the a hint that helps the autofill service to select the appropriate data to fill the
* view.
*/
- public abstract void setAutofillHint(@View.AutofillHint int hint);
+ public abstract void setAutofillHint(@Nullable String[] hint);
/**
* Sets the {@link AutofillValue} representing the current value of this node.
@@ -346,19 +347,27 @@
public abstract void setInputType(int inputType);
/**
- * Marks this node as sanitized so its content are sent on {@link
+ * Sets whether the data on this node is sensitive; if it is, then its content (text, autofill
+ * value, etc..) is striped before calls to {@link
* android.service.autofill.AutofillService#onFillRequest(android.app.assist.AssistStructure,
* Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback)}.
*
- * <p>Only nodes that does not have PII (Personally Identifiable Information - sensitive data
- * such as email addresses, credit card numbers, passwords, etc...) should be marked
- * as sanitized; a good rule of thumb is to mark as sanitized nodes whose value were statically
- * set from resources.
+ * <p>By default, all nodes are assumed to be sensitive, and only nodes that does not have PII
+ * (Personally Identifiable Information - sensitive data such as email addresses, credit card
+ * numbers, passwords, etc...) should be marked as non-sensitive; a good rule of thumb is to
+ * mark as non-sensitive nodes whose value were statically set from resources.
+ *
+ * <p>Notice that the content of even sensitive nodes are sent to the service (through the
+ * {@link
+ * android.service.autofill.AutofillService#onSaveRequest(android.app.assist.AssistStructure,
+ * Bundle, android.service.autofill.SaveCallback)} call) when the user consented to save the
+ * data, so it is important to set the content of sensitive nodes as well, but mark them as
+ * sensitive.
*
* <p>Should only be set when the node is used for autofill purposes - it will be ignored
* when used for Assist.
*/
- public abstract void setSanitized(boolean sanitized);
+ public abstract void setDataIsSensitive(boolean sensitive);
/**
* Call when done populating a {@link ViewStructure} returned by
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index fe888ec..c9f9f31 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -40,6 +40,8 @@
import android.view.IWindow;
import android.view.View;
+import com.android.internal.util.IntPair;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -109,6 +111,8 @@
boolean mIsEnabled;
+ int mRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK;
+
boolean mIsTouchExplorationEnabled;
boolean mIsHighTextContrastEnabled;
@@ -203,6 +207,11 @@
public void notifyServicesStateChanged() {
mHandler.obtainMessage(MyHandler.MSG_NOTIFY_SERVICES_STATE_CHANGED).sendToTarget();
}
+
+ @Override
+ public void setRelevantEventTypes(int eventTypes) {
+ mRelevantEventTypes = eventTypes;
+ }
};
/**
@@ -362,6 +371,14 @@
return;
}
}
+ if ((event.getEventType() & mRelevantEventTypes) == 0) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Not dispatching irrelevant event: " + event
+ + " that is not among "
+ + AccessibilityEvent.eventTypeToString(mRelevantEventTypes));
+ }
+ return;
+ }
userId = mUserId;
}
try {
@@ -865,8 +882,9 @@
}
try {
- final int stateFlags = service.addClient(mClient, mUserId);
- setStateLocked(stateFlags);
+ final long userStateAndRelevantEvents = service.addClient(mClient, mUserId);
+ setStateLocked(IntPair.first(userStateAndRelevantEvents));
+ mRelevantEventTypes = IntPair.second(userStateAndRelevantEvents);
mService = service;
} catch (RemoteException re) {
Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 157980f..06cb5dc 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -38,7 +38,7 @@
oneway void sendAccessibilityEvent(in AccessibilityEvent uiEvent, int userId);
- int addClient(IAccessibilityManagerClient client, int userId);
+ long addClient(IAccessibilityManagerClient client, int userId);
List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId);
diff --git a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl
index 045ac91..9cc0315 100644
--- a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl
@@ -25,5 +25,8 @@
oneway interface IAccessibilityManagerClient {
void setState(int stateFlags);
+
void notifyServicesStateChanged();
+
+ void setRelevantEventTypes(int eventTypes);
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 2a12e4b..c4f90dc 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -72,8 +72,8 @@
"android.view.autofill.extra.AUTHENTICATION_RESULT";
/** @hide */ public static final int FLAG_START_SESSION = 0x1;
- /** @hide */ public static final int FLAG_FOCUS_GAINED = 0x2;
- /** @hide */ public static final int FLAG_FOCUS_LOST = 0x4;
+ /** @hide */ public static final int FLAG_VIEW_ENTERED = 0x2;
+ /** @hide */ public static final int FLAG_VIEW_EXITED = 0x4;
/** @hide */ public static final int FLAG_VALUE_CHANGED = 0x8;
private final Rect mTempRect = new Rect();
@@ -121,11 +121,11 @@
}
/**
- * Called when an autofill operation on a {@link View} should start.
+ * Called when a {@link View} that supports autofill is entered.
*
- * @param view {@link View} that triggered the autofill request.
+ * @param view {@link View} that was entered.
*/
- public void startAutofillRequest(@NonNull View view) {
+ public void notifyViewEntered(@NonNull View view) {
ensureServiceClientAddedIfNeeded();
if (!mEnabled) {
@@ -142,35 +142,34 @@
startSession(id, view.getWindowToken(), bounds, value);
} else {
// Update focus on existing session.
- updateSession(id, bounds, value, FLAG_FOCUS_GAINED);
+ updateSession(id, bounds, value, FLAG_VIEW_ENTERED);
}
}
/**
- * Called when an autofill operation on a {@link View} should stop.
+ * Called when a {@link View} that supports autofill is exited.
*
- * @param view {@link View} that triggered the autofill request in
- * {@link #startAutofillRequest(View)}.
+ * @param view {@link View} that was exited.
*/
- public void stopAutofillRequest(@NonNull View view) {
+ public void notifyViewExited(@NonNull View view) {
ensureServiceClientAddedIfNeeded();
if (mEnabled && mHasSession) {
final AutofillId id = getAutofillId(view);
// Update focus on existing session.
- updateSession(id, null, null, FLAG_FOCUS_LOST);
+ updateSession(id, null, null, FLAG_VIEW_EXITED);
}
}
/**
- * Called when an autofill operation on a virtual {@link View} should start.
+ * Called when a virtual view that supports autofill is entered.
*
- * @param parent parent of the {@link View} that triggered the autofill request.
- * @param childId id identifying the virtual child inside the parent view.
+ * @param view the {@link View} whose descendant is the virtual view.
+ * @param childId id identifying the virtual child inside the view.
* @param bounds child boundaries, relative to the top window.
*/
- public void startAutofillRequestOnVirtualView(@NonNull View parent, int childId,
+ public void notifyVirtualViewEntered(@NonNull View view, int childId,
@NonNull Rect bounds) {
ensureServiceClientAddedIfNeeded();
@@ -178,32 +177,31 @@
return;
}
- final AutofillId id = getAutofillId(parent, childId);
+ final AutofillId id = getAutofillId(view, childId);
if (!mHasSession) {
// Starts new session.
- startSession(id, parent.getWindowToken(), bounds, null);
+ startSession(id, view.getWindowToken(), bounds, null);
} else {
// Update focus on existing session.
- updateSession(id, bounds, null, FLAG_FOCUS_GAINED);
+ updateSession(id, bounds, null, FLAG_VIEW_ENTERED);
}
}
/**
- * Called when an autofill operation on a virtual {@link View} should stop.
+ * Called when a virtual view that supports autofill is exited.
*
- * @param parent parent of the {@link View} that triggered the autofill request in
- * {@link #startAutofillRequestOnVirtualView(View, int, Rect)}.
- * @param childId id identifying the virtual child inside the parent view.
+ * @param view the {@link View} whose descendant is the virtual view.
+ * @param childId id identifying the virtual child inside the view.
*/
- public void stopAutofillRequestOnVirtualView(@NonNull View parent, int childId) {
+ public void notifyVirtualViewExited(@NonNull View view, int childId) {
ensureServiceClientAddedIfNeeded();
if (mEnabled && mHasSession) {
- final AutofillId id = getAutofillId(parent, childId);
+ final AutofillId id = getAutofillId(view, childId);
// Update focus on existing session.
- updateSession(id, null, null, FLAG_FOCUS_LOST);
+ updateSession(id, null, null, FLAG_VIEW_EXITED);
}
}
@@ -212,7 +210,7 @@
*
* @param view view whose value changed.
*/
- public void valueChanged(View view) {
+ public void notifyValueChanged(View view) {
if (!mEnabled || !mHasSession) {
return;
}
@@ -230,7 +228,7 @@
* @param childId id identifying the virtual child inside the parent view.
* @param value new value of the child.
*/
- public void virtualValueChanged(View parent, int childId, AutofillValue value) {
+ public void notifyVirtualValueChanged(View parent, int childId, AutofillValue value) {
if (!mEnabled || !mHasSession) {
return;
}
@@ -240,12 +238,12 @@
}
/**
- * Called to indicate the current autofill context should be reset.
+ * Called to indicate the current autofill context should be commited.
*
* <p>For example, when a virtual view is rendering an {@code HTML} page with a form, it should
* call this method after the form is submitted and another page is rendered.
*/
- public void reset() {
+ public void commit() {
if (!mEnabled && !mHasSession) {
return;
}
@@ -253,6 +251,20 @@
finishSession();
}
+ /**
+ * Called to indicate the current autofill context should be cancelled.
+ *
+ * <p>For example, when a virtual view is rendering an {@code HTML} page with a form, it should
+ * call this method if the user does not post the form but moves to another form in this page.
+ */
+ public void cancel() {
+ if (!mEnabled && !mHasSession) {
+ return;
+ }
+
+ cancelSession();
+ }
+
private AutofillClient getClient() {
if (mContext instanceof AutofillClient) {
return (AutofillClient) mContext;
@@ -324,9 +336,21 @@
}
}
+ private void cancelSession() {
+ if (DEBUG) {
+ Log.d(TAG, "cancelSession()");
+ }
+ mHasSession = false;
+ try {
+ mService.cancelSession(mContext.getActivityToken(), mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private void updateSession(AutofillId id, Rect bounds, AutofillValue value, int flags) {
if (DEBUG) {
- if (VERBOSE || (flags & FLAG_FOCUS_LOST) != 0) {
+ if (VERBOSE || (flags & FLAG_VIEW_EXITED) != 0) {
Log.d(TAG, "updateSession(): id=" + id + ", bounds=" + bounds + ", value=" + value
+ ", flags=" + flags);
}
diff --git a/core/java/android/view/autofill/AutofillValue.java b/core/java/android/view/autofill/AutofillValue.java
index 0c7620e..9babb59 100644
--- a/core/java/android/view/autofill/AutofillValue.java
+++ b/core/java/android/view/autofill/AutofillValue.java
@@ -16,13 +16,22 @@
package android.view.autofill;
+import static android.view.View.AUTOFILL_TYPE_DATE;
+import static android.view.View.AUTOFILL_TYPE_LIST;
+import static android.view.View.AUTOFILL_TYPE_TEXT;
+import static android.view.View.AUTOFILL_TYPE_TOGGLE;
import static android.view.autofill.Helper.DEBUG;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.View;
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
/**
* Abstracts how a {@link View} can be autofilled by an
* {@link android.service.autofill.AutofillService}.
@@ -31,52 +40,96 @@
* {@link View#getAutofillType()}.
*/
public final class AutofillValue implements Parcelable {
- private final String mText;
- private final int mListIndex;
- private final boolean mToggle;
- private final long mDate;
+ private final @View.AutofillType int mType;
+ private final @NonNull Object mValue;
- private AutofillValue(CharSequence text, int listIndex, boolean toggle, long date) {
- mText = (text == null) ? null : text.toString();
- mListIndex = listIndex;
- mToggle = toggle;
- mDate = date;
+ private AutofillValue(@View.AutofillType int type, @NonNull Object value) {
+ mType = type;
+ mValue = value;
}
/**
* Gets the value to autofill a text field.
*
- * <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info.
+ * <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info.</p>
+ *
+ * @throws IllegalStateException if the value is not a text value
*/
- public CharSequence getTextValue() {
- return mText;
+ @NonNull public CharSequence getTextValue() {
+ Preconditions.checkState(isText(), "value must be a text value, not type=" + mType);
+ return (CharSequence) mValue;
+ }
+
+ /**
+ * Checks is this is a text value.
+ *
+ * <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info.</p>
+ */
+ public boolean isText() {
+ return mType == AUTOFILL_TYPE_TEXT;
}
/**
* Gets the value to autofill a toggable field.
*
- * <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info.
+ * <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info.</p>
+ *
+ * @throws IllegalStateException if the value is not a toggle value
*/
public boolean getToggleValue() {
- return mToggle;
+ Preconditions.checkState(isToggle(), "value must be a toggle value, not type=" + mType);
+ return (Boolean) mValue;
+ }
+
+ /**
+ * Checks is this is a toggle value.
+ *
+ * <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info.</p>
+ */
+ public boolean isToggle() {
+ return mType == AUTOFILL_TYPE_TOGGLE;
}
/**
* Gets the value to autofill a selection list field.
*
- * <p>See {@link View#AUTOFILL_TYPE_LIST} for more info.
+ * <p>See {@link View#AUTOFILL_TYPE_LIST} for more info.</p>
+ *
+ * @throws IllegalStateException if the value is not a list value
*/
public int getListValue() {
- return mListIndex;
+ Preconditions.checkState(isList(), "value must be a list value, not type=" + mType);
+ return (Integer) mValue;
+ }
+
+ /**
+ * Checks is this is a list value.
+ *
+ * <p>See {@link View#AUTOFILL_TYPE_LIST} for more info.</p>
+ */
+ public boolean isList() {
+ return mType == AUTOFILL_TYPE_LIST;
}
/**
* Gets the value to autofill a date field.
*
- * <p>See {@link View#AUTOFILL_TYPE_DATE} for more info.
+ * <p>See {@link View#AUTOFILL_TYPE_DATE} for more info.</p>
+ *
+ * @throws IllegalStateException if the value is not a date value
*/
public long getDateValue() {
- return mDate;
+ Preconditions.checkState(isDate(), "value must be a date value, not type=" + mType);
+ return (Long) mValue;
+ }
+
+ /**
+ * Checks is this is a date value.
+ *
+ * <p>See {@link View#AUTOFILL_TYPE_DATE} for more info.</p>
+ */
+ public boolean isDate() {
+ return mType == AUTOFILL_TYPE_DATE;
}
/////////////////////////////////////
@@ -85,13 +138,7 @@
@Override
public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((mText == null) ? 0 : mText.hashCode());
- result = prime * result + mListIndex;
- result = prime * result + (mToggle ? 1231 : 1237);
- result = prime * result + (int) (mDate ^ (mDate >>> 32));
- return result;
+ return mType + mValue.hashCode();
}
@Override
@@ -100,32 +147,31 @@
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
final AutofillValue other = (AutofillValue) obj;
- if (mText == null) {
- if (other.mText != null) return false;
+
+ if (mType != other.mType) return false;
+
+ if (isText()) {
+ return mValue.toString().equals(other.mValue.toString());
} else {
- if (!mText.equals(other.mText)) return false;
+ return Objects.equals(mValue, other.mValue);
}
- if (mListIndex != other.mListIndex) return false;
- if (mToggle != other.mToggle) return false;
- if (mDate != other.mDate) return false;
- return true;
}
/** @hide */
public String coerceToString() {
// TODO(b/33197203): How can we filter on toggles or list values?
- return mText;
+ return mValue.toString();
}
@Override
public String toString() {
if (!DEBUG) return super.toString();
- if (mText != null) {
- return mText.length() + "_chars";
+ if (isText()) {
+ return ((CharSequence) mValue).length() + "_chars";
}
- return "[l=" + mListIndex + ", t=" + mToggle + ", d=" + mDate + "]";
+ return "[type=" + mType + ", value=" + mValue + "]";
}
/////////////////////////////////////
@@ -139,17 +185,44 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeString(mText);
- parcel.writeInt(mListIndex);
- parcel.writeInt(mToggle ? 1 : 0);
- parcel.writeLong(mDate);
+ parcel.writeInt(mType);
+
+ switch (mType) {
+ case AUTOFILL_TYPE_TEXT:
+ parcel.writeCharSequence((CharSequence) mValue);
+ break;
+ case AUTOFILL_TYPE_TOGGLE:
+ parcel.writeInt((Boolean) mValue ? 1 : 0);
+ break;
+ case AUTOFILL_TYPE_LIST:
+ parcel.writeInt((Integer) mValue);
+ break;
+ case AUTOFILL_TYPE_DATE:
+ parcel.writeLong((Long) mValue);
+ break;
+ }
}
- private AutofillValue(Parcel parcel) {
- mText = parcel.readString();
- mListIndex = parcel.readInt();
- mToggle = parcel.readInt() == 1;
- mDate = parcel.readLong();
+ private AutofillValue(@NonNull Parcel parcel) {
+ mType = parcel.readInt();
+
+ switch (mType) {
+ case AUTOFILL_TYPE_TEXT:
+ mValue = parcel.readCharSequence();
+ break;
+ case AUTOFILL_TYPE_TOGGLE:
+ int rawValue = parcel.readInt();
+ mValue = rawValue != 0;
+ break;
+ case AUTOFILL_TYPE_LIST:
+ mValue = parcel.readInt();
+ break;
+ case AUTOFILL_TYPE_DATE:
+ mValue = parcel.readLong();
+ break;
+ default:
+ throw new IllegalArgumentException("type=" + mType + " not valid");
+ }
}
public static final Parcelable.Creator<AutofillValue> CREATOR =
@@ -175,9 +248,8 @@
* <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info.
*/
// TODO(b/33197203): use cache
- @Nullable
public static AutofillValue forText(@Nullable CharSequence value) {
- return value == null ? null : new AutofillValue(value, 0, false, 0);
+ return value == null ? null : new AutofillValue(AUTOFILL_TYPE_TEXT, value);
}
/**
@@ -187,7 +259,7 @@
* <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info.
*/
public static AutofillValue forToggle(boolean value) {
- return new AutofillValue(null, 0, value, 0);
+ return new AutofillValue(AUTOFILL_TYPE_TOGGLE, value);
}
/**
@@ -197,7 +269,7 @@
* <p>See {@link View#AUTOFILL_TYPE_LIST} for more info.
*/
public static AutofillValue forList(int value) {
- return new AutofillValue(null, value, false, 0);
+ return new AutofillValue(AUTOFILL_TYPE_LIST, value);
}
/**
@@ -206,6 +278,6 @@
* <p>See {@link View#AUTOFILL_TYPE_DATE} for more info.
*/
public static AutofillValue forDate(long value) {
- return new AutofillValue(null, 0, false, value);
+ return new AutofillValue(AUTOFILL_TYPE_DATE, value);
}
}
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 07d8cab..86a4965 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -36,6 +36,7 @@
oneway void updateSession(in IBinder activityToken, in AutofillId id, in Rect bounds,
in AutofillValue value, int flags, int userId);
oneway void finishSession(in IBinder activityToken, int userId);
+ oneway void cancelSession(in IBinder activityToken, int userId);
oneway void setAuthenticationResult(in Bundle data,
in IBinder activityToken, int userId);
oneway void setHasCallback(in IBinder activityToken, int userId, boolean hasIt);
diff --git a/core/java/android/view/textclassifier/LangId.java b/core/java/android/view/textclassifier/LangId.java
index ada3c37c..23c7842 100644
--- a/core/java/android/view/textclassifier/LangId.java
+++ b/core/java/android/view/textclassifier/LangId.java
@@ -22,7 +22,7 @@
final class LangId {
static {
- System.loadLibrary("smart-selection_jni");
+ System.loadLibrary("textclassifier");
}
private final long mModelPtr;
diff --git a/core/java/android/view/textclassifier/SmartSelection.java b/core/java/android/view/textclassifier/SmartSelection.java
index c48cd06..9397a41 100644
--- a/core/java/android/view/textclassifier/SmartSelection.java
+++ b/core/java/android/view/textclassifier/SmartSelection.java
@@ -23,7 +23,7 @@
final class SmartSelection {
static {
- System.loadLibrary("smart-selection_jni");
+ System.loadLibrary("textclassifier");
}
private final long mCtx;
@@ -92,4 +92,3 @@
}
}
}
-
diff --git a/core/java/android/widget/AbsSpinner.java b/core/java/android/widget/AbsSpinner.java
index 053574f..020e80a 100644
--- a/core/java/android/widget/AbsSpinner.java
+++ b/core/java/android/widget/AbsSpinner.java
@@ -23,6 +23,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
@@ -38,6 +39,8 @@
* @attr ref android.R.styleable#AbsSpinner_entries
*/
public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> {
+ private static final String LOG_TAG = AbsSpinner.class.getSimpleName();
+
SpinnerAdapter mAdapter;
int mHeightMeasureSpec;
@@ -514,8 +517,11 @@
public void autofill(AutofillValue value) {
if (!isEnabled()) return;
- final int position = value.getListValue();
- setSelection(position);
+ if (value.isList()) {
+ setSelection(value.getListValue());
+ } else {
+ Log.w(LOG_TAG, value + " could not be autofilled into " + this);
+ }
}
@Override
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index e5505a6..5725b49 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -918,7 +918,7 @@
// Always notify AutoFillManager - it will return right away if autofill is disabled.
final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
if (afm != null) {
- afm.valueChanged(this);
+ afm.notifyValueChanged(this);
}
}
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 81aec9c..899a824 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -28,6 +28,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.Gravity;
import android.view.SoundEffectConstants;
import android.view.ViewDebug;
@@ -55,6 +56,7 @@
* </p>
*/
public abstract class CompoundButton extends Button implements Checkable {
+ private static final String LOG_TAG = CompoundButton.class.getSimpleName();
private boolean mChecked;
private boolean mBroadcasting;
@@ -173,7 +175,7 @@
}
final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
if (afm != null) {
- afm.valueChanged(this);
+ afm.notifyValueChanged(this);
}
mBroadcasting = false;
@@ -578,14 +580,18 @@
public void onProvideAutofillStructure(ViewStructure structure, int flags) {
super.onProvideAutofillStructure(structure, flags);
- structure.setSanitized(mCheckedFromResource);
+ structure.setDataIsSensitive(!mCheckedFromResource);
}
@Override
public void autofill(AutofillValue value) {
if (!isEnabled()) return;
- setChecked(value.getToggleValue());
+ if (value.isToggle()) {
+ setChecked(value.getToggleValue());
+ } else {
+ Log.w(LOG_TAG, value + " could not be autofilled into " + this);
+ }
}
@Override
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index fa8316c..f63573f 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -29,6 +29,7 @@
import android.os.Parcelable;
import android.text.format.DateUtils;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewStructure;
@@ -83,6 +84,8 @@
*/
@Widget
public class DatePicker extends FrameLayout {
+ private static final String LOG_TAG = DatePicker.class.getSimpleName();
+
/**
* Presentation mode for the Holo-style date picker that uses a set of
* {@link android.widget.NumberPicker}s.
@@ -182,7 +185,7 @@
mDelegate.setAutoFillChangeListener((v, y, m, d) -> {
final AutofillManager afm = context.getSystemService(AutofillManager.class);
if (afm != null) {
- afm.valueChanged(this);
+ afm.notifyValueChanged(this);
}
});
}
@@ -775,7 +778,11 @@
public void autofill(AutofillValue value) {
if (!isEnabled()) return;
- mDelegate.updateDate(value.getDateValue());
+ if (value.isDate()) {
+ mDelegate.updateDate(value.getDateValue());
+ } else {
+ Log.w(LOG_TAG, value + " could not be autofilled into " + this);
+ }
}
@Override
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index bd62d6c..5e8279a 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -55,6 +55,7 @@
*
*/
public class RadioGroup extends LinearLayout {
+ private static final String LOG_TAG = RadioGroup.class.getSimpleName();
// holds the checked id; the selection is empty by default
private int mCheckedId = -1;
@@ -188,7 +189,7 @@
}
final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
if (afm != null) {
- afm.valueChanged(this);
+ afm.notifyValueChanged(this);
}
}
@@ -421,14 +422,21 @@
@Override
public void onProvideAutofillStructure(ViewStructure structure, int flags) {
super.onProvideAutofillStructure(structure, flags);
- structure.setSanitized(mCheckedId == mInitialCheckedId);
+ structure.setDataIsSensitive(mCheckedId != mInitialCheckedId);
}
@Override
public void autofill(AutofillValue value) {
if (!isEnabled()) return;
- final int index = value.getListValue();
+ int index;
+ if (value.isList()) {
+ index = value.getListValue();
+ } else {
+ Log.w(LOG_TAG, value + " could not be autofilled into " + this);
+ return;
+ }
+
final View child = getChildAt(index);
if (child == null) {
Log.w(VIEW_LOG_TAG, "RadioGroup.autoFill(): no child with index " + index);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index ee70acc..d04f70f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9136,7 +9136,7 @@
if (DEBUG_AUTOFILL) {
Log.v(LOG_TAG, "sendAfterTextChanged(): notify AFM for text=" + mText);
}
- afm.valueChanged(TextView.this);
+ afm.notifyValueChanged(TextView.this);
}
}
@@ -9900,7 +9900,7 @@
final boolean isPassword = hasPasswordTransformationMethod()
|| isPasswordInputType(getInputType());
if (forAutofill) {
- structure.setSanitized(mTextFromResource);
+ structure.setDataIsSensitive(!mTextFromResource);
}
if (!isPassword || forAutofill) {
@@ -10014,10 +10014,12 @@
@Override
public void autofill(AutofillValue value) {
- final CharSequence text = value.getTextValue();
-
- if (text != null && isTextEditable()) {
- setText(text, mBufferType, true, 0);
+ if (value.isText()) {
+ if (isTextEditable()) {
+ setText(value.getTextValue(), mBufferType, true, 0);
+ }
+ } else {
+ Log.w(LOG_TAG, value + " could not be autofilled into " + this);
}
}
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 1435983..cfa78b5 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -27,6 +27,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.MathUtils;
import android.view.View;
import android.view.ViewStructure;
@@ -53,6 +54,8 @@
*/
@Widget
public class TimePicker extends FrameLayout {
+ private static final String LOG_TAG = TimePicker.class.getSimpleName();
+
/**
* Presentation mode for the Holo-style time picker that uses a set of
* {@link android.widget.NumberPicker}s.
@@ -144,7 +147,7 @@
mDelegate.setAutoFillChangeListener((v, h, m) -> {
final AutofillManager afm = context.getSystemService(AutofillManager.class);
if (afm != null) {
- afm.valueChanged(this);
+ afm.notifyValueChanged(this);
}
});
}
@@ -530,7 +533,11 @@
public void autofill(AutofillValue value) {
if (!isEnabled()) return;
- mDelegate.setDate(value.getDateValue());
+ if (value.isDate()) {
+ mDelegate.setDate(value.getDateValue());
+ } else {
+ Log.w(LOG_TAG, value + " could not be autofilled into " + this);
+ }
}
@Override
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 2aeddb3..6aa7766 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -6027,7 +6027,8 @@
* Clear all stats for this uid. Returns true if the uid is completely
* inactive so can be dropped.
*/
- boolean reset() {
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public boolean reset() {
boolean active = false;
if (mWifiRunningTimer != null) {
@@ -6968,7 +6969,10 @@
boolean reset() {
if (mBgCounter != null) {
- mBgCounter.reset(true);
+ mBgCounter.reset(true /*detachIfReset*/);
+ // If we detach, we must null the mBgCounter reference so that it
+ // can be recreated and attached.
+ mBgCounter = null;
}
if (mTimer.reset(true)) {
mTimer = null;
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index a7e900a..0516209 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -132,9 +132,6 @@
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL");
preloadOpenGL();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
- Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL");
- preloadOpenGL();
- Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
preloadSharedLibraries();
preloadTextResources();
// Ask the WebViewFactory to do any initialization that must run in the zygote process,
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 804bd29..6c9280a 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -3585,7 +3585,8 @@
synchronized (mWindows) {
if (!mIsWatching) {
try {
- WindowManagerHolder.sWindowManager.watchRotation(this);
+ WindowManagerHolder.sWindowManager.watchRotation(this,
+ phoneWindow.getContext().getDisplay().getDisplayId());
mHandler = new Handler();
mIsWatching = true;
} catch (RemoteException ex) {
diff --git a/core/java/com/android/internal/util/IntPair.java b/core/java/com/android/internal/util/IntPair.java
new file mode 100644
index 0000000..7992507
--- /dev/null
+++ b/core/java/com/android/internal/util/IntPair.java
@@ -0,0 +1,38 @@
+/*
+ * 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.internal.util;
+
+/**
+ * Utilities for treating a {@code long} as a pair of {@code int}s
+ *
+ * @hide
+ */
+public class IntPair {
+ private IntPair() {}
+
+ public static long of(int first, int second) {
+ return (((long)first) << 32) | ((long)second & 0xffffffffL);
+ }
+
+ public static int first(long intPair) {
+ return (int)(intPair >> 32);
+ }
+
+ public static int second(long intPair) {
+ return (int)intPair;
+ }
+}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 95b2593..af5fca2 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -286,6 +286,7 @@
libhwbinder \
libvintf \
libnativewindow \
+ libtextclassifier \
LOCAL_SHARED_LIBRARIES += \
libhwui \
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index c1bb69d..e64a574 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -417,11 +417,7 @@
// For wide gamut images, we will leave the color space on the SkBitmap. Otherwise,
// use the default.
SkImageInfo bitmapInfo = decodeInfo;
- sk_sp<SkColorSpace> srgb =
- SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
- SkColorSpace::kSRGB_Gamut,
- SkColorSpace::kNonLinearBlending_ColorSpaceFlag);
- if (decodeInfo.colorSpace() == srgb.get()) {
+ if (decodeInfo.colorSpace() && decodeInfo.colorSpace()->isSRGB()) {
bitmapInfo = bitmapInfo.makeColorSpace(GraphicsJNI::colorSpaceForType(decodeColorType));
}
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 7c56c7b..e66587a 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -461,12 +461,7 @@
}
bool GraphicsJNI::isColorSpaceSRGB(SkColorSpace* colorSpace) {
- return colorSpace == nullptr
- || colorSpace == SkColorSpace::MakeSRGB().get()
- || colorSpace == SkColorSpace::MakeRGB(
- SkColorSpace::kSRGB_RenderTargetGamma,
- SkColorSpace::kSRGB_Gamut,
- SkColorSpace::kNonLinearBlending_ColorSpaceFlag).get();
+ return colorSpace == nullptr || colorSpace->isSRGB();
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index c261e41..253daaa 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -178,10 +178,10 @@
// from one to the other (though SkClipOp is destined to become a strict subset)
static_assert(SkRegion::kDifference_Op == static_cast<SkRegion::Op>(SkClipOp::kDifference), "");
static_assert(SkRegion::kIntersect_Op == static_cast<SkRegion::Op>(SkClipOp::kIntersect), "");
-static_assert(SkRegion::kUnion_Op == static_cast<SkRegion::Op>(SkClipOp::kUnion), "");
-static_assert(SkRegion::kXOR_Op == static_cast<SkRegion::Op>(SkClipOp::kXOR), "");
-static_assert(SkRegion::kReverseDifference_Op == static_cast<SkRegion::Op>(SkClipOp::kReverseDifference), "");
-static_assert(SkRegion::kReplace_Op == static_cast<SkRegion::Op>(SkClipOp::kReplace), "");
+static_assert(SkRegion::kUnion_Op == static_cast<SkRegion::Op>(SkClipOp::kUnion_deprecated), "");
+static_assert(SkRegion::kXOR_Op == static_cast<SkRegion::Op>(SkClipOp::kXOR_deprecated), "");
+static_assert(SkRegion::kReverseDifference_Op == static_cast<SkRegion::Op>(SkClipOp::kReverseDifference_deprecated), "");
+static_assert(SkRegion::kReplace_Op == static_cast<SkRegion::Op>(SkClipOp::kReplace_deprecated), "");
static SkClipOp opHandleToClipOp(jint opHandle) {
// The opHandle is defined in Canvas.java to be Region::Op
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 7eb0582..98c9e78 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -97,7 +97,7 @@
SettingProto download_max_bytes_over_mobile = 52;
SettingProto download_recommended_max_bytes_over_mobile = 53;
SettingProto hdmi_control_enabled = 54;
- SettingProto hdmi_system_audio_enabled = 55;
+ SettingProto hdmi_system_audio_control_enabled = 55;
SettingProto hdmi_control_auto_wakeup_enabled = 56;
SettingProto hdmi_control_auto_device_off_enabled = 57;
SettingProto mhl_input_switching_enabled = 58;
diff --git a/core/res/res/layout/autofill_dataset_picker.xml b/core/res/res/layout/autofill_dataset_picker.xml
index 9b90de6..133265b 100644
--- a/core/res/res/layout/autofill_dataset_picker.xml
+++ b/core/res/res/layout/autofill_dataset_picker.xml
@@ -18,6 +18,7 @@
android:id="@+id/autofill_dataset_picker"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
- android:divider="?android:attr/listDivider"
- android:background="#ffffffff">
+ android:divider="@null"
+ android:background="#ffffffff"
+ android:elevation="@dimen/floating_window_z">
</ListView>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 21c8780..c547ae5 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2307,37 +2307,9 @@
</attr>
<!-- Describes the content of a view so that a autofill service can fill in the appropriate
- data. Multiple flags can be combined to mean e.g. emailAddress or postalAddress. -->
- <attr name="autofillHint">
- <!-- No hint. -->
- <flag name="none" value="0" />
- <!-- The view contains an email address. -->
- <flag name="emailAddress" value="0x1" />
- <!-- The view contains a real name. -->
- <flag name="name" value="0x2" />
- <!-- The view contains a user name. -->
- <flag name="username" value="0x4" />
- <!-- The view contains a password. -->
- <flag name="password" value="0x8" />
- <!-- The view contains a phone number. -->
- <flag name="phone" value="0x10" />
- <!-- The view contains a postal address. -->
- <flag name="postalAddress" value="0x20" />
- <!-- The view contains a postal code. -->
- <flag name="postalCode" value="0x40" />
- <!-- The view contains a credit card number. -->
- <flag name="creditCardNumber" value="0x80" />
- <!-- The view contains a credit card security code -->
- <flag name="creditCardSecurityCode" value="0x100" />
- <!-- The view contains a credit card expiration date -->
- <flag name="creditCardExpirationDate" value="0x200" />
- <!-- The view contains the month a credit card expires -->
- <flag name="creditCardExpirationMonth" value="0x400" />
- <!-- The view contains the year a credit card expires -->
- <flag name="creditCardExpirationYear" value="0x800" />
- <!-- The view contains the day a credit card expires -->
- <flag name="creditCardExpirationDay" value="0x1000" />
- </attr>
+ data. Multiple hints can be combined in a comma separated list or an array of strings
+ to mean e.g. emailAddress or postalAddress. -->
+ <attr name="autofillHint" format="string|reference" />
<!-- Hints the Android System whether the view node associated with this View should be
included in a view structure used for autofill purposes. -->
@@ -7213,7 +7185,7 @@
<!-- The key of another Preference that this Preference will depend on. If the other
Preference is not set or is off, this Preference will be disabled. -->
<attr name="dependency" format="string" />
- <!-- Whether the Preference stores its value to the shared preferences. -->
+ <!-- Whether the Preference stores its value to the storage. -->
<attr name="persistent" />
<!-- The default value for the preference, which will be set either if persistence
is off or persistence is on and the preference is not found in the persistent
@@ -7222,6 +7194,9 @@
<!-- Whether the view of this Preference should be disabled when
this Preference is disabled. -->
<attr name="shouldDisableView" format="boolean" />
+ <!-- Whether the preference has enabled to have its view recycled when used in the list
+ view. This is true by default. -->
+ <attr name="recycleEnabled" format="boolean" />
</declare-styleable>
<!-- Base attributes available to CheckBoxPreference. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 359fbcb..bb3f1c3 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2805,6 +2805,7 @@
<public name="autofillHint" />
<public name="fontProviderPackage" />
<public name="importantForAutofill" />
+ <public name="recycleEnabled"/>
</public-group>
<public-group type="style" first-id="0x010302e0">
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 903ef84..0cfdaf5 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -198,7 +198,7 @@
Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
Settings.Global.HDMI_CONTROL_ENABLED,
- Settings.Global.HDMI_SYSTEM_AUDIO_ENABLED,
+ Settings.Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
Settings.Global.HTTP_PROXY,
Settings.Global.INET_CONDITION_DEBOUNCE_DOWN_DELAY,
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
index e152163..4ec78ff 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
@@ -65,4 +65,49 @@
assertEquals(1, sensorBgCounter.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
}
+
+ @SmallTest
+ public void testNestedSensorReset() throws Exception {
+ final MockClocks clocks = new MockClocks();
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+ bi.mForceOnBattery = true;
+ clocks.realtime = 100;
+ clocks.uptime = 100;
+ bi.getOnBatteryTimeBase().setRunning(true, 100, 100);
+ bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_RECEIVER);
+
+ clocks.realtime += 100;
+ clocks.uptime += 100;
+
+ bi.noteStartSensorLocked(UID, SENSOR_ID);
+
+ clocks.realtime += 100;
+ clocks.uptime += 100;
+
+ // The sensor is started and the background counter has been created.
+ final BatteryStats.Uid uid = bi.getUidStats().get(UID);
+ assertNotNull(uid);
+
+ BatteryStats.Uid.Sensor sensor = uid.getSensorStats().get(SENSOR_ID);
+ assertNotNull(sensor);
+ assertNotNull(sensor.getSensorTime());
+ assertNotNull(sensor.getSensorBgCount());
+
+ // Reset the stats. Since the sensor is still running, we should still see the sensor
+ // timer. Background counter should be gone though.
+ bi.getUidStatsLocked(UID).reset();
+
+ sensor = uid.getSensorStats().get(SENSOR_ID);
+ assertNotNull(sensor);
+ assertNotNull(sensor.getSensorTime());
+ assertNull(sensor.getSensorBgCount());
+
+ bi.noteStopSensorLocked(UID, SENSOR_ID);
+
+ // Now the sensor timer has stopped so this reset should also take out the sensor.
+ bi.getUidStatsLocked(UID).reset();
+
+ sensor = uid.getSensorStats().get(SENSOR_ID);
+ assertNull(sensor);
+ }
}
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 76598a0..38a1a46 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -248,6 +248,9 @@
<font weight="400" style="normal">NotoSansCham-Regular.ttf</font>
<font weight="700" style="normal">NotoSansCham-Bold.ttf</font>
</family>
+ <family lang="und-Avst">
+ <font weight="400" style="normal">NotoSansAvestan-Regular.ttf</font>
+ </family>
<family lang="und-Bali">
<font weight="400" style="normal">NotoSansBalinese-Regular.ttf</font>
</family>
@@ -257,6 +260,9 @@
<family lang="und-Batk">
<font weight="400" style="normal">NotoSansBatak-Regular.ttf</font>
</family>
+ <family lang="und-Brah">
+ <font weight="400" style="normal">NotoSansBrahmi-Regular.ttf</font>
+ </family>
<family lang="und-Bugi">
<font weight="400" style="normal">NotoSansBuginese-Regular.ttf</font>
</family>
@@ -266,33 +272,75 @@
<family lang="und-Cans">
<font weight="400" style="normal">NotoSansCanadianAboriginal-Regular.ttf</font>
</family>
+ <family lang="und-Cari">
+ <font weight="400" style="normal">NotoSansCarian-Regular.ttf</font>
+ </family>
<family lang="und-Cher">
<font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
</family>
<family lang="und-Copt">
<font weight="400" style="normal">NotoSansCoptic-Regular.ttf</font>
</family>
+ <family lang="und-Xsux">
+ <font weight="400" style="normal">NotoSansCuneiform-Regular.ttf</font>
+ </family>
+ <family lang="und-Cprt">
+ <font weight="400" style="normal">NotoSansCypriot-Regular.ttf</font>
+ </family>
+ <family lang="und-Dsrt">
+ <font weight="400" style="normal">NotoSansDeseret-Regular.ttf</font>
+ </family>
+ <family lang="und-Egyp">
+ <font weight="400" style="normal">NotoSansEgyptianHieroglyphs-Regular.ttf</font>
+ </family>
<family lang="und-Glag">
<font weight="400" style="normal">NotoSansGlagolitic-Regular.ttf</font>
</family>
+ <family lang="und-Goth">
+ <font weight="400" style="normal">NotoSansGothic-Regular.ttf</font>
+ </family>
<family lang="und-Hano">
<font weight="400" style="normal">NotoSansHanunoo-Regular.ttf</font>
</family>
+ <family lang="und-Armi">
+ <font weight="400" style="normal">NotoSansImperialAramaic-Regular.ttf</font>
+ </family>
+ <family lang="und-Phli">
+ <font weight="400" style="normal">NotoSansInscriptionalPahlavi-Regular.ttf</font>
+ </family>
+ <family lang="und-Prti">
+ <font weight="400" style="normal">NotoSansInscriptionalParthian-Regular.ttf</font>
+ </family>
<family lang="und-Java">
<font weight="400" style="normal">NotoSansJavanese-Regular.ttf</font>
</family>
+ <family lang="und-Kthi">
+ <font weight="400" style="normal">NotoSansKaithi-Regular.ttf</font>
+ </family>
<family lang="und-Kali">
<font weight="400" style="normal">NotoSansKayahLi-Regular.ttf</font>
</family>
+ <family lang="und-Khar">
+ <font weight="400" style="normal">NotoSansKharoshthi-Regular.ttf</font>
+ </family>
<family lang="und-Lepc">
<font weight="400" style="normal">NotoSansLepcha-Regular.ttf</font>
</family>
<family lang="und-Limb">
<font weight="400" style="normal">NotoSansLimbu-Regular.ttf</font>
</family>
+ <family lang="und-Linb">
+ <font weight="400" style="normal">NotoSansLinearB-Regular.ttf</font>
+ </family>
<family lang="und-Lisu">
<font weight="400" style="normal">NotoSansLisu-Regular.ttf</font>
</family>
+ <family lang="und-Lyci">
+ <font weight="400" style="normal">NotoSansLycian-Regular.ttf</font>
+ </family>
+ <family lang="und-Lydi">
+ <font weight="400" style="normal">NotoSansLydian-Regular.ttf</font>
+ </family>
<family lang="und-Mand">
<font weight="400" style="normal">NotoSansMandaic-Regular.ttf</font>
</family>
@@ -305,12 +353,33 @@
<family lang="und-Nkoo">
<font weight="400" style="normal">NotoSansNKo-Regular.ttf</font>
</family>
+ <family lang="und-Ogam">
+ <font weight="400" style="normal">NotoSansOgham-Regular.ttf</font>
+ </family>
<family lang="und-Olck">
<font weight="400" style="normal">NotoSansOlChiki-Regular.ttf</font>
</family>
+ <family lang="und-Ital">
+ <font weight="400" style="normal">NotoSansOldItalic-Regular.ttf</font>
+ </family>
+ <family lang="und-Xpeo">
+ <font weight="400" style="normal">NotoSansOldPersian-Regular.ttf</font>
+ </family>
+ <family lang="und-Sarb">
+ <font weight="400" style="normal">NotoSansOldSouthArabian-Regular.ttf</font>
+ </family>
+ <family lang="und-Orkh">
+ <font weight="400" style="normal">NotoSansOldTurkic-Regular.ttf</font>
+ </family>
+ <family lang="und-Osma">
+ <font weight="400" style="normal">NotoSansOsmanya-Regular.ttf</font>
+ </family>
<family lang="und-Phag">
<font weight="400" style="normal">NotoSansPhagsPa-Regular.ttf</font>
</family>
+ <family lang="und-Phnx">
+ <font weight="400" style="normal">NotoSansPhoenician-Regular.ttf</font>
+ </family>
<family lang="und-Rjng">
<font weight="400" style="normal">NotoSansRejang-Regular.ttf</font>
</family>
@@ -323,6 +392,9 @@
<family lang="und-Saur">
<font weight="400" style="normal">NotoSansSaurashtra-Regular.ttf</font>
</family>
+ <family lang="und-Shaw">
+ <font weight="400" style="normal">NotoSansShavian-Regular.ttf</font>
+ </family>
<family lang="und-Sund">
<font weight="400" style="normal">NotoSansSundanese-Regular.ttf</font>
</family>
@@ -358,6 +430,9 @@
<family lang="und-Tfng">
<font weight="400" style="normal">NotoSansTifinagh-Regular.ttf</font>
</family>
+ <family lang="und-Ugar">
+ <font weight="400" style="normal">NotoSansUgaritic-Regular.ttf</font>
+ </family>
<family lang="und-Vaii">
<font weight="400" style="normal">NotoSansVai-Regular.ttf</font>
</family>
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 908ec50..929ac22 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -490,16 +490,16 @@
* <tr>
* <td>Opto-electronic transfer function (OETF)</td>
* <td colspan="4">\(\begin{equation}
- * C_{sRGB} = \begin{cases} 12.92 \times C_{linear} & C_{linear} \lt 0.0031308 \\
- * 1.055 \times C_{linear}^{\frac{1}{2.4}} - 0.055 & C_{linear} \ge 0.0031308 \end{cases}
+ * C_{DisplayP3} = \begin{cases} 12.92 \times C_{linear} & C_{linear} \lt 0.0030186 \\
+ * 1.055 \times C_{linear}^{\frac{1}{2.4}} - 0.055 & C_{linear} \ge 0.0030186 \end{cases}
* \end{equation}\)
* </td>
* </tr>
* <tr>
* <td>Electro-optical transfer function (EOTF)</td>
* <td colspan="4">\(\begin{equation}
- * C_{linear} = \begin{cases}\frac{C_{sRGB}}{12.92} & C_{sRGB} \lt 0.04045 \\
- * \left( \frac{C_{sRGB} + 0.055}{1.055} \right) ^{2.4} & C_{sRGB} \ge 0.04045 \end{cases}
+ * C_{linear} = \begin{cases}\frac{C_{DisplayP3}}{12.92} & C_{sRGB} \lt 0.039 \\
+ * \left( \frac{C_{DisplayP3} + 0.055}{1.055} \right) ^{2.4} & C_{sRGB} \ge 0.039 \end{cases}
* \end{equation}\)
* </td>
* </tr>
@@ -1482,7 +1482,7 @@
"Display P3",
new float[] { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f },
ILLUMINANT_D65,
- new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4),
+ new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.039, 2.4),
Named.DISPLAY_P3.ordinal()
);
sNamedColorSpaces[Named.NTSC_1953.ordinal()] = new ColorSpace.Rgb(
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index a91b410..b584e0d 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -38,6 +38,9 @@
*/
private long mNativeInstance;
+ /**
+ * Current matrix - always set to null if local matrix is identity.
+ */
private Matrix mLocalMatrix;
public enum TileMode {
@@ -70,7 +73,7 @@
public boolean getLocalMatrix(@NonNull Matrix localM) {
if (mLocalMatrix != null) {
localM.set(mLocalMatrix);
- return true;
+ return true; // presence of mLocalMatrix means it's not identity
}
return false;
}
diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java
index a24b970..6758607 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -545,7 +545,7 @@
mChangingConfigurations = orig.mChangingConfigurations;
mPaint = new Paint(orig.mPaint);
mThemeAttrs = orig.mThemeAttrs;
- if (mShape != null) {
+ if (orig.mShape != null) {
try {
mShape = orig.mShape.clone();
} catch (CloneNotSupportedException e) {
diff --git a/libs/hwui/tests/unit/CanvasStateTests.cpp b/libs/hwui/tests/unit/CanvasStateTests.cpp
index 43974f6..c41313a 100644
--- a/libs/hwui/tests/unit/CanvasStateTests.cpp
+++ b/libs/hwui/tests/unit/CanvasStateTests.cpp
@@ -74,7 +74,7 @@
state.clipRect(10, 10, 200, 200, SkClipOp::kIntersect);
ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10, 100, 100));
- state.clipRect(50, 50, 150, 150, SkClipOp::kReplace);
+ state.clipRect(50, 50, 150, 150, SkClipOp::kReplace_deprecated);
ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(50, 50, 150, 150));
}
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index a329980..bd798e8 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -506,19 +506,19 @@
sk_sp<Bitmap> bitmap(TestUtils::createBitmap(20, 20));
// left side clipped (to inset left half)
- canvas.clipRect(10, 0, 50, 100, SkClipOp::kReplace);
+ canvas.clipRect(10, 0, 50, 100, SkClipOp::kReplace_deprecated);
canvas.drawBitmap(*bitmap, 0, 40, nullptr);
// top side clipped (to inset top half)
- canvas.clipRect(0, 10, 100, 50, SkClipOp::kReplace);
+ canvas.clipRect(0, 10, 100, 50, SkClipOp::kReplace_deprecated);
canvas.drawBitmap(*bitmap, 40, 0, nullptr);
// right side clipped (to inset right half)
- canvas.clipRect(50, 0, 90, 100, SkClipOp::kReplace);
+ canvas.clipRect(50, 0, 90, 100, SkClipOp::kReplace_deprecated);
canvas.drawBitmap(*bitmap, 80, 40, nullptr);
// bottom not clipped, just abutting (inset bottom half)
- canvas.clipRect(0, 50, 100, 90, SkClipOp::kReplace);
+ canvas.clipRect(0, 50, 100, 90, SkClipOp::kReplace_deprecated);
canvas.drawBitmap(*bitmap, 40, 70, nullptr);
});
@@ -2308,7 +2308,7 @@
};
auto node = TestUtils::createNode<RecordingCanvas>(20, 20, 30, 30,
[](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.clipRect(0, -20, 10, 30, SkClipOp::kReplace);
+ canvas.clipRect(0, -20, 10, 30, SkClipOp::kReplace_deprecated);
canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
});
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index b2ea9ac..d36bca0 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -476,7 +476,7 @@
OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_viewportCrop) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
// shouldn't matter, since saveLayer will clip to its bounds
- canvas.clipRect(-1000, -1000, 1000, 1000, SkClipOp::kReplace);
+ canvas.clipRect(-1000, -1000, 1000, 1000, SkClipOp::kReplace_deprecated);
canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer);
canvas.drawRect(0, 0, 400, 400, SkPaint());
@@ -654,7 +654,7 @@
OPENGL_PIPELINE_TEST(RecordingCanvas, replaceClipIntersectWithRoot) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
- canvas.clipRect(-10, -10, 110, 110, SkClipOp::kReplace);
+ canvas.clipRect(-10, -10, 110, 110, SkClipOp::kReplace_deprecated);
canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
canvas.restore();
});
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 5bb0b6d..79429ec 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -331,7 +331,7 @@
std::vector<sp<RenderNode>> nodes;
nodes.push_back(TestUtils::createSkiaNode(20, 20, 30, 30,
[](RenderProperties& props, SkiaRecordingCanvas& canvas) {
- canvas.clipRect(0, -20, 10, 30, SkClipOp::kReplace);
+ canvas.clipRect(0, -20, 10, 30, SkClipOp::kReplace_deprecated);
canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
}));
diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp
index ecad7be..492ca7f 100644
--- a/libs/hwui/utils/TestWindowContext.cpp
+++ b/libs/hwui/utils/TestWindowContext.cpp
@@ -93,7 +93,7 @@
SkCanvas* prepareToDraw() {
//mCanvas->reset(mSize.width(), mSize.height());
- mCanvas->clipRect(0, 0, mSize.width(), mSize.height(), SkClipOp::kReplace);
+ mCanvas->clipRect(0, 0, mSize.width(), mSize.height(), SkClipOp::kReplace_deprecated);
return mCanvas->asSkCanvas();
}
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 6f44e6d..e51025f 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -994,7 +994,9 @@
*
* <p>The current position for time shifting is the same as the current position of
* playback. It should be equal to or greater than the start position reported by
- * {@link #onTimeShiftGetStartPosition()}.
+ * {@link #onTimeShiftGetStartPosition()}. When playback is completed, the current position
+ * should stay where the playback ends, in other words, the returned value of this mehtod
+ * should be equal to the start position plus the duration of the program.
*
* @see #onTimeShiftPlay(Uri)
* @see #onTimeShiftResume()
diff --git a/packages/CarrierDefaultApp/res/values/strings.xml b/packages/CarrierDefaultApp/res/values/strings.xml
index fe5669d..f904600 100644
--- a/packages/CarrierDefaultApp/res/values/strings.xml
+++ b/packages/CarrierDefaultApp/res/values/strings.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">CarrierDefaultApp</string>
- <string name="android_system_label">Android System</string>
+ <string name="android_system_label">Mobile Carrier</string>
<string name="portal_notification_id">Mobile data has run out</string>
- <string name="no_data_notification_id">No Mobile data service</string>
- <string name="portal_notification_detail">Tap to add funds to your %s SIM</string>
+ <string name="no_data_notification_id">Your mobile data has been deactivated</string>
+ <string name="portal_notification_detail">Tap to visit the %s website</string>
<string name="no_data_notification_detail">Please contact your service provider %s</string>
<string name="progress_dialogue_network_connection">Connecting to captive portal...</string>
<string name="alert_dialogue_network_timeout">Network timeout, would you like to retry?</string>
diff --git a/packages/Osu/src/com/android/hotspot2/app/OSUService.java b/packages/Osu/src/com/android/hotspot2/app/OSUService.java
index 62a203d..e9da113 100644
--- a/packages/Osu/src/com/android/hotspot2/app/OSUService.java
+++ b/packages/Osu/src/com/android/hotspot2/app/OSUService.java
@@ -46,8 +46,9 @@
private static final String[] INTENTS = {
WifiManager.SCAN_RESULTS_AVAILABLE_ACTION,
- WifiManager.PASSPOINT_WNM_FRAME_RECEIVED_ACTION,
- WifiManager.PASSPOINT_ICON_RECEIVED_ACTION,
+ // TODO(b/32883320): use updated intent definitions.
+ //WifiManager.PASSPOINT_WNM_FRAME_RECEIVED_ACTION,
+ //WifiManager.PASSPOINT_ICON_RECEIVED_ACTION,
WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION,
WifiManager.WIFI_STATE_CHANGED_ACTION,
WifiManager.NETWORK_STATE_CHANGED_ACTION,
@@ -120,6 +121,8 @@
case WifiManager.SCAN_RESULTS_AVAILABLE_ACTION:
mOsuManager.pushScanResults(wifiManager.getScanResults());
break;
+ // TODO(b/32883320): use updated intent definitions.
+ /*
case WifiManager.PASSPOINT_WNM_FRAME_RECEIVED_ACTION:
long bssid = bundle.getLong(WifiManager.EXTRA_PASSPOINT_WNM_BSSID);
String url = bundle.getString(WifiManager.EXTRA_PASSPOINT_WNM_URL);
@@ -157,6 +160,7 @@
bundle.getString(WifiManager.EXTRA_PASSPOINT_ICON_FILE),
bundle.getByteArray(WifiManager.EXTRA_PASSPOINT_ICON_DATA));
break;
+ */
case WifiManager.NETWORK_STATE_CHANGED_ACTION:
mOsuManager.networkConnectChange(
(WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO));
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
index fa5ba73..ee7885d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
@@ -132,7 +132,7 @@
mCategoryByKeyMap.put(category.key, category);
}
backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);
- normalizePriority(context, mCategoryByKeyMap);
+ sortCategories(context, mCategoryByKeyMap);
filterDuplicateTiles(mCategoryByKeyMap);
}
}
@@ -188,17 +188,17 @@
}
/**
- * Normalize priority values on tiles across injected from all apps to make sure they don't set
- * the same priority value. However internal tiles' priority remains unchanged.
+ * Sort the tiles injected from all apps such that if they have the same priority value,
+ * they wil lbe sorted by package name.
* <p/>
- * A list of tiles are considered normalized when their priority value increases in a linear
+ * A list of tiles are considered sorted when their priority value decreases in a linear
* scan.
*/
@VisibleForTesting
- synchronized void normalizePriority(Context context,
+ synchronized void sortCategories(Context context,
Map<String, DashboardCategory> categoryByKeyMap) {
for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) {
- normalizePriorityForExternalTiles(context, categoryEntry.getValue());
+ sortCategoriesForExternalTiles(context, categoryEntry.getValue());
}
}
@@ -228,39 +228,34 @@
}
/**
- * Normalize priority value for tiles within a single {@code DashboardCategory}.
+ * Sort priority value for tiles within a single {@code DashboardCategory}.
*
- * @see #normalizePriority(Context, Map)
+ * @see #sortCategories(Context, Map)
*/
- private synchronized void normalizePriorityForExternalTiles(Context context,
+ private synchronized void sortCategoriesForExternalTiles(Context context,
DashboardCategory dashboardCategory) {
final String skipPackageName = context.getPackageName();
- // Sort tiles based on [package, priority within package]
+ // Sort tiles based on [priority, package within priority]
Collections.sort(dashboardCategory.tiles, (tile1, tile2) -> {
final String package1 = tile1.intent.getComponent().getPackageName();
final String package2 = tile2.intent.getComponent().getPackageName();
final int packageCompare = CASE_INSENSITIVE_ORDER.compare(package1, package2);
- // First sort by package name
+ // First sort by priority
+ final int priorityCompare = tile2.priority - tile1.priority;
+ if (priorityCompare != 0) {
+ return priorityCompare;
+ }
+ // Then sort by package name, skip package take precedence
if (packageCompare != 0) {
- return packageCompare;
- } else if (TextUtils.equals(package1, skipPackageName)) {
- return 0;
+ if (TextUtils.equals(package1, skipPackageName)) {
+ return -1;
+ }
+ if (TextUtils.equals(package2, skipPackageName)) {
+ return 1;
+ }
}
- // Then sort by priority
- return tile1.priority - tile2.priority;
+ return packageCompare;
});
- // Update priority for all items so no package define the same priority value.
- final int count = dashboardCategory.tiles.size();
- for (int i = 0; i < count; i++) {
- final String packageName =
- dashboardCategory.tiles.get(i).intent.getComponent().getPackageName();
- if (TextUtils.equals(packageName, skipPackageName)) {
- // We skip this tile because it's a intent pointing to our own app. We trust the
- // priority is set correctly, so don't normalize.
- continue;
- }
- dashboardCategory.tiles.get(i).priority = i;
- }
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java
index 434241d..8d61338 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java
@@ -120,7 +120,7 @@
}
@Test
- public void normalizePriority_singlePackage_shouldReorderBasedOnPriority() {
+ public void sortCategories_singlePackage_shouldReorderBasedOnPriority() {
// Create some fake tiles that are not sorted.
final String testPackage = "com.android.test";
final DashboardCategory category = new DashboardCategory();
@@ -141,22 +141,18 @@
category.tiles.add(tile3);
mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
- // Normalize their priorities
- mCategoryManager.normalizePriority(ShadowApplication.getInstance().getApplicationContext(),
+ // Sort their priorities
+ mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
mCategoryByKeyMap);
// Verify they are now sorted.
- assertThat(category.tiles.get(0)).isSameAs(tile2);
+ assertThat(category.tiles.get(0)).isSameAs(tile3);
assertThat(category.tiles.get(1)).isSameAs(tile1);
- assertThat(category.tiles.get(2)).isSameAs(tile3);
- // Verify their priority is normalized
- assertThat(category.tiles.get(0).priority).isEqualTo(0);
- assertThat(category.tiles.get(1).priority).isEqualTo(1);
- assertThat(category.tiles.get(2).priority).isEqualTo(2);
+ assertThat(category.tiles.get(2)).isSameAs(tile2);
}
@Test
- public void normalizePriority_multiPackage_shouldReorderBasedOnPackageAndPriority() {
+ public void sortCategories_multiPackage_shouldReorderBasedOnPackageAndPriority() {
// Create some fake tiles that are not sorted.
final String testPackage1 = "com.android.test1";
final String testPackage2 = "com.android.test2";
@@ -178,22 +174,18 @@
category.tiles.add(tile3);
mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
- // Normalize their priorities
- mCategoryManager.normalizePriority(ShadowApplication.getInstance().getApplicationContext(),
+ // Sort their priorities
+ mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
mCategoryByKeyMap);
// Verify they are now sorted.
- assertThat(category.tiles.get(0)).isSameAs(tile3);
- assertThat(category.tiles.get(1)).isSameAs(tile2);
- assertThat(category.tiles.get(2)).isSameAs(tile1);
- // Verify their priority is normalized
- assertThat(category.tiles.get(0).priority).isEqualTo(0);
- assertThat(category.tiles.get(1).priority).isEqualTo(1);
- assertThat(category.tiles.get(2).priority).isEqualTo(2);
+ assertThat(category.tiles.get(0)).isSameAs(tile2);
+ assertThat(category.tiles.get(1)).isSameAs(tile1);
+ assertThat(category.tiles.get(2)).isSameAs(tile3);
}
@Test
- public void normalizePriority_internalPackageTiles_shouldSkipTileForInternalPackage() {
+ public void sortCategories_internalPackageTiles_shouldSkipTileForInternalPackage() {
// Create some fake tiles that are not sorted.
final String testPackage =
ShadowApplication.getInstance().getApplicationContext().getPackageName();
@@ -215,18 +207,82 @@
category.tiles.add(tile3);
mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
- // Normalize their priorities
- mCategoryManager.normalizePriority(ShadowApplication.getInstance().getApplicationContext(),
+ // Sort their priorities
+ mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
mCategoryByKeyMap);
// Verify the sorting order is not changed
assertThat(category.tiles.get(0)).isSameAs(tile1);
assertThat(category.tiles.get(1)).isSameAs(tile2);
assertThat(category.tiles.get(2)).isSameAs(tile3);
- // Verify their priorities are not changed.
- assertThat(category.tiles.get(0).priority).isEqualTo(100);
- assertThat(category.tiles.get(1).priority).isEqualTo(100);
- assertThat(category.tiles.get(2).priority).isEqualTo(50);
+ }
+
+ @Test
+ public void sortCategories_internalAndExternalPackageTiles_shouldRetainPriorityOrdering() {
+ // Inject one external tile among internal tiles.
+ final String testPackage =
+ ShadowApplication.getInstance().getApplicationContext().getPackageName();
+ final String testPackage2 = "com.google.test2";
+ final DashboardCategory category = new DashboardCategory();
+ final Tile tile1 = new Tile();
+ tile1.intent = new Intent().setComponent(new ComponentName(testPackage, "class1"));
+ tile1.priority = 2;
+ final Tile tile2 = new Tile();
+ tile2.intent = new Intent().setComponent(new ComponentName(testPackage, "class2"));
+ tile2.priority = 1;
+ final Tile tile3 = new Tile();
+ tile3.intent = new Intent().setComponent(new ComponentName(testPackage2, "class0"));
+ tile3.priority = 0;
+ final Tile tile4 = new Tile();
+ tile4.intent = new Intent().setComponent(new ComponentName(testPackage, "class3"));
+ tile4.priority = -1;
+ category.tiles.add(tile1);
+ category.tiles.add(tile2);
+ category.tiles.add(tile3);
+ category.tiles.add(tile4);
+ mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
+
+ // Sort their priorities
+ mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
+ mCategoryByKeyMap);
+
+ // Verify the sorting order is not changed
+ assertThat(category.tiles.get(0)).isSameAs(tile1);
+ assertThat(category.tiles.get(1)).isSameAs(tile2);
+ assertThat(category.tiles.get(2)).isSameAs(tile3);
+ assertThat(category.tiles.get(3)).isSameAs(tile4);
+ }
+
+ @Test
+ public void sortCategories_samePriority_internalPackageTileShouldTakePrecedence() {
+ // Inject one external tile among internal tiles with same priority.
+ final String testPackage =
+ ShadowApplication.getInstance().getApplicationContext().getPackageName();
+ final String testPackage2 = "com.google.test2";
+ final String testPackage3 = "com.abcde.test3";
+ final DashboardCategory category = new DashboardCategory();
+ final Tile tile1 = new Tile();
+ tile1.intent = new Intent().setComponent(new ComponentName(testPackage2, "class1"));
+ tile1.priority = 1;
+ final Tile tile2 = new Tile();
+ tile2.intent = new Intent().setComponent(new ComponentName(testPackage, "class2"));
+ tile2.priority = 1;
+ final Tile tile3 = new Tile();
+ tile3.intent = new Intent().setComponent(new ComponentName(testPackage3, "class3"));
+ tile3.priority = 1;
+ category.tiles.add(tile1);
+ category.tiles.add(tile2);
+ category.tiles.add(tile3);
+ mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
+
+ // Sort their priorities
+ mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
+ mCategoryByKeyMap);
+
+ // Verify the sorting order is internal first, follow by package name ordering
+ assertThat(category.tiles.get(0)).isSameAs(tile2);
+ assertThat(category.tiles.get(1)).isSameAs(tile3);
+ assertThat(category.tiles.get(2)).isSameAs(tile1);
}
@Test
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 1fe3c48..4a54c0e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -240,8 +240,8 @@
Settings.Global.HDMI_CONTROL_ENABLED,
GlobalSettingsProto.HDMI_CONTROL_ENABLED);
dumpSetting(s, p,
- Settings.Global.HDMI_SYSTEM_AUDIO_ENABLED,
- GlobalSettingsProto.HDMI_SYSTEM_AUDIO_ENABLED);
+ Settings.Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
+ GlobalSettingsProto.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED);
dumpSetting(s, p,
Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
GlobalSettingsProto.HDMI_CONTROL_AUTO_WAKEUP_ENABLED);
diff --git a/packages/Shell/res/drawable/ic_bug_report_black_24dp.xml b/packages/Shell/res/drawable/ic_bug_report_black_24dp.xml
new file mode 100644
index 0000000..a102cee
--- /dev/null
+++ b/packages/Shell/res/drawable/ic_bug_report_black_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M20,8h-2.81c-0.45,-0.78 -1.07,-1.45 -1.82,-1.96L17,4.41 15.59,3l-2.17,2.17C12.96,5.06 12.49,5 12,5c-0.49,0 -0.96,0.06 -1.41,0.17L8.41,3 7,4.41l1.62,1.63C7.88,6.55 7.26,7.22 6.81,8L4,8v2h2.09c-0.05,0.33 -0.09,0.66 -0.09,1v1L4,12v2h2v1c0,0.34 0.04,0.67 0.09,1L4,16v2h2.81c1.04,1.79 2.97,3 5.19,3s4.15,-1.21 5.19,-3L20,18v-2h-2.09c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1L20,10L20,8zM14,16h-4v-2h4v2zM14,12h-4v-2h4v2z"
+ android:fillColor="#000000"/>
+</vector>
diff --git a/packages/Shell/res/values/strings.xml b/packages/Shell/res/values/strings.xml
index 2a5703a..1c49a55d 100644
--- a/packages/Shell/res/values/strings.xml
+++ b/packages/Shell/res/values/strings.xml
@@ -4,9 +4,9 @@
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.
@@ -16,6 +16,9 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label">Shell</string>
+ <!-- Title of notification channel for bug report related notifications. [CHAR LIMIT=50] -->
+ <string name="bugreport_notification_channel">Bug reports</string>
+
<!-- Title of notification indicating a bugreport is being generated. [CHAR LIMIT=50] -->
<string name="bugreport_in_progress_title">Bug report <xliff:g id="id">#%d</xliff:g> is being generated</string>
<!-- Title of notification indicating a bugreport has been successfully captured. [CHAR LIMIT=50] -->
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 1df626f..bf5e6f8 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -57,6 +57,7 @@
import android.app.AlertDialog;
import android.app.Notification;
import android.app.Notification.Action;
+import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
@@ -64,6 +65,7 @@
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.net.Uri;
@@ -196,6 +198,8 @@
*/
private static final String SCREENSHOT_DIR = "bugreports";
+ private static final String NOTIFICATION_CHANNEL_ID = "bugreports";
+
/** Managed dumpstate processes (keyed by id) */
private final SparseArray<DumpstateListener> mProcesses = new SparseArray<>();
@@ -240,6 +244,12 @@
final Configuration conf = mContext.getResources().getConfiguration();
mIsWatch = (conf.uiMode & Configuration.UI_MODE_TYPE_MASK) ==
Configuration.UI_MODE_TYPE_WATCH;
+ NotificationManager nm = NotificationManager.from(mContext);
+ nm.createNotificationChannel(
+ new NotificationChannel(NOTIFICATION_CHANNEL_ID,
+ mContext.getString(R.string.bugreport_notification_channel),
+ isTv(this) ? NotificationManager.IMPORTANCE_DEFAULT
+ : NotificationManager.IMPORTANCE_LOW));
}
@Override
@@ -1008,13 +1018,16 @@
sNotificationBundle.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
context.getString(com.android.internal.R.string.android_system_label));
}
- return new Notification.Builder(context)
+ return new Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
.addExtras(sNotificationBundle)
.setCategory(Notification.CATEGORY_SYSTEM)
- .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+ .setSmallIcon(
+ isTv(context) ? R.drawable.ic_bug_report_black_24dp
+ : com.android.internal.R.drawable.stat_sys_adb)
.setLocalOnly(true)
.setColor(context.getColor(
- com.android.internal.R.color.system_notification_accent_color));
+ com.android.internal.R.color.system_notification_accent_color))
+ .extend(new Notification.TvExtender());
}
/**
@@ -1333,6 +1346,10 @@
return false;
}
+ private static boolean isTv(Context context) {
+ return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+ }
+
/**
* Checks whether a character is valid on bugreport names.
*/
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index dcc5501..428b7b8 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -303,20 +303,6 @@
</intent-filter>
</activity>
- <activity android:name=".recents.tv.RecentsTvActivity"
- android:label="@string/accessibility_desc_recent_apps"
- android:exported="false"
- android:launchMode="singleInstance"
- android:excludeFromRecents="true"
- android:stateNotNeeded="true"
- android:resumeWhilePausing="true"
- android:screenOrientation="behind"
- android:theme="@style/RecentsTvTheme.Wallpaper">
- <intent-filter>
- <action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
- </intent-filter>
- </activity>
-
<activity
android:name=".stackdivider.ForcedResizableInfoActivity"
android:theme="@style/ForcedResizableTheme"
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_gain_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_gain_animation.xml
deleted file mode 100644
index 52e3a04..0000000
--- a/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_gain_animation.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-
- <objectAnimator
- android:propertyName="translationY"
- android:valueTo="0dp"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="@integer/recents_tv_pip_focus_anim_duration" />
- <objectAnimator
- android:propertyName="scaleX"
- android:valueTo="1.0"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="@integer/recents_tv_pip_focus_anim_duration" />
- <objectAnimator
- android:propertyName="scaleY"
- android:valueTo="1.0"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="@integer/recents_tv_pip_focus_anim_duration" />
-</set>
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_loss_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_loss_animation.xml
deleted file mode 100644
index b571aa5..0000000
--- a/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_loss_animation.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-
- <objectAnimator
- android:propertyName="translationY"
- android:valueTo="-57dp"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="@integer/recents_tv_pip_focus_anim_duration" />
- <objectAnimator
- android:propertyName="scaleX"
- android:valueTo="0.7"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="@integer/recents_tv_pip_focus_anim_duration" />
- <objectAnimator
- android:propertyName="scaleY"
- android:valueTo="0.7"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="@integer/recents_tv_pip_focus_anim_duration" />
-</set>
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_in_recents_scrim_fade_in_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_in_recents_scrim_fade_in_animation.xml
deleted file mode 100644
index 257bf35..0000000
--- a/packages/SystemUI/res/anim/tv_pip_controls_in_recents_scrim_fade_in_animation.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:propertyName="alpha"
- android:valueTo="1"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="100" />
diff --git a/packages/SystemUI/res/drawable/recents_tv_background_gradient.xml b/packages/SystemUI/res/drawable/recents_tv_background_gradient.xml
deleted file mode 100644
index 1e52a91..0000000
--- a/packages/SystemUI/res/drawable/recents_tv_background_gradient.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <gradient
- android:startColor="#4C000000"
- android:endColor="#72000000"
- android:angle="90"/>
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/recents_tv_card_thumbnail_background.xml b/packages/SystemUI/res/drawable/recents_tv_card_thumbnail_background.xml
deleted file mode 100644
index dc8e629..0000000
--- a/packages/SystemUI/res/drawable/recents_tv_card_thumbnail_background.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
-
- <solid
- android:color="@color/recents_tv_card_background_color"/>
- <corners
- android:radius="@dimen/recents_tv_card_corner_radius" />
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/recents_tv_dismiss_icon.xml b/packages/SystemUI/res/drawable/recents_tv_dismiss_icon.xml
deleted file mode 100644
index 7fb67a2..0000000
--- a/packages/SystemUI/res/drawable/recents_tv_dismiss_icon.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<transition xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@drawable/ic_dismiss_outline" />
- <item android:drawable="@drawable/ic_cancel_white_24dp" />
-</transition>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-television/recents_on_tv.xml b/packages/SystemUI/res/layout-television/recents_on_tv.xml
deleted file mode 100644
index 2b78bee..0000000
--- a/packages/SystemUI/res/layout-television/recents_on_tv.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<com.android.systemui.recents.tv.views.RecentsTvView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/recents_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:background="@drawable/recents_tv_background_gradient">
-
- <com.android.systemui.recents.tv.views.TaskStackHorizontalGridView
- android:id="@+id/task_list"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:descendantFocusability="beforeDescendants"
- android:layout_marginTop="@dimen/recents_tv_gird_row_top_margin"
- android:focusable="true"
- android:layoutDirection="rtl" />
-
- <!-- Placeholder view to give focus to the PIP menus in talkback mode -->
- <View
- android:id="@+id/pip"
- android:layout_width="1dp"
- android:layout_height="1dp"
- android:focusable="true"
- android:visibility="gone" />
-
- <!-- Placeholder to dismiss during talkback. -->
- <ImageView
- android:id="@+id/dismiss_placeholder"
- android:layout_width="@dimen/recents_tv_dismiss_icon_size"
- android:layout_height="@dimen/recents_tv_dismiss_icon_size"
- android:layout_gravity="bottom|center_horizontal"
- android:layout_marginBottom="50dp"
- android:src="@drawable/ic_cancel_white_24dp"
- android:contentDescription="@string/status_bar_accessibility_dismiss_recents"
- android:focusable="true"
- android:visibility="gone" />
-
-</com.android.systemui.recents.tv.views.RecentsTvView>
diff --git a/packages/SystemUI/res/layout-television/recents_tv_card_info_field.xml b/packages/SystemUI/res/layout-television/recents_tv_card_info_field.xml
deleted file mode 100644
index 34cba07..0000000
--- a/packages/SystemUI/res/layout-television/recents_tv_card_info_field.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/card_info_field"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <ImageView
- android:id="@+id/card_extra_badge"
- android:layout_width="@dimen/recents_tv_card_extra_badge_size"
- android:layout_height="@dimen/recents_tv_card_extra_badge_size"
- android:layout_marginBottom="@dimen/recents_tv_icon_padding_bottom"
- android:scaleType="fitCenter"
- android:layout_centerVertical="true"
- android:layout_alignParentEnd="true"/>
- <TextView
- android:id="@+id/card_title_text"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:includeFontPadding="true"
- android:singleLine="true"
- android:shadowColor="@color/recents_tv_text_shadow_color"
- android:shadowRadius="5"
- android:shadowDx="0"
- android:shadowDy="0"
- android:textColor="@color/recents_tv_card_title_text_color"
- android:fontFamily="@string/font_roboto_regular"
- android:textSize="@dimen/recents_tv_title_text_size"
- android:paddingStart="@dimen/recents_tv_text_padding_start"
- android:layout_marginBottom="@dimen/recents_tv_text_padding_bottom"
- android:ellipsize="end"/>
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout-television/recents_tv_empty.xml b/packages/SystemUI/res/layout-television/recents_tv_empty.xml
deleted file mode 100644
index e5d888f..0000000
--- a/packages/SystemUI/res/layout-television/recents_tv_empty.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<TextView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:gravity="center"
- android:drawablePadding="25dp"
- android:textSize="16sp"
- android:textColor="#ffffffff"
- android:text="@string/recents_empty_message"
- android:fontFamily="sans-serif"
- android:visibility="gone" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml b/packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml
deleted file mode 100644
index 201f47d..0000000
--- a/packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml
+++ /dev/null
@@ -1,64 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<com.android.systemui.recents.tv.views.TaskCardView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:layout_gravity="center"
- android:layout_centerInParent="true"
- android:clipToPadding="false"
- android:orientation="vertical" >
- <include layout="@layout/recents_tv_card_info_field"/>
- <LinearLayout
- android:id="@+id/card_view_thumbnail"
- android:layout_width="@dimen/recents_tv_card_width"
- android:layout_height="@dimen/recents_tv_screenshot_height"
- android:gravity="center"
- android:orientation="vertical"
- android:background="@drawable/recents_tv_card_thumbnail_background"
- android:layout_centerHorizontal="true" >
-
- <ImageView
- android:id="@+id/card_view_banner_icon"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_centerHorizontal="true"
- android:scaleType="centerCrop"
- android:gravity="center" />
-
- </LinearLayout>
- <ImageView
- android:id="@+id/dismiss_icon"
- android:layout_width="@dimen/recents_tv_dismiss_icon_size"
- android:layout_height="@dimen/recents_tv_dismiss_icon_size"
- android:layout_gravity="center_horizontal"
- android:layout_marginTop="@dimen/recents_tv_dismiss_icon_top_margin"
- android:layout_marginBottom="@dimen/recents_tv_dismiss_icon_bottom_margin"
- android:alpha="@integer/dismiss_unselected_alpha"
- android:src="@drawable/recents_tv_dismiss_icon" />
- <TextView
- android:id="@+id/card_dismiss_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="@dimen/recents_tv_dismiss_text_size"
- android:fontFamily="@string/font_roboto_light"
- android:textColor="@color/recents_tv_dismiss_text_color"
- android:text="@string/recents_tv_dismiss"
- android:alpha="0.0"
- android:layout_gravity="center_horizontal" />
-</com.android.systemui.recents.tv.views.TaskCardView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml b/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml
deleted file mode 100644
index 949400c..0000000
--- a/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="top|center_horizontal"
- android:orientation="vertical">
-
- <com.android.systemui.pip.tv.PipRecentsControlsView
- android:id="@+id/pip_controls"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:clipToPadding="false">
-
- <View
- android:id="@+id/scrim"
- android:layout_width="160dp"
- android:layout_height="32dp"
- android:translationY="-46dp"
- android:layout_gravity="top|center_horizontal"
- android:background="@drawable/tv_pip_recents_overlay_scrim"
- android:alpha="0" />
- <com.android.systemui.pip.tv.PipControlsView
- android:id="@+id/pip_control_contents"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="10dp"
- android:layout_gravity="top|center_horizontal" />
- </com.android.systemui.pip.tv.PipRecentsControlsView>
-
- <!-- Placeholder view to handle focus change between Recents row and PIP controls
- in talkback mode -->
- <View
- android:id="@+id/recents"
- android:layout_width="1dp"
- android:layout_height="1dp"
- android:focusable="true" />
-</LinearLayout>
diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/packages/SystemUI/res/values/dimens_tv.xml
index 2319229..30355d4 100644
--- a/packages/SystemUI/res/values/dimens_tv.xml
+++ b/packages/SystemUI/res/values/dimens_tv.xml
@@ -17,44 +17,6 @@
*/
-->
<resources>
- <!-- Dimens for recents card in the recents view on tv -->
- <dimen name="recents_tv_card_width">240dip</dimen>
- <dimen name="recents_tv_screenshot_height">135dip</dimen>
- <dimen name="recents_tv_card_extra_badge_size">20dip</dimen>
- <dimen name="recents_tv_banner_width">130dip</dimen>
- <dimen name="recents_tv_banner_height">72dip</dimen>
- <dimen name="recents_tv_fallback_icon_width">40dip</dimen>
- <dimen name="recents_tv_fallback_icon_height">40dip</dimen>
- <dimen name="recents_tv_banner_margin_top">16dip</dimen>
- <dimen name="recents_tv_icon_padding_bottom">8dip</dimen>
- <dimen name="recents_tv_text_padding_start">12dip</dimen>
- <dimen name="recents_tv_text_padding_bottom">12dip</dimen>
- <dimen name="recents_tv_card_corner_radius">2dip</dimen>
-
- <!-- Padding for grid view in recents view on tv -->
- <dimen name="recents_tv_gird_row_top_margin">215dip</dimen>
- <dimen name="recents_tv_grid_max_row_height">268dip</dimen>
- <dimen name="recents_tv_gird_card_spacing">8dip</dimen>
- <dimen name="recents_tv_gird_focused_card_delta">44dip</dimen>
-
- <!-- Values for focus animation -->
- <dimen name="recents_tv_unselected_item_z">6dp</dimen>
- <dimen name="recents_tv_selected_item_z_delta">10dp</dimen>
-
- <!-- Values for text on recents cards on tv -->
- <dimen name="recents_tv_title_text_size">12sp</dimen>
-
- <!-- Values for card dismiss state -->
- <dimen name="recents_tv_dismiss_shift_down">48dip</dimen>
- <dimen name="recents_tv_dismiss_top_margin">356dip</dimen>
- <dimen name="recents_tv_dismiss_icon_size">19dip</dimen>
- <dimen name="recents_tv_dismiss_icon_top_margin">38dip</dimen>
- <dimen name="recents_tv_dismiss_icon_bottom_margin">1dip</dimen>
- <dimen name="recents_tv_dismiss_text_size">12sp</dimen>
-
<!-- Extra space around the PIP and its outline in PIP onboarding activity -->
<dimen name="tv_pip_bounds_space">3dp</dimen>
-
- <!-- Values for entering Recents and exiting Recents -->
- <dimen name="recents_tv_home_recents_shift">125dip</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/integers_tv.xml b/packages/SystemUI/res/values/integers_tv.xml
index 3b62938..09547da 100644
--- a/packages/SystemUI/res/values/integers_tv.xml
+++ b/packages/SystemUI/res/values/integers_tv.xml
@@ -14,17 +14,6 @@
limitations under the License.
-->
<resources>
- <integer name="item_scale_anim_duration">150</integer>
- <integer name="dismiss_short_duration">200</integer>
- <integer name="dismiss_long_duration">400</integer>
-
- <integer name="recents_tv_pip_focus_anim_duration">200</integer>
-
- <!-- Duration for how long it takes cards to slide in or out when going to and from recents. -->
- <integer name="recents_home_duration">400</integer>
- <!-- Delay between the start of slide in animation for each card. -->
- <integer name="recents_home_delay">40</integer>
-
<!-- Delay of the onboarding animation start after it launches -->
<integer name="tv_pip_onboarding_anim_start_delay">1000</integer>
<!-- Duration of the onboarding animation duration -->
diff --git a/packages/SystemUI/res/values/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml
index f49d201..fb9e1f2 100644
--- a/packages/SystemUI/res/values/strings_tv.xml
+++ b/packages/SystemUI/res/values/strings_tv.xml
@@ -37,15 +37,4 @@
<string name="pip_onboarding_description">This keeps your video in view until you play another one. Press and hold <b>HOME</b> to control it.</string>
<!-- Button to close picture-in-picture (PIP) onboarding screen. -->
<string name="pip_onboarding_button">Got it</string>
- <!-- Dismiss icon description -->
- <string name="recents_tv_dismiss">Dismiss</string>
- <!-- Font for Recents -->
- <!-- DO NOT TRANSLATE -->
- <string name="font_roboto_regular" translatable="false">sans-serif</string>
- <!-- DO NOT TRANSLATE -->
- <string name="font_roboto_light" translatable="false">sans-serif-light</string>
- <!-- Package names to be blacklisted in Recents, add package names into overlay as needed -->
- <string-array name="recents_tv_blacklist_array">
- </string-array>
-
</resources>
diff --git a/packages/SystemUI/res/values/styles_tv.xml b/packages/SystemUI/res/values/styles_tv.xml
index 263e1a4..3f0caab 100644
--- a/packages/SystemUI/res/values/styles_tv.xml
+++ b/packages/SystemUI/res/values/styles_tv.xml
@@ -22,11 +22,4 @@
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:backgroundDimEnabled">false</item>
</style>
-
- <style name="RecentsTvTheme.Wallpaper" parent="@android:style/Theme.Material.NoActionBar.Overscan">
- <item name="android:windowBackground">@android:color/transparent</item>
- <item name="android:backgroundDimEnabled">false</item>
- <item name="android:colorBackgroundCacheHint">@null</item>
- <item name="android:windowIsTranslucent">true</item>
- </style>
</resources>
diff --git a/packages/SystemUI/res/values/values_tv.xml b/packages/SystemUI/res/values/values_tv.xml
deleted file mode 100644
index 9259415..0000000
--- a/packages/SystemUI/res/values/values_tv.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <item format="float" type="integer" name="unselected_scale">1.0</item>
- <item format="float" type="integer" name="selected_scale">1.259</item>
- <item format="float" type="integer" name="dismiss_unselected_alpha">0.3</item>
-</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index ee8641b..14f2c4a 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -15,6 +15,8 @@
*/
package com.android.systemui;
+import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
+
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -50,8 +52,6 @@
public class BatteryMeterView extends LinearLayout implements
BatteryStateChangeCallback, Tunable, DarkReceiver, ConfigurationListener {
- public static final String SHOW_PERCENT_SETTING = "status_bar_show_battery_percent";
-
private final BatteryMeterDrawableBase mDrawable;
private final String mSlotBattery;
private final ImageView mBatteryIconView;
@@ -129,7 +129,7 @@
mBatteryController = Dependency.get(BatteryController.class);
mBatteryController.addCallback(this);
getContext().getContentResolver().registerContentObserver(
- Settings.System.getUriFor(SHOW_PERCENT_SETTING), false, mSettingObserver);
+ Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver);
updateShowPercent();
Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
Dependency.get(ConfigurationController.class).addCallback(this);
@@ -175,7 +175,7 @@
private void updateShowPercent() {
final boolean showing = mBatteryPercentView != null;
if (0 != Settings.System.getInt(getContext().getContentResolver(),
- BatteryMeterView.SHOW_PERCENT_SETTING, 0) || mForceShowPercent) {
+ SHOW_BATTERY_PERCENT, 0) || mForceShowPercent) {
if (!showing) {
mBatteryPercentView = loadPercentView();
if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor);
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index bb7e19d..4dfaf45 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -30,6 +30,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.PluginDependencyProvider;
import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.plugins.PluginManagerImpl;
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
import com.android.systemui.statusbar.phone.ManagedProfileController;
@@ -73,6 +74,7 @@
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
import com.android.systemui.tuner.TunerService;
+import com.android.systemui.tuner.TunerServiceImpl;
import com.android.systemui.util.leak.GarbageMonitor;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.leak.LeakReporter;
@@ -200,7 +202,7 @@
new DeviceProvisionedControllerImpl(mContext));
mProviders.put(PluginManager.class, () ->
- new PluginManager(mContext));
+ new PluginManagerImpl(mContext));
mProviders.put(AssistManager.class, () ->
new AssistManager(getDependency(DeviceProvisionedController.class), mContext));
@@ -223,7 +225,7 @@
getDependency(LeakReporter.class)));
mProviders.put(TunerService.class, () ->
- new TunerService(mContext));
+ new TunerServiceImpl(mContext));
mProviders.put(StatusBarWindowManager.class, () ->
new StatusBarWindowManager(mContext));
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
index 762d6bc..867c15c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
@@ -98,6 +98,9 @@
*/
public void setRegistrationListener(RegistrationListener listener) {
mRegistrationListener = listener;
+ if (mRegistrationListener != null) {
+ mRegistrationListener.onRegistrationChanged(mInputEventReceiver != null);
+ }
}
/**
@@ -122,6 +125,9 @@
Log.e(TAG, "Failed to create PIP input consumer", e);
}
mInputEventReceiver = new PipInputEventReceiver(inputChannel, Looper.myLooper());
+ if (mRegistrationListener != null) {
+ mRegistrationListener.onRegistrationChanged(true /* isRegistered */);
+ }
}
}
@@ -137,6 +143,9 @@
}
mInputEventReceiver.dispose();
mInputEventReceiver = null;
+ if (mRegistrationListener != null) {
+ mRegistrationListener.onRegistrationChanged(false /* isRegistered */);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipAccessibilityInteractionConnection.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAccessibilityInteractionConnection.java
new file mode 100644
index 0000000..968bd28
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAccessibilityInteractionConnection.java
@@ -0,0 +1,191 @@
+/*
+ * 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.systemui.pip.phone;
+
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.view.MagnificationSpec;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityWindowInfo;
+import android.view.accessibility.IAccessibilityInteractionConnection;
+import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Expose the touch actions to accessibility as if this object were a window with a single view.
+ * That pseudo-view exposes all of the actions this object can perform.
+ */
+public class PipAccessibilityInteractionConnection
+ extends IAccessibilityInteractionConnection.Stub {
+
+ public interface AccessibilityCallbacks {
+ void onAccessibilityShowMenu();
+ }
+
+ private static final long ACCESSIBILITY_NODE_ID = 1;
+ private List<AccessibilityNodeInfo> mAccessibilityNodeInfoList;
+
+ private Handler mHandler;
+ private PipMotionHelper mMotionHelper;
+ private AccessibilityCallbacks mCallbacks;
+
+ private Rect mTmpBounds = new Rect();
+
+ public PipAccessibilityInteractionConnection(PipMotionHelper motionHelper,
+ AccessibilityCallbacks callbacks, Handler handler) {
+ mHandler = handler;
+ mMotionHelper = motionHelper;
+ mCallbacks = callbacks;
+ }
+
+ @Override
+ public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId,
+ Region interactiveRegion, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, int flags,
+ int interrogatingPid, long interrogatingTid, MagnificationSpec spec, Bundle args) {
+ try {
+ callback.setFindAccessibilityNodeInfosResult(
+ (accessibilityNodeId == AccessibilityNodeInfo.ROOT_NODE_ID)
+ ? getNodeList() : null, interactionId);
+ } catch (RemoteException re) {
+ /* best effort - ignore */
+ }
+ }
+
+ @Override
+ public void performAccessibilityAction(long accessibilityNodeId, int action,
+ Bundle arguments, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, int flags,
+ int interrogatingPid, long interrogatingTid) {
+ // We only support one view. A request for anything else is invalid
+ boolean result = false;
+ if (accessibilityNodeId == AccessibilityNodeInfo.ROOT_NODE_ID) {
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_CLICK:
+ mHandler.post(() -> {
+ mCallbacks.onAccessibilityShowMenu();
+ });
+ result = true;
+ break;
+ case AccessibilityNodeInfo.ACTION_DISMISS:
+ mMotionHelper.dismissPip();
+ result = true;
+ break;
+ case com.android.internal.R.id.accessibilityActionMoveWindow:
+ int newX = arguments.getInt(
+ AccessibilityNodeInfo.ACTION_ARGUMENT_MOVE_WINDOW_X);
+ int newY = arguments.getInt(
+ AccessibilityNodeInfo.ACTION_ARGUMENT_MOVE_WINDOW_Y);
+ Rect pipBounds = new Rect();
+ pipBounds.set(mMotionHelper.getBounds());
+ mTmpBounds.offsetTo(newX, newY);
+ mMotionHelper.movePip(mTmpBounds);
+ result = true;
+ break;
+ case AccessibilityNodeInfo.ACTION_EXPAND:
+ mMotionHelper.expandPip();
+ result = true;
+ break;
+ default:
+ // Leave result as false
+ }
+ }
+ try {
+ callback.setPerformAccessibilityActionResult(result, interactionId);
+ } catch (RemoteException re) {
+ /* best effort - ignore */
+ }
+ }
+
+ @Override
+ public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId,
+ String viewId, Region interactiveRegion, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, int flags,
+ int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
+ // We have no view with a proper ID
+ try {
+ callback.setFindAccessibilityNodeInfoResult(null, interactionId);
+ } catch (RemoteException re) {
+ /* best effort - ignore */
+ }
+ }
+
+ @Override
+ public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text,
+ Region interactiveRegion, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, int flags,
+ int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
+ // We have no view with text
+ try {
+ callback.setFindAccessibilityNodeInfoResult(null, interactionId);
+ } catch (RemoteException re) {
+ /* best effort - ignore */
+ }
+ }
+
+ @Override
+ public void findFocus(long accessibilityNodeId, int focusType, Region interactiveRegion,
+ int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
+ int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
+ // We have no view that can take focus
+ try {
+ callback.setFindAccessibilityNodeInfoResult(null, interactionId);
+ } catch (RemoteException re) {
+ /* best effort - ignore */
+ }
+ }
+
+ @Override
+ public void focusSearch(long accessibilityNodeId, int direction, Region interactiveRegion,
+ int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
+ int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
+ // We have no view that can take focus
+ try {
+ callback.setFindAccessibilityNodeInfoResult(null, interactionId);
+ } catch (RemoteException re) {
+ /* best effort - ignore */
+ }
+ }
+
+ public static AccessibilityNodeInfo obtainRootAccessibilityNodeInfo() {
+ AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+ info.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID,
+ AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_MOVE_WINDOW);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
+ info.setImportantForAccessibility(true);
+ info.setClickable(true);
+ info.setVisibleToUser(true);
+ return info;
+ }
+
+ private List<AccessibilityNodeInfo> getNodeList() {
+ if (mAccessibilityNodeInfoList == null) {
+ mAccessibilityNodeInfoList = new ArrayList<>(1);
+ }
+ AccessibilityNodeInfo info = obtainRootAccessibilityNodeInfo();
+ mAccessibilityNodeInfoList.clear();
+ mAccessibilityNodeInfoList.add(info);
+ return mAccessibilityNodeInfoList;
+ }
+}
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 17c3448..80231a9 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -21,22 +21,16 @@
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.graphics.Region;
-import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
import android.util.Size;
import android.view.IPinnedStackController;
-import android.view.MagnificationSpec;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityWindowInfo;
-import android.view.accessibility.IAccessibilityInteractionConnection;
-import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -45,8 +39,6 @@
import com.android.systemui.statusbar.FlingAnimationUtils;
import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
/**
* Manages all the touch handling for PIP on the Phone, including moving, dismissing and expanding
@@ -96,13 +88,6 @@
}
};
- private Runnable mShowMenuRunnable = new Runnable() {
- @Override
- public void run() {
- mMenuController.showMenu(mMotionHelper.getBounds(), mMovementBounds);
- }
- };
-
// Behaviour states
private boolean mIsMenuVisible;
private boolean mIsMinimized;
@@ -263,7 +248,12 @@
private void onRegistrationChanged(boolean isRegistered) {
mAccessibilityManager.setPictureInPictureActionReplacingConnection(isRegistered
- ? new AccessibilityInteractionConnection() : null);
+ ? new PipAccessibilityInteractionConnection(mMotionHelper,
+ this::onAccessibilityShowMenu, mHandler) : null);
+ }
+
+ private void onAccessibilityShowMenu() {
+ mMenuController.showMenu(mMotionHelper.getBounds(), mMovementBounds);
}
private boolean handleTouchEvent(MotionEvent ev) {
@@ -314,7 +304,8 @@
if (!mSendingHoverAccessibilityEvents) {
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
- AccessibilityNodeInfo info = obtainRootAccessibilityNodeInfo();
+ AccessibilityNodeInfo info =
+ PipAccessibilityInteractionConnection.obtainRootAccessibilityNodeInfo();
event.setSource(info);
info.recycle();
mAccessibilityManager.sendAccessibilityEvent(event);
@@ -326,7 +317,8 @@
if (mSendingHoverAccessibilityEvents) {
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
- AccessibilityNodeInfo info = obtainRootAccessibilityNodeInfo();
+ AccessibilityNodeInfo info =
+ PipAccessibilityInteractionConnection.obtainRootAccessibilityNodeInfo();
event.setSource(info);
info.recycle();
mAccessibilityManager.sendAccessibilityEvent(event);
@@ -625,143 +617,4 @@
mMotionHelper.dump(pw, innerPrefix);
}
- private static AccessibilityNodeInfo obtainRootAccessibilityNodeInfo() {
- AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
- info.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID,
- AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID);
- info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK);
- info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
- info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_MOVE_WINDOW);
- info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
- info.setImportantForAccessibility(true);
- info.setClickable(true);
- info.setVisibleToUser(true);
- return info;
- }
-
- /**
- * Expose the touch actions to accessibility as if this object were a window with a single view.
- * That pseudo-view exposes all of the actions this object can perform.
- */
- class AccessibilityInteractionConnection extends IAccessibilityInteractionConnection.Stub {
- static final long ACCESSIBILITY_NODE_ID = 1;
- List<AccessibilityNodeInfo> mAccessibilityNodeInfoList;
-
- @Override
- public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId,
- Region interactiveRegion, int interactionId,
- IAccessibilityInteractionConnectionCallback callback, int flags,
- int interrogatingPid, long interrogatingTid, MagnificationSpec spec, Bundle args) {
- try {
- callback.setFindAccessibilityNodeInfosResult(
- (accessibilityNodeId == AccessibilityNodeInfo.ROOT_NODE_ID)
- ? getNodeList() : null, interactionId);
- } catch (RemoteException re) {
- /* best effort - ignore */
- }
- }
-
- @Override
- public void performAccessibilityAction(long accessibilityNodeId, int action,
- Bundle arguments, int interactionId,
- IAccessibilityInteractionConnectionCallback callback, int flags,
- int interrogatingPid, long interrogatingTid) {
- // We only support one view. A request for anything else is invalid
- boolean result = false;
- if (accessibilityNodeId == AccessibilityNodeInfo.ROOT_NODE_ID) {
- switch (action) {
- case AccessibilityNodeInfo.ACTION_CLICK:
- mHandler.post(mShowMenuRunnable);
- result = true;
- break;
- case AccessibilityNodeInfo.ACTION_DISMISS:
- mMotionHelper.dismissPip();
- result = true;
- break;
- case com.android.internal.R.id.accessibilityActionMoveWindow:
- int newX = arguments.getInt(
- AccessibilityNodeInfo.ACTION_ARGUMENT_MOVE_WINDOW_X);
- int newY = arguments.getInt(
- AccessibilityNodeInfo.ACTION_ARGUMENT_MOVE_WINDOW_Y);
- Rect pipBounds = new Rect();
- pipBounds.set(mMotionHelper.getBounds());
- mTmpBounds.offsetTo(newX, newY);
- mMotionHelper.movePip(mTmpBounds);
- result = true;
- break;
- case AccessibilityNodeInfo.ACTION_EXPAND:
- mMotionHelper.expandPip();
- result = true;
- break;
- default:
- // Leave result as false
- }
- }
- try {
- callback.setPerformAccessibilityActionResult(result, interactionId);
- } catch (RemoteException re) {
- /* best effort - ignore */
- }
- }
-
- @Override
- public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId,
- String viewId, Region interactiveRegion, int interactionId,
- IAccessibilityInteractionConnectionCallback callback, int flags,
- int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
- // We have no view with a proper ID
- try {
- callback.setFindAccessibilityNodeInfoResult(null, interactionId);
- } catch (RemoteException re) {
- /* best effort - ignore */
- }
- }
-
- @Override
- public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text,
- Region interactiveRegion, int interactionId,
- IAccessibilityInteractionConnectionCallback callback, int flags,
- int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
- // We have no view with text
- try {
- callback.setFindAccessibilityNodeInfoResult(null, interactionId);
- } catch (RemoteException re) {
- /* best effort - ignore */
- }
- }
-
- @Override
- public void findFocus(long accessibilityNodeId, int focusType, Region interactiveRegion,
- int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
- int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
- // We have no view that can take focus
- try {
- callback.setFindAccessibilityNodeInfoResult(null, interactionId);
- } catch (RemoteException re) {
- /* best effort - ignore */
- }
- }
-
- @Override
- public void focusSearch(long accessibilityNodeId, int direction, Region interactiveRegion,
- int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
- int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
- // We have no view that can take focus
- try {
- callback.setFindAccessibilityNodeInfoResult(null, interactionId);
- } catch (RemoteException re) {
- /* best effort - ignore */
- }
- }
-
- private List<AccessibilityNodeInfo> getNodeList() {
- if (mAccessibilityNodeInfoList == null) {
- mAccessibilityNodeInfoList = new ArrayList<>(1);
- }
- AccessibilityNodeInfo info = obtainRootAccessibilityNodeInfo();
- mAccessibilityNodeInfoList.clear();
- mAccessibilityNodeInfoList.add(info);
- return mAccessibilityNodeInfoList;
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 9a8974d..b71c87d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -86,14 +86,6 @@
* State when PIP menu dialog is shown.
*/
public static final int STATE_PIP_MENU = 2;
- /**
- * State when PIP is shown in Recents.
- */
- public static final int STATE_PIP_RECENTS = 3;
- /**
- * State when PIP is shown in Recents and it's focused to allow an user to control.
- */
- public static final int STATE_PIP_RECENTS_FOCUSED = 4;
private static final int TASK_ID_NO_PIP = -1;
private static final int INVALID_RESOURCE_TYPE = -1;
@@ -119,7 +111,6 @@
private int mSuspendPipResizingReason;
private Context mContext;
- private PipRecentsOverlayManager mPipRecentsOverlayManager;
private IActivityManager mActivityManager;
private IWindowManager mWindowManager;
private MediaSessionManager mMediaSessionManager;
@@ -132,9 +123,6 @@
private Rect mDefaultPipBounds = new Rect();
private Rect mSettingsPipBounds;
private Rect mMenuModePipBounds;
- private Rect mRecentsPipBounds;
- private Rect mRecentsFocusedPipBounds;
- private int mRecentsFocusChangedAnimationDurationMs;
private boolean mInitialized;
private int mPipTaskId = TASK_ID_NO_PIP;
private ComponentName mPipComponentName;
@@ -259,7 +247,6 @@
}
loadConfigurationsAndApply();
- mPipRecentsOverlayManager = new PipRecentsOverlayManager(context);
mMediaSessionManager =
(MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
@@ -276,12 +263,6 @@
R.string.pip_settings_bounds));
mMenuModePipBounds = Rect.unflattenFromString(res.getString(
R.string.pip_menu_bounds));
- mRecentsPipBounds = Rect.unflattenFromString(res.getString(
- R.string.pip_recents_bounds));
- mRecentsFocusedPipBounds = Rect.unflattenFromString(res.getString(
- R.string.pip_recents_focused_bounds));
- mRecentsFocusChangedAnimationDurationMs = res.getInteger(
- R.integer.recents_tv_pip_focus_anim_duration);
// Reset the PIP bounds and apply. PIP bounds can be changed by two reasons.
// 1. Configuration changed due to the language change (RTL <-> RTL)
@@ -295,7 +276,6 @@
*/
public void onConfigurationChanged() {
loadConfigurationsAndApply();
- mPipRecentsOverlayManager.onConfigurationChanged(mContext);
}
/**
@@ -385,8 +365,6 @@
*/
void resizePinnedStack(int state) {
if (DEBUG) Log.d(TAG, "resizePinnedStack() state=" + state);
- boolean wasRecentsShown =
- (mState == STATE_PIP_RECENTS || mState == STATE_PIP_RECENTS_FOCUSED);
boolean wasStateNoPip = (mState == STATE_NO_PIP);
mState = state;
for (int i = mListeners.size() - 1; i >= 0; --i) {
@@ -413,22 +391,12 @@
case STATE_PIP_OVERLAY:
mCurrentPipBounds = mPipBounds;
break;
- case STATE_PIP_RECENTS:
- mCurrentPipBounds = mRecentsPipBounds;
- break;
- case STATE_PIP_RECENTS_FOCUSED:
- mCurrentPipBounds = mRecentsFocusedPipBounds;
- break;
default:
mCurrentPipBounds = mPipBounds;
break;
}
try {
int animationDurationMs = -1;
- if (wasRecentsShown
- && (mState == STATE_PIP_RECENTS || mState == STATE_PIP_RECENTS_FOCUSED)) {
- animationDurationMs = mRecentsFocusChangedAnimationDurationMs;
- }
mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds,
true, true, true, animationDurationMs);
} catch (RemoteException e) {
@@ -444,23 +412,11 @@
}
/**
- * Returns the focused PIP bound while Recents is shown.
- * This is used to place PIP controls in Recents.
- */
- public Rect getRecentsFocusedPipBounds() {
- return mRecentsFocusedPipBounds;
- }
-
- /**
* Shows PIP menu UI by launching {@link PipMenuActivity}. It also locates the pinned
* stack to the centered PIP bound {@link R.config_centeredPictureInPictureBounds}.
*/
private void showPipMenu() {
if (DEBUG) Log.d(TAG, "showPipMenu()");
- if (mPipRecentsOverlayManager.isRecentsShown()) {
- if (DEBUG) Log.d(TAG, "Ignore showing PIP menu");
- return;
- }
mState = STATE_PIP_MENU;
for (int i = mListeners.size() - 1; i >= 0; --i) {
mListeners.get(i).onShowPipMenu();
@@ -692,11 +648,6 @@
mMediaSessionManager.addOnActiveSessionsChangedListener(
mActiveMediaSessionListener, null);
updateMediaController(mMediaSessionManager.getActiveSessions(null));
- if (mPipRecentsOverlayManager.isRecentsShown()) {
- // If an activity becomes PIPed again after the fullscreen, the Recents is shown
- // behind so we need to resize the pinned stack and show the correct overlay.
- resizePinnedStack(STATE_PIP_RECENTS);
- }
for (int i = mListeners.size() - 1; i >= 0; i--) {
mListeners.get(i).onPipEntered();
}
@@ -721,18 +672,7 @@
}
switch (mState) {
case STATE_PIP_OVERLAY:
- if (!mPipRecentsOverlayManager.isRecentsShown()) {
- showPipOverlay();
- break;
- } else {
- // This happens only if an activity is PIPed after the Recents is shown.
- // See {@link PipRecentsOverlayManager.requestFocus} for more details.
- resizePinnedStack(mState);
- break;
- }
- case STATE_PIP_RECENTS:
- case STATE_PIP_RECENTS_FOCUSED:
- mPipRecentsOverlayManager.addPipRecentsOverlayView();
+ showPipOverlay();
break;
case STATE_PIP_MENU:
showPipMenu();
@@ -780,13 +720,6 @@
return sPipManager;
}
- /**
- * Gets an instance of {@link PipRecentsOverlayManager}.
- */
- public PipRecentsOverlayManager getPipRecentsOverlayManager() {
- return mPipRecentsOverlayManager;
- }
-
private void updatePipVisibility(final boolean visible) {
SystemServicesProxy.getInstance(mContext).setTvPipVisibility(visible);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipRecentsControlsView.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipRecentsControlsView.java
deleted file mode 100644
index a891d12..0000000
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipRecentsControlsView.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.pip.tv;
-
-import android.animation.Animator;
-import android.animation.AnimatorInflater;
-import android.animation.AnimatorSet;
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import com.android.systemui.R;
-
-/**
- * An FrameLayout that contains {@link PipControlsView} with its scrim.
- */
-public class PipRecentsControlsView extends FrameLayout {
- /**
- * An interface to listen user action.
- */
- public interface Listener extends PipControlsView.Listener {
- /**
- * Called when an user presses BACK key and up.
- */
- abstract void onBackPressed();
- }
-
- private final PipManager mPipManager = PipManager.getInstance();
- private PipControlsView mPipControlsView;
- private View mScrim;
- private Animator mFocusGainAnimator;
- private AnimatorSet mFocusLossAnimatorSet;
-
- public PipRecentsControlsView(Context context) {
- this(context, null, 0, 0);
- }
-
- public PipRecentsControlsView(Context context, AttributeSet attrs) {
- this(context, attrs, 0, 0);
- }
-
- public PipRecentsControlsView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public PipRecentsControlsView(
- Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- @Override
- public void onFinishInflate() {
- super.onFinishInflate();
-
- mPipControlsView = (PipControlsView) findViewById(R.id.pip_control_contents);
- mScrim = findViewById(R.id.scrim);
-
- mFocusGainAnimator = loadAnimator(mPipControlsView,
- R.anim.tv_pip_controls_in_recents_focus_gain_animation);
-
- mFocusLossAnimatorSet = new AnimatorSet();
- mFocusLossAnimatorSet.playSequentially(
- loadAnimator(mPipControlsView,
- R.anim.tv_pip_controls_in_recents_focus_loss_animation),
- loadAnimator(mScrim, R.anim.tv_pip_controls_in_recents_scrim_fade_in_animation));
-
- Rect pipBounds = mPipManager.getRecentsFocusedPipBounds();
- setPadding(0, pipBounds.bottom, 0, 0);
- }
-
- private Animator loadAnimator(View view, int animatorResId) {
- Animator animator = AnimatorInflater.loadAnimator(getContext(), animatorResId);
- animator.setTarget(view);
- return animator;
- }
-
- /**
- * Starts focus gain animation.
- */
- public void startFocusGainAnimation() {
- // Hides the scrim view as soon as possible, before the PIP resize animation starts.
- // If we don't, PIP will be moved down a bit and a gap between the scrim and PIP will be
- // shown at the bottom of the PIP.
- mScrim.setAlpha(0);
- PipControlButtonView focus = mPipControlsView.getFocusedButton();
- if (focus != null) {
- focus.startFocusGainAnimation();
- }
- startAnimator(mFocusGainAnimator, mFocusLossAnimatorSet);
- }
-
- /**
- * Starts focus loss animation.
- */
- public void startFocusLossAnimation() {
- PipControlButtonView focus = mPipControlsView.getFocusedButton();
- if (focus != null) {
- focus.startFocusLossAnimation();
- }
- startAnimator(mFocusLossAnimatorSet, mFocusGainAnimator);
- }
-
- /**
- * Resets the view to the initial state. (i.e. end of the focus gain)
- */
- public void reset() {
- cancelAnimator(mFocusGainAnimator);
- cancelAnimator(mFocusLossAnimatorSet);
-
- // Reset to initial state (i.e. end of focused)
- mScrim.setAlpha(0);
- mPipControlsView.setTranslationY(0);
- mPipControlsView.setScaleX(1);
- mPipControlsView.setScaleY(1);
- mPipControlsView.reset();
- }
-
- private static void startAnimator(Animator animator, Animator previousAnimator) {
- cancelAnimator(previousAnimator);
- if (!animator.isStarted()) {
- animator.start();
- }
- }
-
- private static void cancelAnimator(Animator animator) {
- if (animator.isStarted()) {
- animator.cancel();
- }
- }
-
- /**
- * Sets listeners.
- */
- public void setListener(Listener listener) {
- mPipControlsView.setListener(listener);
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- if (!event.isCanceled()) {
- if (event.getKeyCode() == KeyEvent.KEYCODE_BACK
- && event.getAction() == KeyEvent.ACTION_UP) {
- if (mPipControlsView.mListener != null) {
- ((PipRecentsControlsView.Listener) mPipControlsView.mListener).onBackPressed();
- }
- return true;
- } else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN) {
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- mPipManager.getPipRecentsOverlayManager().clearFocus();
- }
- // Consume the down event always to prevent warning logs from ViewRootImpl.
- return true;
- }
- }
- return super.dispatchKeyEvent(event);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipRecentsOverlayManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipRecentsOverlayManager.java
deleted file mode 100644
index 835bcbc..0000000
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipRecentsOverlayManager.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.pip.tv;
-
-import android.content.Context;
-import android.graphics.PixelFormat;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-import android.view.accessibility.AccessibilityEvent;
-
-import com.android.systemui.R;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-
-import static android.view.Gravity.CENTER_HORIZONTAL;
-import static android.view.Gravity.TOP;
-import static android.view.View.MeasureSpec.UNSPECIFIED;
-
-public class PipRecentsOverlayManager {
- private static final String TAG = "PipRecentsOverlayManager";
-
- public interface Callback {
- void onClosed();
- void onBackPressed();
- void onRecentsFocused();
- }
-
- private final PipManager mPipManager = PipManager.getInstance();
- private final WindowManager mWindowManager;
- private final SystemServicesProxy mSystemServicesProxy;
- private View mOverlayView;
- private PipRecentsControlsView mPipControlsView;
- private View mRecentsView;
- private boolean mTalkBackEnabled;
-
- private LayoutParams mPipRecentsControlsViewLayoutParams;
- private LayoutParams mPipRecentsControlsViewFocusedLayoutParams;
-
- private boolean mHasFocusableInRecents;
- private boolean mIsPipRecentsOverlayShown;
- private boolean mIsRecentsShown;
- private boolean mIsPipFocusedInRecent;
- private Callback mCallback;
- private PipRecentsControlsView.Listener mPipControlsViewListener =
- new PipRecentsControlsView.Listener() {
- @Override
- public void onClosed() {
- if (mCallback != null) {
- mCallback.onClosed();
- }
- }
-
- @Override
- public void onBackPressed() {
- if (mCallback != null) {
- mCallback.onBackPressed();
- }
- }
- };
-
- PipRecentsOverlayManager(Context context) {
- mWindowManager = (WindowManager) context.getSystemService(WindowManager.class);
- mSystemServicesProxy = SystemServicesProxy.getInstance(context);
- initViews(context);
- }
-
- private void initViews(Context context) {
- LayoutInflater inflater = (LayoutInflater) context
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mOverlayView = inflater.inflate(R.layout.tv_pip_recents_overlay, null);
- mPipControlsView = (PipRecentsControlsView) mOverlayView.findViewById(R.id.pip_controls);
- mRecentsView = mOverlayView.findViewById(R.id.recents);
- mRecentsView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- if (hasFocus) {
- clearFocus();
- }
- }
- });
-
- mOverlayView.measure(UNSPECIFIED, UNSPECIFIED);
- mPipRecentsControlsViewLayoutParams = new WindowManager.LayoutParams(
- mOverlayView.getMeasuredWidth(), mOverlayView.getMeasuredHeight(),
- LayoutParams.TYPE_SYSTEM_DIALOG,
- LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCHABLE,
- PixelFormat.TRANSLUCENT);
- mPipRecentsControlsViewLayoutParams.gravity = TOP | CENTER_HORIZONTAL;
- mPipRecentsControlsViewFocusedLayoutParams = new WindowManager.LayoutParams(
- mOverlayView.getMeasuredWidth(), mOverlayView.getMeasuredHeight(),
- LayoutParams.TYPE_SYSTEM_DIALOG,
- 0,
- PixelFormat.TRANSLUCENT);
- mPipRecentsControlsViewFocusedLayoutParams.gravity = TOP | CENTER_HORIZONTAL;
- }
-
- /**
- * Add Recents overlay view.
- * This is expected to be called after the PIP animation is over.
- */
- void addPipRecentsOverlayView() {
- if (mIsPipRecentsOverlayShown) {
- return;
- }
- mTalkBackEnabled = mSystemServicesProxy.isTouchExplorationEnabled();
- mRecentsView.setVisibility(mTalkBackEnabled ? View.VISIBLE : View.GONE);
- mIsPipRecentsOverlayShown = true;
- mIsPipFocusedInRecent = true;
- mWindowManager.addView(mOverlayView, mPipRecentsControlsViewFocusedLayoutParams);
- }
-
- /**
- * Remove Recents overlay view.
- * This should be called when Recents or PIP is closed.
- */
- public void removePipRecentsOverlayView() {
- if (!mIsPipRecentsOverlayShown) {
- return;
- }
- mWindowManager.removeView(mOverlayView);
- // Resets the controls view when its removed.
- // If not, changing focus in reset will be show animation when Recents is resumed.
- mPipControlsView.reset();
- mIsPipRecentsOverlayShown = false;
- }
-
- /**
- * Request focus to the PIP Recents overlay.
- * This should be called only by {@link com.android.systemui.recents.tv.RecentsTvActivity}.
- * @param hasFocusableInRecents {@code true} if Recents can have focus. (i.e. Has a recent task)
- */
- public void requestFocus(boolean hasFocusableInRecents) {
- mHasFocusableInRecents = hasFocusableInRecents;
- if (!mIsPipRecentsOverlayShown || !mIsRecentsShown || mIsPipFocusedInRecent
- || !mPipManager.isPipShown()) {
- return;
- }
- mIsPipFocusedInRecent = true;
- mPipControlsView.startFocusGainAnimation();
- mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewFocusedLayoutParams);
- mPipManager.resizePinnedStack(PipManager.STATE_PIP_RECENTS_FOCUSED);
- if (mTalkBackEnabled) {
- mPipControlsView.requestFocus();
- mPipControlsView.sendAccessibilityEvent(
- AccessibilityEvent.TYPE_VIEW_FOCUSED);
- }
- }
-
- /**
- * Request focus to the PIP Recents overlay.
- */
- public void clearFocus() {
- if (!mIsPipRecentsOverlayShown || !mIsRecentsShown || !mIsPipFocusedInRecent
- || !mPipManager.isPipShown() || !mHasFocusableInRecents) {
- return;
- }
- mIsPipFocusedInRecent = false;
- mPipControlsView.startFocusLossAnimation();
- mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewLayoutParams);
- mPipManager.resizePinnedStack(PipManager.STATE_PIP_RECENTS);
- if (mCallback != null) {
- mCallback.onRecentsFocused();
- }
- }
-
- public void setCallback(Callback listener) {
- mCallback = listener;
- mPipControlsView.setListener(mCallback != null ? mPipControlsViewListener : null);
- }
-
- /**
- * Called when Recents is resumed.
- * PIPed activity will be resized accordingly and overlay will show available buttons.
- */
- public void onRecentsResumed() {
- if (!mPipManager.isPipShown()) {
- return;
- }
- mIsRecentsShown = true;
- mIsPipFocusedInRecent = true;
- mPipManager.resizePinnedStack(PipManager.STATE_PIP_RECENTS_FOCUSED);
- // Overlay view will be added after the resize animation ends, if any.
- }
-
- /**
- * Called when Recents is paused.
- * PIPed activity will be resized accordingly and overlay will hide available buttons.
- */
- public void onRecentsPaused() {
- mIsRecentsShown = false;
- mIsPipFocusedInRecent = false;
- removePipRecentsOverlayView();
-
- if (mPipManager.isPipShown()) {
- mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY);
- }
- }
-
- /**
- * Returns {@code true} if recents is shown.
- */
- boolean isRecentsShown() {
- return mIsRecentsShown;
- }
-
- /**
- * Updates the PIP per configuration changed.
- */
- void onConfigurationChanged(Context context) {
- if (mIsRecentsShown) {
- Log.w(TAG, "Configuration is changed while Recents is shown");
- }
- initViews(context);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
index 79f78c9..07ac52d 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
@@ -62,10 +62,10 @@
final PluginHandler mPluginHandler;
private final boolean isDebuggable;
private final PackageManager mPm;
- private final PluginManager mManager;
+ private final PluginManagerImpl mManager;
PluginInstanceManager(Context context, String action, PluginListener<T> listener,
- boolean allowMultiple, Looper looper, VersionInfo version, PluginManager manager) {
+ boolean allowMultiple, Looper looper, VersionInfo version, PluginManagerImpl manager) {
this(context, context.getPackageManager(), action, listener, allowMultiple, looper, version,
manager, Build.IS_DEBUGGABLE);
}
@@ -73,7 +73,7 @@
@VisibleForTesting
PluginInstanceManager(Context context, PackageManager pm, String action,
PluginListener<T> listener, boolean allowMultiple, Looper looper, VersionInfo version,
- PluginManager manager, boolean debuggable) {
+ PluginManagerImpl manager, boolean debuggable) {
mMainHandler = new MainHandler(Looper.getMainLooper());
mPluginHandler = new PluginHandler(looper);
mManager = manager;
@@ -346,7 +346,7 @@
.setContentText("Check to see if an OTA is available.\n"
+ e.getMessage());
}
- Intent i = new Intent(PluginManager.DISABLE_PLUGIN).setData(
+ Intent i = new Intent(PluginManagerImpl.DISABLE_PLUGIN).setData(
Uri.parse("package://" + component.flattenToString()));
PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, 0);
nb.addAction(new Action.Builder(null, "Disable plugin", pi).build());
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java
index 9ad862d..298eaf1 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 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
@@ -14,276 +14,33 @@
package com.android.systemui.plugins;
-import android.app.Notification;
-import android.app.Notification.Action;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.SystemProperties;
-import android.os.UserHandle;
import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.systemui.Dependency;
-import com.android.systemui.plugins.PluginInstanceManager.PluginContextWrapper;
-import com.android.systemui.plugins.PluginInstanceManager.PluginInfo;
import com.android.systemui.plugins.annotations.ProvidesInterface;
-import dalvik.system.PathClassLoader;
+public interface PluginManager {
-import java.lang.Thread.UncaughtExceptionHandler;
-import java.util.Map;
-
-/**
- * @see Plugin
- */
-public class PluginManager extends BroadcastReceiver {
-
- public static final String PLUGIN_CHANGED = "com.android.systemui.action.PLUGIN_CHANGED";
-
- static final String DISABLE_PLUGIN = "com.android.systemui.action.DISABLE_PLUGIN";
+ String PLUGIN_CHANGED = "com.android.systemui.action.PLUGIN_CHANGED";
// must be one of the channels created in NotificationChannels.java
- static final String NOTIFICATION_CHANNEL_ID = "ALR";
+ String NOTIFICATION_CHANNEL_ID = "ALR";
- private static PluginManager sInstance;
+ <T extends Plugin> T getOneShotPlugin(Class<T> cls);
+ <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls);
- private final HandlerThread mBackgroundThread;
- private final ArrayMap<PluginListener<?>, PluginInstanceManager> mPluginMap
- = new ArrayMap<>();
- private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>();
- private final ArraySet<String> mOneShotPackages = new ArraySet<>();
- private final Context mContext;
- private final PluginInstanceManagerFactory mFactory;
- private final boolean isDebuggable;
- private final PluginPrefs mPluginPrefs;
- private ClassLoaderFilter mParentClassLoader;
- private boolean mListening;
- private boolean mHasOneShot;
+ <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls);
+ <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
+ boolean allowMultiple);
+ <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
+ Class<?> cls);
+ <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
+ Class cls, boolean allowMultiple);
- public PluginManager(Context context) {
- this(context, new PluginInstanceManagerFactory(),
- Build.IS_DEBUGGABLE, Thread.getDefaultUncaughtExceptionHandler());
- }
+ void removePluginListener(PluginListener<?> listener);
- @VisibleForTesting
- PluginManager(Context context, PluginInstanceManagerFactory factory, boolean debuggable,
- UncaughtExceptionHandler defaultHandler) {
- mContext = context;
- mFactory = factory;
- mBackgroundThread = new HandlerThread("Plugins");
- mBackgroundThread.start();
- isDebuggable = debuggable;
- mPluginPrefs = new PluginPrefs(mContext);
+ <T> boolean dependsOn(Plugin p, Class<T> cls);
- PluginExceptionHandler uncaughtExceptionHandler = new PluginExceptionHandler(
- defaultHandler);
- Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler);
- if (isDebuggable) {
- new Handler(mBackgroundThread.getLooper()).post(() -> {
- // Plugin dependencies that don't have another good home can go here, but
- // dependencies that have better places to init can happen elsewhere.
- Dependency.get(PluginDependencyProvider.class)
- .allowPluginDependency(ActivityStarter.class);
- });
- }
- }
-
- public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
- ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
- if (info == null) {
- throw new RuntimeException(cls + " doesn't provide an interface");
- }
- if (TextUtils.isEmpty(info.action())) {
- throw new RuntimeException(cls + " doesn't provide an action");
- }
- return getOneShotPlugin(info.action(), cls);
- }
-
- public <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls) {
- if (!isDebuggable) {
- // Never ever ever allow these on production builds, they are only for prototyping.
- return null;
- }
- if (Looper.myLooper() != Looper.getMainLooper()) {
- throw new RuntimeException("Must be called from UI thread");
- }
- PluginInstanceManager<T> p = mFactory.createPluginInstanceManager(mContext, action, null,
- false, mBackgroundThread.getLooper(), cls, this);
- mPluginPrefs.addAction(action);
- PluginInfo<T> info = p.getPlugin();
- if (info != null) {
- mOneShotPackages.add(info.mPackage);
- mHasOneShot = true;
- startListening();
- return info.mPlugin;
- }
- return null;
- }
-
- public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls) {
- addPluginListener(listener, cls, false);
- }
-
- public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
- boolean allowMultiple) {
- addPluginListener(getAction(cls), listener, cls, allowMultiple);
- }
-
- public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
- Class<?> cls) {
- addPluginListener(action, listener, cls, false);
- }
-
- public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
- Class cls, boolean allowMultiple) {
- if (!isDebuggable) {
- // Never ever ever allow these on production builds, they are only for prototyping.
- return;
- }
- mPluginPrefs.addAction(action);
- PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, listener,
- allowMultiple, mBackgroundThread.getLooper(), cls, this);
- p.loadAll();
- mPluginMap.put(listener, p);
- startListening();
- }
-
- public void removePluginListener(PluginListener<?> listener) {
- if (!isDebuggable) {
- // Never ever ever allow these on production builds, they are only for prototyping.
- return;
- }
- if (!mPluginMap.containsKey(listener)) return;
- mPluginMap.remove(listener).destroy();
- stopListening();
- }
-
- private void startListening() {
- if (mListening) return;
- mListening = true;
- IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
- filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addAction(PLUGIN_CHANGED);
- filter.addAction(DISABLE_PLUGIN);
- filter.addDataScheme("package");
- mContext.registerReceiver(this, filter);
- filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
- mContext.registerReceiver(this, filter);
- }
-
- private void stopListening() {
- // Never stop listening if a one-shot is present.
- if (!mListening || mHasOneShot) return;
- mListening = false;
- mContext.unregisterReceiver(this);
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
- for (PluginInstanceManager manager : mPluginMap.values()) {
- manager.loadAll();
- }
- } else if (DISABLE_PLUGIN.equals(intent.getAction())) {
- Uri uri = intent.getData();
- ComponentName component = ComponentName.unflattenFromString(
- uri.toString().substring(10));
- mContext.getPackageManager().setComponentEnabledSetting(component,
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
- PackageManager.DONT_KILL_APP);
- mContext.getSystemService(NotificationManager.class).cancel(component.getClassName(),
- SystemMessage.NOTE_PLUGIN);
- } else {
- Uri data = intent.getData();
- String pkg = data.getEncodedSchemeSpecificPart();
- if (mOneShotPackages.contains(pkg)) {
- int icon = mContext.getResources().getIdentifier("tuner", "drawable",
- mContext.getPackageName());
- int color = Resources.getSystem().getIdentifier(
- "system_notification_accent_color", "color", "android");
- String label = pkg;
- try {
- PackageManager pm = mContext.getPackageManager();
- label = pm.getApplicationInfo(pkg, 0).loadLabel(pm).toString();
- } catch (NameNotFoundException e) {
- }
- // Localization not required as this will never ever appear in a user build.
- final Notification.Builder nb =
- new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
- .setSmallIcon(icon)
- .setWhen(0)
- .setShowWhen(false)
- .setPriority(Notification.PRIORITY_MAX)
- .setVisibility(Notification.VISIBILITY_PUBLIC)
- .setColor(mContext.getColor(color))
- .setContentTitle("Plugin \"" + label + "\" has updated")
- .setContentText("Restart SysUI for changes to take effect.");
- Intent i = new Intent("com.android.systemui.action.RESTART").setData(
- Uri.parse("package://" + pkg));
- PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, 0);
- nb.addAction(new Action.Builder(null, "Restart SysUI", pi).build());
- mContext.getSystemService(NotificationManager.class).notifyAsUser(pkg,
- SystemMessage.NOTE_PLUGIN, nb.build(), UserHandle.ALL);
- }
- clearClassLoader(pkg);
- if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
- for (PluginInstanceManager manager : mPluginMap.values()) {
- manager.onPackageChange(pkg);
- }
- } else {
- for (PluginInstanceManager manager : mPluginMap.values()) {
- manager.onPackageRemoved(pkg);
- }
- }
- }
- }
-
- public ClassLoader getClassLoader(String sourceDir, String pkg) {
- if (mClassLoaders.containsKey(pkg)) {
- return mClassLoaders.get(pkg);
- }
- ClassLoader classLoader = new PathClassLoader(sourceDir, getParentClassLoader());
- mClassLoaders.put(pkg, classLoader);
- return classLoader;
- }
-
- private void clearClassLoader(String pkg) {
- mClassLoaders.remove(pkg);
- }
-
- ClassLoader getParentClassLoader() {
- if (mParentClassLoader == null) {
- // Lazily load this so it doesn't have any effect on devices without plugins.
- mParentClassLoader = new ClassLoaderFilter(getClass().getClassLoader(),
- "com.android.systemui.plugin");
- }
- return mParentClassLoader;
- }
-
- public Context getContext(ApplicationInfo info, String pkg) throws NameNotFoundException {
- ClassLoader classLoader = getClassLoader(info.sourceDir, pkg);
- return new PluginContextWrapper(mContext.createApplicationContext(info, 0), classLoader);
- }
-
- public static <P> String getAction(Class<P> cls) {
+ static <P> String getAction(Class<P> cls) {
ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
if (info == null) {
throw new RuntimeException(cls + " doesn't provide an interface");
@@ -293,82 +50,4 @@
}
return info.action();
}
-
- public <T> boolean dependsOn(Plugin p, Class<T> cls) {
- for (int i = 0; i < mPluginMap.size(); i++) {
- if (mPluginMap.valueAt(i).dependsOn(p, cls)) {
- return true;
- }
- }
- return false;
- }
-
- @VisibleForTesting
- public static class PluginInstanceManagerFactory {
- public <T extends Plugin> PluginInstanceManager createPluginInstanceManager(Context context,
- String action, PluginListener<T> listener, boolean allowMultiple, Looper looper,
- Class<?> cls, PluginManager manager) {
- return new PluginInstanceManager(context, action, listener, allowMultiple, looper,
- new VersionInfo().addClass(cls), manager);
- }
- }
-
- // This allows plugins to include any libraries or copied code they want by only including
- // classes from the plugin library.
- private static class ClassLoaderFilter extends ClassLoader {
- private final String mPackage;
- private final ClassLoader mBase;
-
- public ClassLoaderFilter(ClassLoader base, String pkg) {
- super(ClassLoader.getSystemClassLoader());
- mBase = base;
- mPackage = pkg;
- }
-
- @Override
- protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
- if (!name.startsWith(mPackage)) super.loadClass(name, resolve);
- return mBase.loadClass(name);
- }
- }
-
- private class PluginExceptionHandler implements UncaughtExceptionHandler {
- private final UncaughtExceptionHandler mHandler;
-
- private PluginExceptionHandler(UncaughtExceptionHandler handler) {
- mHandler = handler;
- }
-
- @Override
- public void uncaughtException(Thread thread, Throwable throwable) {
- if (SystemProperties.getBoolean("plugin.debugging", false)) {
- mHandler.uncaughtException(thread, throwable);
- return;
- }
- // Search for and disable plugins that may have been involved in this crash.
- boolean disabledAny = checkStack(throwable);
- if (!disabledAny) {
- // We couldn't find any plugins involved in this crash, just to be safe
- // disable all the plugins, so we can be sure that SysUI is running as
- // best as possible.
- for (PluginInstanceManager manager : mPluginMap.values()) {
- manager.disableAll();
- }
- }
-
- // Run the normal exception handler so we can crash and cleanup our state.
- mHandler.uncaughtException(thread, throwable);
- }
-
- private boolean checkStack(Throwable throwable) {
- if (throwable == null) return false;
- boolean disabledAny = false;
- for (StackTraceElement element : throwable.getStackTrace()) {
- for (PluginInstanceManager manager : mPluginMap.values()) {
- disabledAny |= manager.checkAndDisable(element.getClassName());
- }
- }
- return disabledAny | checkStack(throwable.getCause());
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
new file mode 100644
index 0000000..1fb6c87
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.plugins;
+
+import android.app.Notification;
+import android.app.Notification.Action;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.systemui.Dependency;
+import com.android.systemui.plugins.PluginInstanceManager.PluginContextWrapper;
+import com.android.systemui.plugins.PluginInstanceManager.PluginInfo;
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+import dalvik.system.PathClassLoader;
+
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.util.Map;
+
+/**
+ * @see Plugin
+ */
+public class PluginManagerImpl extends BroadcastReceiver implements PluginManager {
+
+ static final String DISABLE_PLUGIN = "com.android.systemui.action.DISABLE_PLUGIN";
+
+ private static PluginManager sInstance;
+
+ private final HandlerThread mBackgroundThread;
+ private final ArrayMap<PluginListener<?>, PluginInstanceManager> mPluginMap
+ = new ArrayMap<>();
+ private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>();
+ private final ArraySet<String> mOneShotPackages = new ArraySet<>();
+ private final Context mContext;
+ private final PluginInstanceManagerFactory mFactory;
+ private final boolean isDebuggable;
+ private final PluginPrefs mPluginPrefs;
+ private ClassLoaderFilter mParentClassLoader;
+ private boolean mListening;
+ private boolean mHasOneShot;
+
+ public PluginManagerImpl(Context context) {
+ this(context, new PluginInstanceManagerFactory(),
+ Build.IS_DEBUGGABLE, Thread.getDefaultUncaughtExceptionHandler());
+ }
+
+ @VisibleForTesting
+ PluginManagerImpl(Context context, PluginInstanceManagerFactory factory, boolean debuggable,
+ UncaughtExceptionHandler defaultHandler) {
+ mContext = context;
+ mFactory = factory;
+ mBackgroundThread = new HandlerThread("Plugins");
+ mBackgroundThread.start();
+ isDebuggable = debuggable;
+ mPluginPrefs = new PluginPrefs(mContext);
+
+ PluginExceptionHandler uncaughtExceptionHandler = new PluginExceptionHandler(
+ defaultHandler);
+ Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler);
+ if (isDebuggable) {
+ new Handler(mBackgroundThread.getLooper()).post(() -> {
+ // Plugin dependencies that don't have another good home can go here, but
+ // dependencies that have better places to init can happen elsewhere.
+ Dependency.get(PluginDependencyProvider.class)
+ .allowPluginDependency(ActivityStarter.class);
+ });
+ }
+ }
+
+ public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
+ ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
+ if (info == null) {
+ throw new RuntimeException(cls + " doesn't provide an interface");
+ }
+ if (TextUtils.isEmpty(info.action())) {
+ throw new RuntimeException(cls + " doesn't provide an action");
+ }
+ return getOneShotPlugin(info.action(), cls);
+ }
+
+ public <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls) {
+ if (!isDebuggable) {
+ // Never ever ever allow these on production builds, they are only for prototyping.
+ return null;
+ }
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ throw new RuntimeException("Must be called from UI thread");
+ }
+ PluginInstanceManager<T> p = mFactory.createPluginInstanceManager(mContext, action, null,
+ false, mBackgroundThread.getLooper(), cls, this);
+ mPluginPrefs.addAction(action);
+ PluginInfo<T> info = p.getPlugin();
+ if (info != null) {
+ mOneShotPackages.add(info.mPackage);
+ mHasOneShot = true;
+ startListening();
+ return info.mPlugin;
+ }
+ return null;
+ }
+
+ public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls) {
+ addPluginListener(listener, cls, false);
+ }
+
+ public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
+ boolean allowMultiple) {
+ addPluginListener(PluginManager.getAction(cls), listener, cls, allowMultiple);
+ }
+
+ public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
+ Class<?> cls) {
+ addPluginListener(action, listener, cls, false);
+ }
+
+ public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
+ Class cls, boolean allowMultiple) {
+ if (!isDebuggable) {
+ // Never ever ever allow these on production builds, they are only for prototyping.
+ return;
+ }
+ mPluginPrefs.addAction(action);
+ PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, listener,
+ allowMultiple, mBackgroundThread.getLooper(), cls, this);
+ p.loadAll();
+ mPluginMap.put(listener, p);
+ startListening();
+ }
+
+ public void removePluginListener(PluginListener<?> listener) {
+ if (!isDebuggable) {
+ // Never ever ever allow these on production builds, they are only for prototyping.
+ return;
+ }
+ if (!mPluginMap.containsKey(listener)) return;
+ mPluginMap.remove(listener).destroy();
+ stopListening();
+ }
+
+ private void startListening() {
+ if (mListening) return;
+ mListening = true;
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(PLUGIN_CHANGED);
+ filter.addAction(DISABLE_PLUGIN);
+ filter.addDataScheme("package");
+ mContext.registerReceiver(this, filter);
+ filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
+ mContext.registerReceiver(this, filter);
+ }
+
+ private void stopListening() {
+ // Never stop listening if a one-shot is present.
+ if (!mListening || mHasOneShot) return;
+ mListening = false;
+ mContext.unregisterReceiver(this);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
+ for (PluginInstanceManager manager : mPluginMap.values()) {
+ manager.loadAll();
+ }
+ } else if (DISABLE_PLUGIN.equals(intent.getAction())) {
+ Uri uri = intent.getData();
+ ComponentName component = ComponentName.unflattenFromString(
+ uri.toString().substring(10));
+ mContext.getPackageManager().setComponentEnabledSetting(component,
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP);
+ mContext.getSystemService(NotificationManager.class).cancel(component.getClassName(),
+ SystemMessage.NOTE_PLUGIN);
+ } else {
+ Uri data = intent.getData();
+ String pkg = data.getEncodedSchemeSpecificPart();
+ if (mOneShotPackages.contains(pkg)) {
+ int icon = mContext.getResources().getIdentifier("tuner", "drawable",
+ mContext.getPackageName());
+ int color = Resources.getSystem().getIdentifier(
+ "system_notification_accent_color", "color", "android");
+ String label = pkg;
+ try {
+ PackageManager pm = mContext.getPackageManager();
+ label = pm.getApplicationInfo(pkg, 0).loadLabel(pm).toString();
+ } catch (NameNotFoundException e) {
+ }
+ // Localization not required as this will never ever appear in a user build.
+ final Notification.Builder nb =
+ new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
+ .setSmallIcon(icon)
+ .setWhen(0)
+ .setShowWhen(false)
+ .setPriority(Notification.PRIORITY_MAX)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .setColor(mContext.getColor(color))
+ .setContentTitle("Plugin \"" + label + "\" has updated")
+ .setContentText("Restart SysUI for changes to take effect.");
+ Intent i = new Intent("com.android.systemui.action.RESTART").setData(
+ Uri.parse("package://" + pkg));
+ PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, 0);
+ nb.addAction(new Action.Builder(null, "Restart SysUI", pi).build());
+ mContext.getSystemService(NotificationManager.class).notifyAsUser(pkg,
+ SystemMessage.NOTE_PLUGIN, nb.build(), UserHandle.ALL);
+ }
+ clearClassLoader(pkg);
+ if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
+ for (PluginInstanceManager manager : mPluginMap.values()) {
+ manager.onPackageChange(pkg);
+ }
+ } else {
+ for (PluginInstanceManager manager : mPluginMap.values()) {
+ manager.onPackageRemoved(pkg);
+ }
+ }
+ }
+ }
+
+ public ClassLoader getClassLoader(String sourceDir, String pkg) {
+ if (mClassLoaders.containsKey(pkg)) {
+ return mClassLoaders.get(pkg);
+ }
+ ClassLoader classLoader = new PathClassLoader(sourceDir, getParentClassLoader());
+ mClassLoaders.put(pkg, classLoader);
+ return classLoader;
+ }
+
+ private void clearClassLoader(String pkg) {
+ mClassLoaders.remove(pkg);
+ }
+
+ ClassLoader getParentClassLoader() {
+ if (mParentClassLoader == null) {
+ // Lazily load this so it doesn't have any effect on devices without plugins.
+ mParentClassLoader = new ClassLoaderFilter(getClass().getClassLoader(),
+ "com.android.systemui.plugin");
+ }
+ return mParentClassLoader;
+ }
+
+ public Context getContext(ApplicationInfo info, String pkg) throws NameNotFoundException {
+ ClassLoader classLoader = getClassLoader(info.sourceDir, pkg);
+ return new PluginContextWrapper(mContext.createApplicationContext(info, 0), classLoader);
+ }
+
+ public <T> boolean dependsOn(Plugin p, Class<T> cls) {
+ for (int i = 0; i < mPluginMap.size(); i++) {
+ if (mPluginMap.valueAt(i).dependsOn(p, cls)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @VisibleForTesting
+ public static class PluginInstanceManagerFactory {
+ public <T extends Plugin> PluginInstanceManager createPluginInstanceManager(Context context,
+ String action, PluginListener<T> listener, boolean allowMultiple, Looper looper,
+ Class<?> cls, PluginManagerImpl manager) {
+ return new PluginInstanceManager(context, action, listener, allowMultiple, looper,
+ new VersionInfo().addClass(cls), manager);
+ }
+ }
+
+ // This allows plugins to include any libraries or copied code they want by only including
+ // classes from the plugin library.
+ private static class ClassLoaderFilter extends ClassLoader {
+ private final String mPackage;
+ private final ClassLoader mBase;
+
+ public ClassLoaderFilter(ClassLoader base, String pkg) {
+ super(ClassLoader.getSystemClassLoader());
+ mBase = base;
+ mPackage = pkg;
+ }
+
+ @Override
+ protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ if (!name.startsWith(mPackage)) super.loadClass(name, resolve);
+ return mBase.loadClass(name);
+ }
+ }
+
+ private class PluginExceptionHandler implements UncaughtExceptionHandler {
+ private final UncaughtExceptionHandler mHandler;
+
+ private PluginExceptionHandler(UncaughtExceptionHandler handler) {
+ mHandler = handler;
+ }
+
+ @Override
+ public void uncaughtException(Thread thread, Throwable throwable) {
+ if (SystemProperties.getBoolean("plugin.debugging", false)) {
+ mHandler.uncaughtException(thread, throwable);
+ return;
+ }
+ // Search for and disable plugins that may have been involved in this crash.
+ boolean disabledAny = checkStack(throwable);
+ if (!disabledAny) {
+ // We couldn't find any plugins involved in this crash, just to be safe
+ // disable all the plugins, so we can be sure that SysUI is running as
+ // best as possible.
+ for (PluginInstanceManager manager : mPluginMap.values()) {
+ manager.disableAll();
+ }
+ }
+
+ // Run the normal exception handler so we can crash and cleanup our state.
+ mHandler.uncaughtException(thread, throwable);
+ }
+
+ private boolean checkStack(Throwable throwable) {
+ if (throwable == null) return false;
+ boolean disabledAny = false;
+ for (StackTraceElement element : throwable.getStackTrace()) {
+ for (PluginInstanceManager manager : mPluginMap.values()) {
+ disabledAny |= manager.checkAndDisable(element.getClassName());
+ }
+ }
+ return disabledAny | checkStack(throwable.getCause());
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index e635162..d3e939f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -60,7 +60,6 @@
import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.recents.tv.RecentsTvImpl;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
@@ -86,7 +85,6 @@
public final static Set<String> RECENTS_ACTIVITIES = new HashSet<>();
static {
RECENTS_ACTIVITIES.add(RecentsImpl.RECENTS_ACTIVITY);
- RECENTS_ACTIVITIES.add(RecentsTvImpl.RECENTS_TV_ACTIVITY);
}
// Purely for experimentation
@@ -205,13 +203,7 @@
sTaskLoader = new RecentsTaskLoader(mContext);
sConfiguration = new RecentsConfiguration(mContext);
mHandler = new Handler();
- UiModeManager uiModeManager = (UiModeManager) mContext.
- getSystemService(Context.UI_MODE_SERVICE);
- if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
- mImpl = new RecentsTvImpl(mContext);
- } else {
- mImpl = new RecentsImpl(mContext);
- }
+ mImpl = new RecentsImpl(mContext);
// Check if there is a recents override package
if ("userdebug".equals(Build.TYPE) || "eng".equals(Build.TYPE)) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskEvent.java
deleted file mode 100644
index 04ca68f..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskEvent.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.recents.events.activity;
-
-
-import android.graphics.Rect;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.tv.views.TaskCardView;
-
-public class LaunchTvTaskEvent extends EventBus.Event {
-
- public final TaskCardView taskView;
- public final Task task;
- public final Rect targetTaskBounds;
- public final int targetTaskStack;
-
- public LaunchTvTaskEvent(TaskCardView taskView, Task task, Rect targetTaskBounds,
- int targetTaskStack) {
- this.taskView = taskView;
- this.task = task;
- this.targetTaskBounds = targetTaskBounds;
- this.targetTaskStack = targetTaskStack;
- }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskStartedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskStartedEvent.java
deleted file mode 100644
index 75d3efa..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskStartedEvent.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.tv.views.TaskCardView;
-
-/**
- * This event is sent following {@link LaunchTvTaskEvent} after the call to the system is made to
- * start the task, only used on TV.
- */
-public class LaunchTvTaskStartedEvent extends EventBus.AnimatedEvent {
-
- public final TaskCardView taskView;
-
- public LaunchTvTaskStartedEvent(TaskCardView taskView) {
- this.taskView = taskView;
- }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
deleted file mode 100644
index a691a424..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ /dev/null
@@ -1,606 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.recents.tv;
-
-import android.app.Activity;
-import android.app.ActivityOptions;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewTreeObserver.OnPreDrawListener;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityEvent;
-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;
-import com.android.systemui.recents.RecentsImpl;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
-import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
-import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
-import com.android.systemui.recents.events.activity.HideRecentsEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
-import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
-import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
-import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
-import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
-import com.android.systemui.recents.events.ui.UserInteractionEvent;
-import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.model.RecentsPackageMonitor;
-import com.android.systemui.recents.model.RecentsTaskLoadPlan;
-import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.tv.animations.HomeRecentsEnterExitAnimationHolder;
-import com.android.systemui.recents.tv.views.RecentsTvView;
-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.phone.StatusBar;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * The main TV recents activity started by the RecentsImpl.
- */
-public class RecentsTvActivity extends Activity implements OnPreDrawListener {
- private final static String TAG = "RecentsTvActivity";
- private final static boolean DEBUG = false;
-
- public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
- private final static String RECENTS_HOME_INTENT_EXTRA =
- "com.android.systemui.recents.tv.RecentsTvActivity.RECENTS_HOME_INTENT_EXTRA";
-
- private boolean mFinishedOnStartup;
- private RecentsPackageMonitor mPackageMonitor;
- private long mLastTabKeyEventTime;
- private boolean mIgnoreAltTabRelease;
- private boolean mLaunchedFromHome;
- private boolean mTalkBackEnabled;
-
- private RecentsTvView mRecentsView;
- private View mPipView;
- private TaskStackHorizontalViewAdapter mTaskStackViewAdapter;
- private TaskStackHorizontalGridView mTaskStackHorizontalGridView;
- private FinishRecentsRunnable mFinishLaunchHomeRunnable;
- private HomeRecentsEnterExitAnimationHolder mHomeRecentsEnterExitAnimationHolder;
-
- private final PipManager mPipManager = PipManager.getInstance();
- private final PipManager.Listener mPipListener = new PipManager.Listener() {
- @Override
- public void onPipEntered() {
- updatePipUI();
- }
-
- @Override
- public void onPipActivityClosed() {
- updatePipUI();
- }
-
- @Override
- public void onShowPipMenu() {
- updatePipUI();
- }
-
- @Override
- public void onMoveToFullscreen() {
- // Recents should be dismissed when PIP moves to fullscreen. If not, Recents will
- // be unnecessarily shown in the scenario: PIP->Fullscreen->PIP.
- // Do not show Recents close animation because PIP->Fullscreen animation will be shown
- // instead.
- dismissRecentsToLaunchTargetTaskOrHome(false);
- }
-
- @Override
- public void onPipResizeAboutToStart() { }
- };
- private PipRecentsOverlayManager mPipRecentsOverlayManager;
- private final PipRecentsOverlayManager.Callback mPipRecentsOverlayManagerCallback =
- new PipRecentsOverlayManager.Callback() {
- @Override
- public void onClosed() {
- dismissRecentsToLaunchTargetTaskOrHome(true);
- }
-
- @Override
- public void onBackPressed() {
- RecentsTvActivity.this.onBackPressed();
- }
-
- @Override
- public void onRecentsFocused() {
- if (mTalkBackEnabled) {
- mTaskStackHorizontalGridView.requestFocus();
- mTaskStackHorizontalGridView.sendAccessibilityEvent(
- AccessibilityEvent.TYPE_VIEW_FOCUSED);
- }
- mTaskStackHorizontalGridView.startFocusGainAnimation();
- }
- };
-
- private final View.OnFocusChangeListener mPipViewFocusChangeListener =
- new View.OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- if (hasFocus) {
- requestPipControlsFocus();
- }
- }
- };
-
- /**
- * A common Runnable to finish Recents by launching Home with an animation depending on the
- * last activity launch state. Generally we always launch home when we exit Recents rather than
- * just finishing the activity since we don't know what is behind Recents in the task stack.
- */
- class FinishRecentsRunnable implements Runnable {
- Intent mLaunchIntent;
-
- /**
- * Creates a finish runnable that starts the specified intent.
- */
- public FinishRecentsRunnable(Intent launchIntent) {
- mLaunchIntent = launchIntent;
- }
-
- @Override
- public void run() {
- try {
- ActivityOptions opts = ActivityOptions.makeCustomAnimation(RecentsTvActivity.this,
- R.anim.recents_to_launcher_enter, R.anim.recents_to_launcher_exit);
- startActivityAsUser(mLaunchIntent, opts.toBundle(), UserHandle.CURRENT);
- } catch (Exception e) {
- Log.e(TAG, getString(R.string.recents_launch_error_message, "Home"), e);
- }
- }
- }
-
- private void updateRecentsTasks() {
- RecentsTaskLoader loader = Recents.getTaskLoader();
- RecentsTaskLoadPlan plan = RecentsImpl.consumeInstanceLoadPlan();
- if (plan == null) {
- plan = loader.createLoadPlan(this);
- }
-
- RecentsConfiguration config = Recents.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- if (!plan.hasTasks()) {
- loader.preloadTasks(plan, -1, !launchState.launchedFromHome);
- }
-
- int numVisibleTasks = TaskCardView.getNumberOfVisibleTasks(getApplicationContext());
- mLaunchedFromHome = launchState.launchedFromHome;
- TaskStack stack = plan.getTaskStack();
- RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
- loadOpts.runningTaskId = launchState.launchedToTaskId;
- loadOpts.numVisibleTasks = numVisibleTasks;
- loadOpts.numVisibleTaskThumbnails = numVisibleTasks;
- loader.loadTasks(this, plan, loadOpts);
-
- List stackTasks = stack.getStackTasks();
- Collections.reverse(stackTasks);
- if (mTaskStackViewAdapter == null) {
- mTaskStackViewAdapter = new TaskStackHorizontalViewAdapter(stackTasks);
- mTaskStackHorizontalGridView = mRecentsView
- .setTaskStackViewAdapter(mTaskStackViewAdapter);
- mHomeRecentsEnterExitAnimationHolder = new HomeRecentsEnterExitAnimationHolder(
- getApplicationContext(), mTaskStackHorizontalGridView);
- } else {
- mTaskStackViewAdapter.setNewStackTasks(stackTasks);
- }
- mRecentsView.init(stack);
-
- if (launchState.launchedToTaskId != -1) {
- ArrayList<Task> tasks = stack.getStackTasks();
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task t = tasks.get(i);
- if (t.key.id == launchState.launchedToTaskId) {
- t.isLaunchTarget = true;
- break;
- }
- }
- }
- }
-
- boolean dismissRecentsToLaunchTargetTaskOrHome(boolean animate) {
- SystemServicesProxy ssp = Recents.getSystemServices();
- if (ssp.isRecentsActivityVisible()) {
- // If we have a focused Task, launch that Task now
- if (mRecentsView.launchPreviousTask(animate)) {
- return true;
- }
- // If none of the other cases apply, then just go Home
- dismissRecentsToHome(animate /* animateTaskViews */);
- }
- return false;
- }
-
- boolean dismissRecentsToFocusedTaskOrHome() {
- SystemServicesProxy ssp = Recents.getSystemServices();
- if (ssp.isRecentsActivityVisible()) {
- // If we have a focused Task, launch that Task now
- if (mRecentsView.launchFocusedTask()) return true;
- // If none of the other cases apply, then just go Home
- dismissRecentsToHome(true /* animateTaskViews */);
- return true;
- }
- return false;
- }
-
- void dismissRecentsToHome(boolean animateTaskViews) {
- Runnable closeSystemWindows = new Runnable() {
- @Override
- public void run() {
- Recents.getSystemServices().sendCloseSystemWindows(
- StatusBar.SYSTEM_DIALOG_REASON_HOME_KEY);
- }
- };
- DismissRecentsToHomeAnimationStarted dismissEvent =
- new DismissRecentsToHomeAnimationStarted(animateTaskViews);
- dismissEvent.addPostAnimationCallback(mFinishLaunchHomeRunnable);
- dismissEvent.addPostAnimationCallback(closeSystemWindows);
-
- if (mTaskStackHorizontalGridView.getChildCount() > 0 && animateTaskViews) {
- mHomeRecentsEnterExitAnimationHolder.startExitAnimation(dismissEvent);
- } else {
- closeSystemWindows.run();
- mFinishLaunchHomeRunnable.run();
- }
- }
-
- boolean dismissRecentsToHomeIfVisible(boolean animated) {
- SystemServicesProxy ssp = Recents.getSystemServices();
- if (ssp.isRecentsActivityVisible()) {
- // Return to Home
- dismissRecentsToHome(animated);
- return true;
- }
- return false;
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mFinishedOnStartup = false;
-
- // In the case that the activity starts up before the Recents component has initialized
- // (usually when debugging/pushing the SysUI apk), just finish this activity.
- SystemServicesProxy ssp = Recents.getSystemServices();
- if (ssp == null) {
- mFinishedOnStartup = true;
- finish();
- return;
- }
- mPipRecentsOverlayManager = PipManager.getInstance().getPipRecentsOverlayManager();
-
- // Register this activity with the event bus
- EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
-
- mPackageMonitor = new RecentsPackageMonitor();
- mPackageMonitor.register(this);
-
- // Set the Recents layout
- setContentView(R.layout.recents_on_tv);
-
- mRecentsView = (RecentsTvView) findViewById(R.id.recents_view);
- mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
-
- mPipView = findViewById(R.id.pip);
- mPipView.setOnFocusChangeListener(mPipViewFocusChangeListener);
- // Place mPipView at the PIP bounds for fine tuned focus handling.
- Rect pipBounds = mPipManager.getRecentsFocusedPipBounds();
- LayoutParams lp = (LayoutParams) mPipView.getLayoutParams();
- lp.width = pipBounds.width();
- lp.height = pipBounds.height();
- lp.leftMargin = pipBounds.left;
- lp.topMargin = pipBounds.top;
- mPipView.setLayoutParams(lp);
-
- mPipRecentsOverlayManager.setCallback(mPipRecentsOverlayManagerCallback);
-
- getWindow().getAttributes().privateFlags |=
- WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
-
- // Create the home intent runnable
- Intent homeIntent = new Intent(Intent.ACTION_MAIN, null);
- homeIntent.addCategory(Intent.CATEGORY_HOME);
- homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- homeIntent.putExtra(RECENTS_HOME_INTENT_EXTRA, true);
- mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent);
-
- mPipManager.addListener(mPipListener);
- }
-
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- setIntent(intent);
- }
-
- @Override
- public void onEnterAnimationComplete() {
- super.onEnterAnimationComplete();
- if(mLaunchedFromHome) {
- mHomeRecentsEnterExitAnimationHolder.startEnterAnimation(mPipManager.isPipShown());
- }
- EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mPipRecentsOverlayManager.onRecentsResumed();
- // Update the recent tasks
- updateRecentsTasks();
-
- // If this is a new instance from a configuration change, then we have to manually trigger
- // the enter animation state, or if recents was relaunched by AM, without going through
- // the normal mechanisms
- RecentsConfiguration config = Recents.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- boolean wasLaunchedByAm = !launchState.launchedFromHome &&
- !launchState.launchedFromApp;
- if (wasLaunchedByAm) {
- EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
- }
-
- // Notify that recents is now visible
- SystemServicesProxy ssp = Recents.getSystemServices();
- EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
- if(mTaskStackHorizontalGridView.getStack().getTaskCount() > 1 && !mLaunchedFromHome) {
- // If there are 2 or more tasks, and we are not launching from home
- // set the selected position to the 2nd task to allow for faster app switching
- mTaskStackHorizontalGridView.setSelectedPosition(1);
- } else {
- mTaskStackHorizontalGridView.setSelectedPosition(0);
- }
- mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
-
- View dismissPlaceholder = findViewById(R.id.dismiss_placeholder);
- mTalkBackEnabled = ssp.isTouchExplorationEnabled();
- if (mTalkBackEnabled) {
- dismissPlaceholder.setAccessibilityTraversalBefore(R.id.task_list);
- dismissPlaceholder.setAccessibilityTraversalAfter(R.id.dismiss_placeholder);
- mTaskStackHorizontalGridView.setAccessibilityTraversalAfter(R.id.dismiss_placeholder);
- mTaskStackHorizontalGridView.setAccessibilityTraversalBefore(R.id.pip);
- dismissPlaceholder.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mTaskStackHorizontalGridView.requestFocus();
- mTaskStackHorizontalGridView.
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
- Task focusedTask = mTaskStackHorizontalGridView.getFocusedTask();
- if (focusedTask != null) {
- mTaskStackViewAdapter.removeTask(focusedTask);
- EventBus.getDefault().send(new DeleteTaskDataEvent(focusedTask));
- }
- }
- });
- }
-
- // Initialize PIP UI
- if (mPipManager.isPipShown()) {
- if (mTalkBackEnabled) {
- // If talkback is on, use the mPipView to handle focus changes
- // between recents row and PIP controls.
- mPipView.setVisibility(View.VISIBLE);
- } else {
- mPipView.setVisibility(View.GONE);
- }
- // When PIP view has focus, recents overlay view will takes the focus
- // as if it's the part of the Recents UI.
- mPipRecentsOverlayManager.requestFocus(mTaskStackViewAdapter.getItemCount() > 0);
- } else {
- mPipView.setVisibility(View.GONE);
- mPipRecentsOverlayManager.removePipRecentsOverlayView();
- }
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mPipRecentsOverlayManager.onRecentsPaused();
- }
-
- @Override
- protected void onStop() {
- super.onStop();
-
- mIgnoreAltTabRelease = false;
- // Notify that recents is now hidden
- EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
-
- // Workaround for b/22542869, if the RecentsActivity is started again, but without going
- // through SystemUI, we need to reset the config launch flags to ensure that we do not
- // wait on the system to send a signal that was never queued.
- RecentsConfiguration config = Recents.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- launchState.reset();
-
- // Workaround for b/28333917.
- finish();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
-
- mPipManager.removeListener(mPipListener);
- // In the case that the activity finished on startup, just skip the unregistration below
- if (mFinishedOnStartup) {
- return;
- }
-
- // Unregister any broadcast receivers for the task loader
- mPackageMonitor.unregister();
-
- EventBus.getDefault().unregister(this);
- }
-
- @Override
- public void onTrimMemory(int level) {
- RecentsTaskLoader loader = Recents.getTaskLoader();
- if (loader != null) {
- loader.onTrimMemory(level);
- }
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_DEL:
- case KeyEvent.KEYCODE_FORWARD_DEL: {
- EventBus.getDefault().send(new DismissFocusedTaskViewEvent());
- return true;
- }
- default:
- break;
- }
- return super.onKeyDown(keyCode, event);
- }
-
- @Override
- public void onUserInteraction() {
- EventBus.getDefault().send(new UserInteractionEvent());
- }
-
- @Override
- public void onBackPressed() {
- // Back behaves like the recents button so just trigger a toggle event
- EventBus.getDefault().send(new ToggleRecentsEvent());
- }
-
- /**** EventBus events ****/
-
- public final void onBusEvent(ToggleRecentsEvent event) {
- RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
- if (launchState.launchedFromHome) {
- dismissRecentsToHome(true /* animateTaskViews */);
- } else {
- dismissRecentsToLaunchTargetTaskOrHome(true);
- }
- }
-
- public final void onBusEvent(HideRecentsEvent event) {
- if (event.triggeredFromAltTab) {
- // If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app
- if (!mIgnoreAltTabRelease) {
- dismissRecentsToFocusedTaskOrHome();
- }
- } else if (event.triggeredFromHomeKey) {
- dismissRecentsToHome(true /* animateTaskViews */);
- } else {
- // Do nothing
- }
- }
-
- public final void onBusEvent(CancelEnterRecentsWindowAnimationEvent event) {
- RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
- int launchToTaskId = launchState.launchedToTaskId;
- if (launchToTaskId != -1 &&
- (event.launchTask == null || launchToTaskId != event.launchTask.key.id)) {
- SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.cancelWindowTransition(launchState.launchedToTaskId);
- ssp.cancelThumbnailTransition(getTaskId());
- }
- }
-
- public final void onBusEvent(DeleteTaskDataEvent event) {
- // Remove any stored data from the loader
- RecentsTaskLoader loader = Recents.getTaskLoader();
- loader.deleteTaskData(event.task, false);
-
- // Remove the task from activity manager
- SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.removeTask(event.task.key.id);
- }
-
- public final void onBusEvent(AllTaskViewsDismissedEvent event) {
- if (mPipManager.isPipShown()) {
- mRecentsView.showEmptyView();
- mPipRecentsOverlayManager.requestFocus(false);
- } else {
- dismissRecentsToHome(false);
- }
- }
-
- public final void onBusEvent(LaunchTaskFailedEvent event) {
- // Return to Home
- dismissRecentsToHome(true /* animateTaskViews */);
- }
-
- @Override
- public boolean onPreDraw() {
- mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
- // Sets the initial values for enter animation.
- // Animation will be started in {@link #onEnterAnimationComplete()}
- if (mLaunchedFromHome) {
- mHomeRecentsEnterExitAnimationHolder
- .setEnterFromHomeStartingAnimationValues(mPipManager.isPipShown());
- } else {
- mHomeRecentsEnterExitAnimationHolder
- .setEnterFromAppStartingAnimationValues(mPipManager.isPipShown());
- }
- // We post to make sure that this information is delivered after this traversals is
- // finished.
- mRecentsView.post(new Runnable() {
- @Override
- public void run() {
- Recents.getSystemServices().endProlongedAnimations();
- }
- });
- return true;
- }
-
- private void updatePipUI() {
- if (!mPipManager.isPipShown()) {
- mPipRecentsOverlayManager.removePipRecentsOverlayView();
- mTaskStackHorizontalGridView.startFocusLossAnimation();
- } else {
- Log.w(TAG, "An activity entered PIP mode while Recents is shown");
- }
- }
-
- /**
- * Requests the focus to the PIP controls.
- * This starts the relevant recents row animation
- * and give focus to the recents overlay if needed.
- */
- public void requestPipControlsFocus() {
- if (!mPipManager.isPipShown()) {
- return;
- }
-
- mTaskStackHorizontalGridView.startFocusLossAnimation();
- mPipRecentsOverlayManager.requestFocus(mTaskStackViewAdapter.getItemCount() > 0);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
deleted file mode 100644
index ac9a217..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.tv;
-
-import android.app.ActivityManager;
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.Rect;
-import android.os.SystemClock;
-import android.os.UserHandle;
-
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsActivityLaunchState;
-import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.RecentsImpl;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.model.ThumbnailData;
-import com.android.systemui.recents.tv.views.TaskCardView;
-import com.android.systemui.pip.tv.PipManager;
-
-public class RecentsTvImpl extends RecentsImpl{
- public final static String RECENTS_TV_ACTIVITY =
- "com.android.systemui.recents.tv.RecentsTvActivity";
-
- private static final PipManager mPipManager = PipManager.getInstance();
-
- public RecentsTvImpl(Context context) {
- super(context);
- }
-
- @Override
- protected void startRecentsActivity(ActivityManager.RunningTaskInfo runningTask,
- boolean isHomeStackVisible, boolean animate, int growTarget) {
- RecentsTaskLoader loader = Recents.getTaskLoader();
-
- // In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
- // should always preload the tasks now. If we are dragging in recents, reload them as
- // the stacks might have changed.
- if (mTriggeredFromAltTab || sInstanceLoadPlan == null) {
- // Create a new load plan if preloadRecents() was never triggered
- sInstanceLoadPlan = loader.createLoadPlan(mContext);
- }
- if (mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
- loader.preloadTasks(sInstanceLoadPlan, runningTask.id, !isHomeStackVisible);
- }
- TaskStack stack = sInstanceLoadPlan.getTaskStack();
-
- if (!animate) {
- ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext, -1, -1);
- startRecentsActivity(runningTask, opts, false /* fromHome */, false /* fromThumbnail*/);
- return;
- }
-
- boolean hasRecentTasks = stack.getTaskCount() > 0;
- boolean useThumbnailTransition = (runningTask != null) && !isHomeStackVisible && hasRecentTasks;
-
- if (useThumbnailTransition) {
- // Try starting with a thumbnail transition
- ActivityOptions opts = getThumbnailTransitionActivityOptionsForTV(runningTask,
- stack.getTaskCount());
- if (opts != null) {
- startRecentsActivity(runningTask, opts, false /* fromHome */, true /* fromThumbnail */);
- } else {
- // Fall through below to the non-thumbnail transition
- useThumbnailTransition = false;
- }
- }
-
- if (!useThumbnailTransition) {
- startRecentsActivity(runningTask, null, true /* fromHome */, false /* fromThumbnail */);
- }
- mLastToggleTime = SystemClock.elapsedRealtime();
- }
-
- protected void startRecentsActivity(ActivityManager.RunningTaskInfo runningTask,
- ActivityOptions opts, boolean fromHome, boolean fromThumbnail) {
- // Update the configuration based on the launch options
- RecentsConfiguration config = Recents.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- launchState.launchedFromHome = fromHome;
- launchState.launchedFromApp = fromThumbnail;
- launchState.launchedToTaskId = (runningTask != null) ? runningTask.id : -1;
- launchState.launchedWithAltTab = mTriggeredFromAltTab;
-
- Intent intent = new Intent();
- intent.setClassName(RECENTS_PACKAGE, RECENTS_TV_ACTIVITY);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
-
- if (opts != null) {
- mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);
- } else {
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
- }
- EventBus.getDefault().send(new RecentsActivityStartingEvent());
- }
-
- /**
- * Creates the activity options for an app->recents transition on TV.
- */
- private ActivityOptions getThumbnailTransitionActivityOptionsForTV(
- ActivityManager.RunningTaskInfo runningTask, int numTasks) {
- Rect rect = TaskCardView.getStartingCardThumbnailRect(
- mContext, !mPipManager.isPipShown(), numTasks);
- SystemServicesProxy ssp = Recents.getSystemServices();
- ThumbnailData thumbnailData = ssp.getTaskThumbnail(runningTask.id);
- if (thumbnailData.thumbnail != null) {
- Bitmap thumbnail = Bitmap.createScaledBitmap(thumbnailData.thumbnail, rect.width(),
- rect.height(), false);
- return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
- thumbnail, (int) rect.left, (int) rect.top, (int) rect.width(),
- (int) rect.height(), mHandler, null);
- }
- // If both the screenshot and thumbnail fails, then just fall back to the default transition
- return getUnknownTransitionActivityOptions();
- }
-
- @Override
- public void onVisibilityChanged(Context context, boolean visible) {
- Recents.getSystemServices().setRecentsVisibility(visible);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/DismissAnimationsHolder.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/DismissAnimationsHolder.java
deleted file mode 100644
index 65f5fff..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/DismissAnimationsHolder.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.recents.tv.animations;
-
-import android.animation.Animator.AnimatorListener;
-import android.content.res.Resources;
-import android.graphics.drawable.TransitionDrawable;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-import com.android.systemui.Interpolators;
-import com.android.systemui.recents.tv.views.TaskCardView;
-
-import com.android.systemui.R;
-
-public class DismissAnimationsHolder {
- private LinearLayout mInfoField;
- private View mThumbnailView;
-
- private int mDismissEnterYDelta;
- private int mDismissStartYDelta;
-
- private ImageView mCardDismissIcon;
- private TransitionDrawable mDismissDrawable;
- private TextView mDismissText;
-
- private float mDismissIconNotInDismissStateAlpha;
- private long mShortDuration;
- private long mLongDuration;
-
- public DismissAnimationsHolder(TaskCardView taskCardView) {
-
- mInfoField = (LinearLayout) taskCardView.findViewById(R.id.card_info_field);
- mThumbnailView = taskCardView.findViewById(R.id.card_view_thumbnail);
- mCardDismissIcon = (ImageView) taskCardView.findViewById(R.id.dismiss_icon);
- mDismissDrawable = (TransitionDrawable) mCardDismissIcon.getDrawable();
- mDismissDrawable.setCrossFadeEnabled(true);
- mDismissText = (TextView) taskCardView.findViewById(R.id.card_dismiss_text);
-
- Resources res = taskCardView.getResources();
- mDismissEnterYDelta = res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_shift_down);
- mDismissStartYDelta = mDismissEnterYDelta * 2;
- mShortDuration = res.getInteger(R.integer.dismiss_short_duration);
- mLongDuration = res.getInteger(R.integer.dismiss_long_duration);
- mDismissIconNotInDismissStateAlpha = res.getFloat(R.integer.dismiss_unselected_alpha);
- }
-
- public void startEnterAnimation() {
- mCardDismissIcon.animate()
- .setDuration(mShortDuration)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .alpha(1.0f)
- .withStartAction(new Runnable() {
- @Override
- public void run() {
- mDismissDrawable.startTransition(0);
- }
- });
-
- mDismissText.animate()
- .setDuration(mShortDuration)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .alpha(1.0f);
-
- mInfoField.animate()
- .setDuration(mShortDuration)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .translationY(mDismissEnterYDelta)
- .alpha(0.5f);
-
- mThumbnailView.animate()
- .setDuration(mShortDuration)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .translationY(mDismissEnterYDelta)
- .alpha(0.5f);
- }
-
- public void startExitAnimation() {
- mCardDismissIcon.animate()
- .setDuration(mShortDuration)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .alpha(mDismissIconNotInDismissStateAlpha)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- mDismissDrawable.reverseTransition(0);
- }
- });
-
- mDismissText.animate()
- .setDuration(mShortDuration)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .alpha(0.0f);
-
- mInfoField.animate()
- .setDuration(mShortDuration)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .translationY(0)
- .alpha(1.0f);
-
- mThumbnailView.animate()
- .setDuration(mShortDuration)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .translationY(0)
- .alpha(1.0f);
- }
-
- public void startDismissAnimation(AnimatorListener listener) {
- mCardDismissIcon.animate()
- .setDuration(mShortDuration)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .alpha(0.0f)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- mDismissDrawable.reverseTransition(0);
- }
- });
-
- mDismissText.animate()
- .setDuration(mShortDuration)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .alpha(0.0f);
-
- mInfoField.animate()
- .setDuration(mLongDuration)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .translationY(mDismissStartYDelta)
- .alpha(0.0f)
- .setListener(listener);
-
- mThumbnailView.animate()
- .setDuration(mLongDuration)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .translationY(mDismissStartYDelta)
- .alpha(0.0f);
- }
-
- public void reset() {
- mInfoField.setAlpha(1.0f);
- mInfoField.setTranslationY(0);
- mInfoField.animate().setListener(null);
- mThumbnailView.setAlpha(1.0f);
- mThumbnailView.setTranslationY(0);
- mCardDismissIcon.setAlpha(0.0f);
- mDismissText.setAlpha(0.0f);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/HomeRecentsEnterExitAnimationHolder.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/HomeRecentsEnterExitAnimationHolder.java
deleted file mode 100644
index a673c8c..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/HomeRecentsEnterExitAnimationHolder.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.tv.animations;
-
-import android.content.Context;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
-import com.android.systemui.recents.tv.views.TaskCardView;
-import com.android.systemui.recents.tv.views.TaskStackHorizontalGridView;
-
-
-public class HomeRecentsEnterExitAnimationHolder {
-
- private Context mContext;
- private TaskStackHorizontalGridView mGridView;
- private float mDimAlpha;
- private long mDelay;
- private int mDuration;
- private int mTranslationX;
-
- public HomeRecentsEnterExitAnimationHolder(Context context,
- TaskStackHorizontalGridView gridView) {
- mContext = context;
- mGridView = gridView;
- mDimAlpha = mContext.getResources().getFloat(R.dimen.recents_recents_row_dim_alpha);
- mTranslationX = mContext.getResources()
- .getDimensionPixelSize(R.dimen.recents_tv_home_recents_shift);
- mDelay = mContext.getResources().getInteger(R.integer.recents_home_delay);
- mDuration = mContext.getResources().getInteger(R.integer.recents_home_duration);
- }
-
- public void startEnterAnimation(boolean isPipShown) {
- for(int i = 0; i < mGridView.getChildCount(); i++) {
- TaskCardView view = (TaskCardView) mGridView.getChildAt(i);
- long delay = Math.max(mDelay * i, 0);
- view.setTranslationX(-mTranslationX);
- view.animate()
- .alpha(isPipShown ? mDimAlpha : 1.0f)
- .translationX(0)
- .setDuration(mDuration)
- .setStartDelay(delay)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- }
- }
-
- public void startExitAnimation(DismissRecentsToHomeAnimationStarted dismissEvent) {
- for(int i = mGridView.getChildCount() - 1; i >= 0; i--) {
- TaskCardView view = (TaskCardView) mGridView.getChildAt(i);
- long delay = Math.max(mDelay * (mGridView.getChildCount() - 1 - i), 0);
- view.animate()
- .alpha(0.0f)
- .translationXBy(-mTranslationX)
- .setDuration(mDuration)
- .setStartDelay(delay)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- if(i == 0) {
- view.animate().setListener(dismissEvent.getAnimationTrigger()
- .decrementOnAnimationEnd());
- dismissEvent.getAnimationTrigger().increment();
- }
- }
- }
-
- /**
- * Sets the initial values Recents enter animation
- * when Recents is started from the Launcher.
- */
- public void setEnterFromHomeStartingAnimationValues(boolean isPipShown) {
- for(int i = 0; i < mGridView.getChildCount(); i++) {
- TaskCardView view = (TaskCardView) mGridView.getChildAt(i);
- view.setTranslationX(0);
- view.setAlpha(0.0f);
- view.getInfoFieldView().setAlpha(isPipShown ? 0 : 1f);
- if (isPipShown && view.hasFocus()) {
- view.getViewFocusAnimator().changeSize(false);
- }
- }
- }
-
- /**
- * Sets the initial values Recents enter animation
- * when Recents is started from an app.
- */
- public void setEnterFromAppStartingAnimationValues(boolean isPipShown) {
- for(int i = 0; i < mGridView.getChildCount(); i++) {
- TaskCardView view = (TaskCardView) mGridView.getChildAt(i);
- view.setTranslationX(0);
- view.setAlpha(isPipShown ? mDimAlpha : 1f);
- view.getInfoFieldView().setAlpha(isPipShown ? 0 : 1f);
- if (isPipShown && view.hasFocus()) {
- view.getViewFocusAnimator().changeSize(false);
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/RecentsRowFocusAnimationHolder.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/RecentsRowFocusAnimationHolder.java
deleted file mode 100644
index 8a4cf399..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/RecentsRowFocusAnimationHolder.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.recents.tv.animations;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.content.res.Resources;
-import android.view.View;
-
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.recents.tv.views.TaskCardView;
-
-/**
- * Recents row's focus animation with PIP controls.
- */
-public class RecentsRowFocusAnimationHolder {
- private final View mView;
- private final View mTitleView;
-
- private AnimatorSet mFocusGainAnimatorSet;
- private AnimatorSet mFocusLossAnimatorSet;
-
- public RecentsRowFocusAnimationHolder(View view, View titleView) {
- mView = view;
- mTitleView = titleView;
-
- Resources res = view.getResources();
- int duration = res.getInteger(R.integer.recents_tv_pip_focus_anim_duration);
- float dimAlpha = res.getFloat(R.dimen.recents_recents_row_dim_alpha);
-
- mFocusGainAnimatorSet = new AnimatorSet();
- mFocusGainAnimatorSet.playTogether(
- ObjectAnimator.ofFloat(mView, "alpha", 1f),
- ObjectAnimator.ofFloat(mTitleView, "alpha", 1f));
- mFocusGainAnimatorSet.setDuration(duration);
- mFocusGainAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-
- mFocusLossAnimatorSet = new AnimatorSet();
- mFocusLossAnimatorSet.playTogether(
- // Animation doesn't start from the current value (1f) sometimes,
- // so specify the desired initial value here.
- ObjectAnimator.ofFloat(mView, "alpha", 1f, dimAlpha),
- ObjectAnimator.ofFloat(mTitleView, "alpha", 0f));
- mFocusLossAnimatorSet.setDuration(duration);
- mFocusLossAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- }
-
- /**
- * Starts the Recents row's focus gain animation.
- */
- public void startFocusGainAnimation() {
- cancelAnimator(mFocusLossAnimatorSet);
- mFocusGainAnimatorSet.start();
- }
-
- /**
- * Starts the Recents row's focus loss animation.
- */
- public void startFocusLossAnimation() {
- cancelAnimator(mFocusGainAnimatorSet);
- mFocusLossAnimatorSet.start();
- }
-
- /**
- * Resets the views immediately and ends the animations.
- */
- public void reset() {
- cancelAnimator(mFocusLossAnimatorSet);
- cancelAnimator(mFocusGainAnimatorSet);
- mView.setAlpha(1f);
- mTitleView.setAlpha(1f);
- }
-
- private static void cancelAnimator(Animator animator) {
- if (animator.isStarted()) {
- animator.cancel();
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
deleted file mode 100644
index 72fd7a4..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.recents.tv.animations;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.content.res.Resources;
-import android.util.TypedValue;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.Interpolator;
-
-import com.android.systemui.R;
-import com.android.systemui.recents.tv.views.TaskCardView;
-
-public class ViewFocusAnimator implements View.OnFocusChangeListener {
- private final float mUnselectedScale;
- private final float mSelectedScale;
- private final float mSelectedScaleDelta;
- private final float mUnselectedZ;
- private final float mSelectedZDelta;
- private final float mUnselectedSpacing;
- private final float mSelectedSpacingDelta;
- private final float mDismissIconAlpha;
- private final int mAnimDuration;
- private final Interpolator mFocusInterpolator;
-
- protected TaskCardView mTargetView;
- private float mFocusProgress;
-
- ObjectAnimator mFocusAnimation;
-
- public ViewFocusAnimator(TaskCardView view) {
- mTargetView = view;
- final Resources res = view.getResources();
-
- mTargetView.setOnFocusChangeListener(this);
-
- TypedValue out = new TypedValue();
- res.getValue(R.integer.unselected_scale, out, true);
- mUnselectedScale = out.getFloat();
- res.getValue(R.integer.selected_scale, out, true);
- mSelectedScale = out.getFloat();
- mSelectedScaleDelta = mSelectedScale - mUnselectedScale;
-
- mUnselectedZ = res.getDimensionPixelOffset(R.dimen.recents_tv_unselected_item_z);
- mSelectedZDelta = res.getDimensionPixelOffset(R.dimen.recents_tv_selected_item_z_delta);
-
- mUnselectedSpacing = res.getDimensionPixelOffset(R.dimen.recents_tv_gird_card_spacing);
- mSelectedSpacingDelta = res.getDimensionPixelOffset(R.dimen.recents_tv_gird_focused_card_delta);
-
- mAnimDuration = res.getInteger(R.integer.item_scale_anim_duration);
-
- mFocusInterpolator = new AccelerateDecelerateInterpolator();
-
- mFocusAnimation = ObjectAnimator.ofFloat(this, "focusProgress", 0.0f);
- mFocusAnimation.setDuration(mAnimDuration);
- mFocusAnimation.setInterpolator(mFocusInterpolator);
-
- mDismissIconAlpha = res.getFloat(R.integer.dismiss_unselected_alpha);
-
- setFocusProgress(0.0f);
-
- mFocusAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mTargetView.setHasTransientState(true);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mTargetView.setHasTransientState(false);
- }
- });
- }
-
- private void setFocusProgress(float level) {
- mFocusProgress = level;
-
- float scale = mUnselectedScale + (level * mSelectedScaleDelta);
- float z = mUnselectedZ + (level * mSelectedZDelta);
- float spacing = mUnselectedSpacing + (level * mSelectedSpacingDelta);
-
- mTargetView.setScaleX(scale);
- mTargetView.setScaleY(scale);
-
- mTargetView.setPadding((int) spacing, mTargetView.getPaddingTop(),
- (int) spacing, mTargetView.getPaddingBottom());
-
- mTargetView.getDismissIconView().setAlpha(mDismissIconAlpha * level);
- mTargetView.getThumbnailView().setZ(z);
- mTargetView.getDismissIconView().setZ(z);
- }
-
- private void animateFocus(boolean focused) {
- if (mFocusAnimation.isStarted()) {
- mFocusAnimation.cancel();
- }
-
- float target = focused ? 1.0f : 0.0f;
-
- if (mFocusProgress != target) {
- mFocusAnimation.setFloatValues(mFocusProgress, target);
- mFocusAnimation.start();
- }
- }
-
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- if (v != mTargetView) {
- return;
- }
- changeSize(hasFocus);
- }
-
- /**
- * Changes the size of the {@link TaskCardView} to show its focused state.
- */
- public void changeSize(boolean hasFocus) {
- ViewGroup.LayoutParams lp = mTargetView.getLayoutParams();
- int width = lp.width;
- int height = lp.height;
-
- if (width < 0 && height < 0) {
- mTargetView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
- }
-
- if (mTargetView.isAttachedToWindow() && mTargetView.hasWindowFocus() &&
- mTargetView.getVisibility() == View.VISIBLE) {
- animateFocus(hasFocus);
- } else {
- // Set focus immediately.
- if (mFocusAnimation.isStarted()) {
- mFocusAnimation.cancel();
- }
- setFocusProgress(hasFocus ? 1.0f : 0.0f);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java
deleted file mode 100644
index 2894cd8..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.recents.tv.views;
-
-import android.annotation.Nullable;
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IRemoteCallback;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.WindowManagerGlobal;
-import com.android.internal.annotations.GuardedBy;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.*;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.model.TaskStack;
-
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-
-
-public class RecentsTvTransitionHelper {
- private static final String TAG = "RecentsTvTransitionHelper";
-
- private Context mContext;
- private Handler mHandler;
-
- public RecentsTvTransitionHelper(Context context, Handler handler) {
- mContext = context;
- mHandler = handler;
- }
-
- public void launchTaskFromRecents(final TaskStack stack, @Nullable final Task task,
- final TaskStackHorizontalGridView stackView, final TaskCardView taskView,
- final Rect bounds, int destinationStack) {
- final ActivityOptions opts = ActivityOptions.makeBasic();
- if (bounds != null) {
- opts.setLaunchBounds(bounds.isEmpty() ? null : bounds);
- }
-
- final ActivityOptions.OnAnimationStartedListener animStartedListener;
- if (task.thumbnail != null && task.thumbnail.getWidth() > 0 &&
- task.thumbnail.getHeight() > 0) {
- animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
- @Override
- public void onAnimationStarted() {
- // If we are launching into another task, cancel the previous task's
- // window transition
- EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
- EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
- }
- };
- } else {
- // This is only the case if the task is not on screen (scrolled offscreen for example)
- animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
- @Override
- public void onAnimationStarted() {
- EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
- }
- };
- }
-
- if (taskView == null) {
- // If there is no task view, then we do not need to worry about animating out occluding
- // task views, and we can launch immediately
- startTaskActivity(stack, task, taskView, opts, animStartedListener);
- } else {
- LaunchTvTaskStartedEvent launchStartedEvent = new LaunchTvTaskStartedEvent(taskView);
- EventBus.getDefault().send(launchStartedEvent);
- startTaskActivity(stack, task, taskView, opts, animStartedListener);
- }
- }
-
- private void startTaskActivity(TaskStack stack, Task task, @Nullable TaskCardView taskView,
- ActivityOptions opts,final ActivityOptions.OnAnimationStartedListener animStartedListener) {
- SystemServicesProxy ssp = Recents.getSystemServices();
- if (ssp.startActivityFromRecents(mContext, task.key, task.title, opts, INVALID_STACK_ID)) {
- // Keep track of the index of the task launch
- int taskIndexFromFront = 0;
- int taskIndex = stack.indexOfStackTask(task);
- if (taskIndex > -1) {
- taskIndexFromFront = stack.getTaskCount() - taskIndex - 1;
- }
- EventBus.getDefault().send(new LaunchTaskSucceededEvent(taskIndexFromFront));
- } else {
- // Keep track of failed launches
- EventBus.getDefault().send(new LaunchTaskFailedEvent());
- }
-
- Rect taskRect = taskView.getFocusedThumbnailRect();
- // Check both the rect and the thumbnail for null. The rect can be null if the user
- // decides to disallow animations, so automatic scrolling does not happen properly.
-
- // The thumbnail can be null if the app was partially launched on TV. In this case
- // we do not override the transition.
- if (taskRect == null || task.thumbnail == null) {
- return;
- }
-
- IRemoteCallback.Stub callback = null;
- if (animStartedListener != null) {
- callback = new IRemoteCallback.Stub() {
- @Override
- public void sendResult(Bundle data) throws RemoteException {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (animStartedListener != null) {
- animStartedListener.onAnimationStarted();
- }
- }
- });
- }
- };
- }
- try {
- Bitmap thumbnail = Bitmap.createScaledBitmap(task.thumbnail, taskRect.width(),
- taskRect.height(), false);
- WindowManagerGlobal.getWindowManagerService()
- .overridePendingAppTransitionAspectScaledThumb(thumbnail, taskRect.left,
- taskRect.top, taskRect.width(), taskRect.height(), callback, true);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to override transition: " + e);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
deleted file mode 100644
index 9db8071..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.recents.tv.views;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.support.v7.widget.RecyclerView;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.WindowInsets;
-import android.widget.FrameLayout;
-
-import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsActivity;
-import com.android.systemui.recents.RecentsActivityLaunchState;
-import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
-import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
-import com.android.systemui.recents.events.activity.LaunchTvTaskEvent;
-import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
-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.recents.tv.animations.RecentsRowFocusAnimationHolder;
-import android.support.v7.widget.RecyclerView.OnScrollListener;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-
-/**
- * Top level layout of recents for TV. This will show the TaskStacks using a HorizontalGridView.
- */
-public class RecentsTvView extends FrameLayout {
-
- private static final String TAG = "RecentsTvView";
- private static final boolean DEBUG = false;
-
- private TaskStack mStack;
- private TaskStackHorizontalGridView mTaskStackHorizontalView;
- private View mEmptyView;
- private View mDismissPlaceholder;
- private RecentsRowFocusAnimationHolder mEmptyViewFocusAnimationHolder;
- private boolean mAwaitingFirstLayout = true;
- private Rect mSystemInsets = new Rect();
- private RecentsTvTransitionHelper mTransitionHelper;
- private final Handler mHandler = new Handler();
- private OnScrollListener mScrollListener;
- public RecentsTvView(Context context) {
- this(context, null);
- }
-
- public RecentsTvView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public RecentsTvView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public RecentsTvView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
-
- setWillNotDraw(false);
-
- LayoutInflater inflater = LayoutInflater.from(context);
- mEmptyView = inflater.inflate(R.layout.recents_tv_empty, this, false);
- addView(mEmptyView);
-
- mTransitionHelper = new RecentsTvTransitionHelper(mContext, mHandler);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mDismissPlaceholder = findViewById(R.id.dismiss_placeholder);
- mTaskStackHorizontalView = (TaskStackHorizontalGridView) findViewById(R.id.task_list);
- }
-
- /**
- * Initialize the view.
- */
- public void init(TaskStack stack) {
- RecentsConfiguration config = Recents.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- mStack = stack;
-
- mTaskStackHorizontalView.init(stack);
-
- if (stack.getStackTaskCount() > 0) {
- hideEmptyView();
- } else {
- showEmptyView();
- }
-
- // Layout with the new stack
- requestLayout();
- }
-
- public boolean launchFocusedTask() {
- if (mTaskStackHorizontalView != null) {
- Task task = mTaskStackHorizontalView.getFocusedTask();
- if (task != null) {
- launchTaskFomRecents(task, true);
- return true;
- }
- }
- return false;
- }
-
- /** Launches the task that recents was launched from if possible */
- public boolean launchPreviousTask(boolean animate) {
- if (mTaskStackHorizontalView != null) {
- TaskStack stack = mTaskStackHorizontalView.getStack();
- Task task = stack.getLaunchTarget();
- if (task != null) {
- launchTaskFomRecents(task, animate);
- return true;
- }
- }
- return false;
- }
-
- /**
- * Launch the given task from recents with animation. If the task is not focused, this will
- * attempt to scroll to focus the task before launching.
- * @param task
- */
- private void launchTaskFomRecents(final Task task, boolean animate) {
- if (!animate) {
- SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.startActivityFromRecents(getContext(), task.key, task.title, null,
- INVALID_STACK_ID);
- return;
- }
- mTaskStackHorizontalView.requestFocus();
- Task focusedTask = mTaskStackHorizontalView.getFocusedTask();
- if (focusedTask != null && task != focusedTask) {
- if (mScrollListener != null) {
- mTaskStackHorizontalView.removeOnScrollListener(mScrollListener);
- }
- mScrollListener = new OnScrollListener() {
- @Override
- public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
- super.onScrollStateChanged(recyclerView, newState);
- if (newState == RecyclerView.SCROLL_STATE_IDLE) {
- TaskCardView cardView = mTaskStackHorizontalView.getChildViewForTask(task);
- if (cardView != null) {
- mTransitionHelper.launchTaskFromRecents(mStack, task,
- mTaskStackHorizontalView, cardView, null, INVALID_STACK_ID);
- } else {
- // This should not happen normally. If this happens then the data in
- // the grid view was altered during the scroll. Log error and launch
- // task with no animation.
- Log.e(TAG, "Card view for task : " + task + ", returned null.");
- SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.startActivityFromRecents(getContext(), task.key, task.title, null,
- INVALID_STACK_ID);
- }
- mTaskStackHorizontalView.removeOnScrollListener(mScrollListener);
- }
- }
- };
- mTaskStackHorizontalView.addOnScrollListener(mScrollListener);
- mTaskStackHorizontalView.setSelectedPositionSmooth(
- ((TaskStackHorizontalViewAdapter) mTaskStackHorizontalView.getAdapter())
- .getPositionOfTask(task));
- } else {
- mTransitionHelper.launchTaskFromRecents(mStack, task, mTaskStackHorizontalView,
- mTaskStackHorizontalView.getChildViewForTask(task), null,
- INVALID_STACK_ID);
- }
- }
-
- /**
- * Hides the task stack and shows the empty view.
- */
- public void showEmptyView() {
- mEmptyView.setVisibility(View.VISIBLE);
- mTaskStackHorizontalView.setVisibility(View.GONE);
- if (Recents.getSystemServices().isTouchExplorationEnabled()) {
- mDismissPlaceholder.setVisibility(View.GONE);
- }
- }
-
- /**
- * Shows the task stack and hides the empty view.
- */
- public void hideEmptyView() {
- mEmptyView.setVisibility(View.GONE);
- mTaskStackHorizontalView.setVisibility(View.VISIBLE);
- if (Recents.getSystemServices().isTouchExplorationEnabled()) {
- mDismissPlaceholder.setVisibility(View.VISIBLE);
- }
- }
-
- /**
- * Returns the last known system insets.
- */
- public Rect getSystemInsets() {
- return mSystemInsets;
- }
-
- @Override
- protected void onAttachedToWindow() {
- EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
- super.onAttachedToWindow();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- EventBus.getDefault().unregister(this);
- }
-
- @Override
- public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- mSystemInsets.set(insets.getSystemWindowInsets());
- requestLayout();
- return insets;
- }
-
- /**** EventBus Events ****/
-
- public final void onBusEvent(LaunchTvTaskEvent event) {
- mTransitionHelper.launchTaskFromRecents(mStack, event.task, mTaskStackHorizontalView,
- event.taskView, event.targetTaskBounds, event.targetTaskStack);
- }
-
- public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
- // If we are going home, cancel the previous task's window transition
- EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
- }
-
- public final void onBusEvent(RecentsVisibilityChangedEvent event) {
- if (!event.visible) {
- // Reset the view state
- mAwaitingFirstLayout = true;
- }
- }
-
- public TaskStackHorizontalGridView setTaskStackViewAdapter(
- TaskStackHorizontalViewAdapter taskStackViewAdapter) {
- if (mTaskStackHorizontalView != null) {
- mTaskStackHorizontalView.setAdapter(taskStackViewAdapter);
- taskStackViewAdapter.setTaskStackHorizontalGridView(mTaskStackHorizontalView);
- }
- return mTaskStackHorizontalView;
- }
-
- public TaskStackHorizontalGridView getGridView() {
- return mTaskStackHorizontalView;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
deleted file mode 100644
index e757560..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
+++ /dev/null
@@ -1,355 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.recents.tv.views;
-
-import android.animation.Animator;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Outline;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.Display;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-import android.view.WindowManager;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.tv.RecentsTvActivity;
-import com.android.systemui.recents.tv.animations.DismissAnimationsHolder;
-import com.android.systemui.recents.tv.animations.RecentsRowFocusAnimationHolder;
-import com.android.systemui.recents.tv.animations.ViewFocusAnimator;
-
-public class TaskCardView extends LinearLayout {
-
- private static final String TAG = "TaskCardView";
- private View mThumbnailView;
- private View mDismissIconView;
- private View mInfoFieldView;
- private TextView mTitleTextView;
- private ImageView mBadgeView;
- private Task mTask;
- private boolean mDismissState;
- private boolean mTouchExplorationEnabled;
- private int mCornerRadius;
-
- private ViewFocusAnimator mViewFocusAnimator;
- private DismissAnimationsHolder mDismissAnimationsHolder;
- private RecentsRowFocusAnimationHolder mRecentsRowFocusAnimationHolder;
-
- public TaskCardView(Context context) {
- this(context, null);
- }
-
- public TaskCardView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public TaskCardView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- mDismissState = false;
- Configuration config = getResources().getConfiguration();
- setLayoutDirection(config.getLayoutDirection());
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mThumbnailView = findViewById(R.id.card_view_thumbnail);
- mInfoFieldView = findViewById(R.id.card_info_field);
- mTitleTextView = (TextView) findViewById(R.id.card_title_text);
- mBadgeView = (ImageView) findViewById(R.id.card_extra_badge);
- mDismissIconView = findViewById(R.id.dismiss_icon);
- mDismissAnimationsHolder = new DismissAnimationsHolder(this);
- mCornerRadius = getResources().getDimensionPixelSize(
- R.dimen.recents_task_view_rounded_corners_radius);
- mRecentsRowFocusAnimationHolder = new RecentsRowFocusAnimationHolder(this, mInfoFieldView);
- SystemServicesProxy ssp = Recents.getSystemServices();
- mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
- if (!mTouchExplorationEnabled) {
- mDismissIconView.setVisibility(VISIBLE);
- } else {
- mDismissIconView.setVisibility(GONE);
- }
- mViewFocusAnimator = new ViewFocusAnimator(this);
- }
-
- public void init(Task task) {
- mTask = task;
- mTitleTextView.setText(task.title);
- mBadgeView.setImageDrawable(task.icon);
- setThumbnailView();
- setContentDescription(task.titleDescription);
- mDismissState = false;
- mDismissAnimationsHolder.reset();
- mRecentsRowFocusAnimationHolder.reset();
- }
-
- public Task getTask() {
- return mTask;
- }
-
- @Override
- public void getFocusedRect(Rect r) {
- mThumbnailView.getFocusedRect(r);
- }
-
- public Rect getFocusedThumbnailRect() {
- Rect r = new Rect();
- mThumbnailView.getGlobalVisibleRect(r);
- return r;
- }
-
- public static Rect getStartingCardThumbnailRect(
- Context context, boolean hasFocus, int numberOfTasks) {
- if(numberOfTasks > 1) {
- return getStartingCardThumbnailRectForStartPosition(context, hasFocus);
- } else {
- return getStartingCardThumbnailRectForFocusedPosition(context, hasFocus);
- }
- }
-
- private static Rect getStartingCardThumbnailRectForStartPosition(
- Context context, boolean hasFocus) {
- Resources res = context.getResources();
-
- int width = res.getDimensionPixelOffset(R.dimen.recents_tv_card_width);
- int totalSpacing = res.getDimensionPixelOffset(R.dimen.recents_tv_gird_card_spacing) * 2;
- if (hasFocus) {
- totalSpacing += res.getDimensionPixelOffset(R.dimen.recents_tv_gird_focused_card_delta);
- }
- int height = res.getDimensionPixelOffset(R.dimen.recents_tv_screenshot_height);
- int topMargin = res.getDimensionPixelOffset(R.dimen.recents_tv_gird_row_top_margin);
- int headerHeight = res.getDimensionPixelOffset(R.dimen.recents_tv_card_extra_badge_size) +
- res.getDimensionPixelOffset(R.dimen.recents_tv_icon_padding_bottom);
-
- WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- Display display = wm.getDefaultDisplay();
- Point size = new Point();
- display.getSize(size);
- int screenWidth = size.x;
-
- return new Rect(screenWidth / 2 + width / 2 + totalSpacing,
- topMargin + headerHeight,
- screenWidth / 2 + width / 2 + totalSpacing + width,
- topMargin + headerHeight + height);
- }
-
- private static Rect getStartingCardThumbnailRectForFocusedPosition(
- Context context, boolean hasFocus) {
- Resources res = context.getResources();
-
- TypedValue out = new TypedValue();
- res.getValue(R.integer.selected_scale, out, true);
- float scale = hasFocus ? out.getFloat() : 1;
-
- int width = res.getDimensionPixelOffset(R.dimen.recents_tv_card_width);
- int widthDelta = (int) (width * scale - width);
- int height = res.getDimensionPixelOffset(R.dimen.recents_tv_screenshot_height);
- int heightDelta = (int) (height * scale - height);
- int topMargin = res.getDimensionPixelOffset(R.dimen.recents_tv_gird_row_top_margin);
-
- int headerHeight = res.getDimensionPixelOffset(R.dimen.recents_tv_card_extra_badge_size) +
- res.getDimensionPixelOffset(R.dimen.recents_tv_icon_padding_bottom);
- int headerHeightDelta = (int) (headerHeight * scale - headerHeight);
-
- int dismissAreaHeight =
- res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_icon_top_margin) +
- res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_icon_bottom_margin) +
- res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_icon_size) +
- res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_text_size);
-
- int dismissAreaHeightDelta = (int) (dismissAreaHeight * scale - dismissAreaHeight);
-
- int totalHeightDelta = heightDelta + headerHeightDelta + dismissAreaHeightDelta;
-
- WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- Display display = wm.getDefaultDisplay();
- Point size = new Point();
- display.getSize(size);
- int screenWidth = size.x;
-
- return new Rect(screenWidth / 2 - width / 2 - widthDelta / 2,
- topMargin - totalHeightDelta / 2 + (int) (headerHeight * scale),
- screenWidth / 2 + width / 2 + widthDelta / 2,
- topMargin - totalHeightDelta / 2 + (int) (headerHeight * scale) +
- (int) (height * scale));
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- // Override dispatchKeyEvent() instead of onKeyDown() to prevent warning from ViewRootImpl.
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_DPAD_DOWN : {
- if (!isInDismissState() && event.getAction() == KeyEvent.ACTION_DOWN) {
- setDismissState(true);
- return true;
- }
- break;
- }
- case KeyEvent.KEYCODE_DPAD_UP : {
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- if (isInDismissState()) {
- setDismissState(false);
- } else {
- ((RecentsTvActivity) getContext()).requestPipControlsFocus();
- }
- }
- return true;
- }
-
- // Eat right and left key presses when we are in dismiss state
- case KeyEvent.KEYCODE_DPAD_LEFT :
- case KeyEvent.KEYCODE_DPAD_RIGHT : {
- if (isInDismissState()) {
- return true;
- }
- break;
- }
- }
- return super.dispatchKeyEvent(event);
- }
-
- private void setDismissState(boolean dismissState) {
- if (mDismissState != dismissState) {
- mDismissState = dismissState;
- // Check for touch exploration to ensure dismiss icon/text do not
- // get animated. This should be removed based on decision from
- // b/29208918
- if (!mTouchExplorationEnabled) {
- if (dismissState) {
- mDismissAnimationsHolder.startEnterAnimation();
- } else {
- mDismissAnimationsHolder.startExitAnimation();
- }
- }
- }
- }
-
- public boolean isInDismissState() {
- return mDismissState;
- }
-
- public void startDismissTaskAnimation(Animator.AnimatorListener listener) {
- mDismissState = false;
- mDismissAnimationsHolder.startDismissAnimation(listener);
- }
-
- public ViewFocusAnimator getViewFocusAnimator() {
- return mViewFocusAnimator;
- }
-
- public RecentsRowFocusAnimationHolder getRecentsRowFocusAnimationHolder() {
- return mRecentsRowFocusAnimationHolder;
- }
-
- private void setThumbnailView() {
- ImageView screenshotView = (ImageView) findViewById(R.id.card_view_banner_icon);
- PackageManager pm = getContext().getPackageManager();
- if (mTask.thumbnail != null) {
- setAsScreenShotView(mTask.thumbnail, screenshotView);
- } else {
- try {
- Drawable banner = null;
- if (mTask.key != null) {
- banner = pm.getActivityBanner(mTask.key.baseIntent);
- }
- if (banner != null) {
- setAsBannerView(banner, screenshotView);
- } else {
- setAsIconView(mTask.icon, screenshotView);
- }
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "Package not found : " + e);
- setAsIconView(mTask.icon, screenshotView);
- }
- }
- }
-
- private void setAsScreenShotView(Bitmap screenshot, ImageView screenshotView) {
- LayoutParams lp = (LayoutParams) screenshotView.getLayoutParams();
- lp.width = LayoutParams.MATCH_PARENT;
- lp.height = LayoutParams.MATCH_PARENT;
-
- screenshotView.setLayoutParams(lp);
- screenshotView.setClipToOutline(true);
- screenshotView.setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCornerRadius);
- }
- });
- screenshotView.setImageBitmap(screenshot);
- }
-
- private void setAsBannerView(Drawable banner, ImageView bannerView) {
- LayoutParams lp = (LayoutParams) bannerView.getLayoutParams();
- lp.width = getResources()
- .getDimensionPixelSize(R.dimen.recents_tv_banner_width);
- lp.height = getResources()
- .getDimensionPixelSize(R.dimen.recents_tv_banner_height);
- bannerView.setLayoutParams(lp);
- bannerView.setImageDrawable(banner);
- }
-
- private void setAsIconView(Drawable icon, ImageView iconView) {
- LayoutParams lp = (LayoutParams) iconView.getLayoutParams();
- lp.width = getResources()
- .getDimensionPixelSize(R.dimen.recents_tv_fallback_icon_width);
- lp.height = getResources()
- .getDimensionPixelSize(R.dimen.recents_tv_fallback_icon_height);
-
- iconView.setLayoutParams(lp);
- iconView.setImageDrawable(icon);
- }
-
- public View getThumbnailView() {
- return mThumbnailView;
- }
-
- public View getInfoFieldView() {
- return mInfoFieldView;
- }
-
- public View getDismissIconView() {
- return mDismissIconView;
- }
-
- public static int getNumberOfVisibleTasks(Context context) {
- Resources res = context.getResources();
- WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- Display display = wm.getDefaultDisplay();
- Point size = new Point();
- display.getSize(size);
- int screenWidth = size.x;
- int cardWidth = res.getDimensionPixelSize(R.dimen.recents_tv_card_width);
- int spacing = res.getDimensionPixelSize(R.dimen.recents_tv_gird_card_spacing);
- return (int) (1.0 + Math.ceil(screenWidth / (cardWidth + spacing * 2.0)));
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
deleted file mode 100644
index f9b8700..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.recents.tv.views;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.content.Context;
-import android.support.v17.leanback.widget.HorizontalGridView;
-import android.util.AttributeSet;
-import android.view.View;
-
-import com.android.systemui.R;
-import com.android.systemui.recents.RecentsActivity;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
-import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.model.TaskStack.TaskStackCallbacks;
-import com.android.systemui.recents.views.AnimationProps;
-
-/**
- * Horizontal Grid View Implementation to show the Task Stack for TV.
- */
-public class TaskStackHorizontalGridView extends HorizontalGridView implements TaskStackCallbacks {
- private static final int ANIMATION_DELAY_MS = 50;
- private static final int MSG_START_RECENT_ROW_FOCUS_ANIMATION = 100;
- private TaskStack mStack;
- private Task mFocusedTask;
- private AnimatorSet mRecentsRowFocusAnimation;
-
- public TaskStackHorizontalGridView(Context context) {
- this(context, null);
- }
-
- public TaskStackHorizontalGridView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onAttachedToWindow() {
- EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
- setWindowAlignment(WINDOW_ALIGN_NO_EDGE);
- setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
- super.onAttachedToWindow();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- EventBus.getDefault().unregister(this);
- }
-
- /**
- * Initializes the grid view.
- * @param stack
- */
- public void init(TaskStack stack) {
- // Set new stack
- mStack = stack;
- if (mStack != null) {
- mStack.setCallbacks(this);
- }
- }
-
- /**
- * @return Returns the task stack.
- */
- public TaskStack getStack() {
- return mStack;
- }
-
- /**
- * @return - The focused task.
- */
- public Task getFocusedTask() {
- if (findFocus() != null) {
- mFocusedTask = ((TaskCardView)findFocus()).getTask();
- }
- return mFocusedTask;
- }
-
- /**
- * @param task
- * @return Child view for given task
- */
- public TaskCardView getChildViewForTask(Task task) {
- for (int i = 0; i < getChildCount(); i++) {
- TaskCardView tv = (TaskCardView) getChildAt(i);
- if (tv.getTask() == task) {
- return tv;
- }
- }
- return null;
- }
-
-
- /**
- * Starts the Recents row's focus gain animation.
- */
- public void startFocusGainAnimation() {
- for (int i = 0; i < getChildCount(); i++) {
- TaskCardView v = (TaskCardView) getChildAt(i);
- if (v.hasFocus()) {
- v.getViewFocusAnimator().changeSize(true);
- }
- v.getRecentsRowFocusAnimationHolder().startFocusGainAnimation();
- }
- }
-
- /**
- * Starts the Recents row's focus loss animation.
- */
- public void startFocusLossAnimation() {
- for (int i = 0; i < getChildCount(); i++) {
- TaskCardView v = (TaskCardView) getChildAt(i);
- if (v.hasFocus()) {
- v.getViewFocusAnimator().changeSize(false);
- }
- v.getRecentsRowFocusAnimationHolder().startFocusLossAnimation();
- }
- }
-
- @Override
- public void onStackTaskAdded(TaskStack stack, Task newTask) {
- ((TaskStackHorizontalViewAdapter) getAdapter()).addTaskAt(newTask,
- stack.indexOfStackTask(newTask));
- }
-
- @Override
- public void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask,
- AnimationProps animation, boolean fromDockGesture) {
- ((TaskStackHorizontalViewAdapter) getAdapter()).removeTask(removedTask);
- if (mFocusedTask == removedTask) {
- mFocusedTask = null;
- }
- // If there are no remaining tasks, then just close recents
- if (mStack.getStackTaskCount() == 0) {
- boolean shouldFinishActivity = (mStack.getStackTaskCount() == 0);
- if (shouldFinishActivity) {
- EventBus.getDefault().send(new AllTaskViewsDismissedEvent(fromDockGesture
- ? R.string.recents_empty_message
- : R.string.recents_empty_message_dismissed_all));
- }
- }
- }
-
- @Override
- public void onStackTasksRemoved(TaskStack stack) {
- // Do nothing
- }
-
- @Override
- public void onStackTasksUpdated(TaskStack stack) {
- // Do nothing
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
deleted file mode 100644
index 236d077..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.recents.tv.views;
-
-import android.animation.Animator;
-import android.support.v7.widget.RecyclerView;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.LaunchTvTaskEvent;
-import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
-import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.views.AnimationProps;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-
-public class TaskStackHorizontalViewAdapter extends
- RecyclerView.Adapter<TaskStackHorizontalViewAdapter.ViewHolder> {
-
- //Full class name is 30 characters
- private static final String TAG = "TaskStackViewAdapter";
- private List<Task> mTaskList;
- private TaskStackHorizontalGridView mGridView;
-
- public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
- private TaskCardView mTaskCardView;
- private Task mTask;
- public ViewHolder(View v) {
- super(v);
- mTaskCardView = (TaskCardView) v;
- }
-
- public void init(Task task) {
- mTaskCardView.init(task);
- mTask = task;
- mTaskCardView.setOnClickListener(this);
- }
-
- @Override
- public void onClick(View v) {
- try {
- if (mTaskCardView.isInDismissState()) {
- mTaskCardView.startDismissTaskAnimation(
- getRemoveAtListener(getAdapterPosition(), mTaskCardView.getTask()));
- } else {
- EventBus.getDefault().send(new LaunchTvTaskEvent(mTaskCardView, mTask,
- null, INVALID_STACK_ID));
- }
- } catch (Exception e) {
- Log.e(TAG, v.getContext()
- .getString(R.string.recents_launch_error_message, mTask.title), e);
- }
-
- }
-
- private Animator.AnimatorListener getRemoveAtListener(final int position,
- final Task task) {
- return new Animator.AnimatorListener() {
-
- @Override
- public void onAnimationStart(Animator animation) { }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- removeTask(task);
- EventBus.getDefault().send(new DeleteTaskDataEvent(task));
- }
-
- @Override
- public void onAnimationCancel(Animator animation) { }
-
- @Override
- public void onAnimationRepeat(Animator animation) { }
- };
-
- }
- }
-
- public TaskStackHorizontalViewAdapter(List tasks) {
- mTaskList = new ArrayList<Task>(tasks);
- }
-
- public void setNewStackTasks(List tasks) {
- mTaskList.clear();
- mTaskList.addAll(tasks);
- notifyDataSetChanged();
- }
-
- @Override
- public TaskStackHorizontalViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
- int viewType) {
- LayoutInflater inflater = LayoutInflater.from(parent.getContext());
- ViewHolder viewHolder = new ViewHolder(
- inflater.inflate(R.layout.recents_tv_task_card_view, parent, false));
- return viewHolder;
- }
-
- @Override
- public void onBindViewHolder(ViewHolder holder, int position) {
- Task task = mTaskList.get(position);
- // Retrives from caches, loading only if necessary
- Recents.getTaskLoader().loadTaskData(task);
- holder.init(task);
- }
-
- @Override
- public int getItemCount() {
- return mTaskList.size();
- }
-
- public void removeTask(Task task) {
- int position = mTaskList.indexOf(task);
- if (position >= 0) {
- mTaskList.remove(position);
- notifyItemRemoved(position);
- if (mGridView != null) {
- mGridView.getStack().removeTask(task, AnimationProps.IMMEDIATE,
- false);
- }
- }
- }
-
- public int getPositionOfTask(Task task) {
- int position = mTaskList.indexOf(task);
- return (position >= 0) ? position : 0;
- }
-
-
- public void setTaskStackHorizontalGridView(TaskStackHorizontalGridView gridView) {
- mGridView = gridView;
- }
-
- public void addTaskAt(Task task, int position) {
- mTaskList.add(position, task);
- notifyItemInserted(position);
- }
-}
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 6a66fca7..8882cab 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -16,6 +16,7 @@
package com.android.systemui.recents.views;
+import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
@@ -303,7 +304,7 @@
// TODO: Sometimes targetStackId is not initialized after reboot, so we also have to
// check for INVALID_STACK_ID
if (targetStackId == FULLSCREEN_WORKSPACE_STACK_ID || targetStackId == DOCKED_STACK_ID
- || targetStackId == INVALID_STACK_ID) {
+ || targetStackId == ASSISTANT_STACK_ID || targetStackId == INVALID_STACK_ID) {
if (taskView == null) {
specs.add(composeOffscreenAnimationSpec(task, offscreenTaskRect));
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index dceeb74..0924089 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -29,7 +29,6 @@
import android.os.BatteryStats;
import android.os.Handler;
import android.os.Message;
-import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -95,12 +94,20 @@
private final DevicePolicyManager mDevicePolicyManager;
private boolean mDozing;
+ /**
+ * Creates a new KeyguardIndicationController and registers callbacks.
+ */
public KeyguardIndicationController(Context context, ViewGroup indicationArea,
LockIcon lockIcon) {
this(context, indicationArea, lockIcon,
WakeLock.createPartial(context, "Doze:KeyguardIndication"));
+
+ registerCallbacks(KeyguardUpdateMonitor.getInstance(context));
}
+ /**
+ * Creates a new KeyguardIndicationController for testing. Does *not* register callbacks.
+ */
@VisibleForTesting
KeyguardIndicationController(Context context, ViewGroup indicationArea, LockIcon lockIcon,
WakeLock wakeLock) {
@@ -124,12 +131,15 @@
mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
- KeyguardUpdateMonitor.getInstance(context).registerCallback(getKeyguardCallback());
- context.registerReceiverAsUser(mTickReceiver, UserHandle.SYSTEM,
+ updateDisclosure();
+ }
+
+ private void registerCallbacks(KeyguardUpdateMonitor monitor) {
+ monitor.registerCallback(getKeyguardCallback());
+
+ mContext.registerReceiverAsUser(mTickReceiver, UserHandle.SYSTEM,
new IntentFilter(Intent.ACTION_TIME_TICK), null,
Dependency.get(Dependency.TIME_TICK_HANDLER));
-
- updateDisclosure();
}
/**
@@ -331,7 +341,7 @@
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
}
- BroadcastReceiver mTickReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mTickReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mHandler.post(() -> {
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 99f8aaf..8a3c4e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -142,7 +142,7 @@
try {
WindowManagerGlobal.getWindowManagerService()
- .watchRotation(mRotationWatcher);
+ .watchRotation(mRotationWatcher, getContext().getDisplay().getDisplayId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
index 1df12ac..1a51dab 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
@@ -25,7 +25,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.statusbar.phone.StatusBarIconController;
-import static com.android.systemui.BatteryMeterView.SHOW_PERCENT_SETTING;
+import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
public class BatteryPreference extends DropDownPreference implements TunerService.Tunable {
@@ -49,7 +49,7 @@
public void onAttached() {
super.onAttached();
mHasPercentage = Settings.System.getInt(getContext().getContentResolver(),
- SHOW_PERCENT_SETTING, 0) != 0;
+ SHOW_BATTERY_PERCENT, 0) != 0;
Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
}
@@ -84,7 +84,7 @@
protected boolean persistString(String value) {
final boolean v = PERCENT.equals(value);
MetricsLogger.action(getContext(), MetricsEvent.TUNER_BATTERY_PERCENTAGE, v);
- Settings.System.putInt(getContext().getContentResolver(), SHOW_PERCENT_SETTING, v ? 1 : 0);
+ Settings.System.putInt(getContext().getContentResolver(), SHOW_BATTERY_PERCENT, v ? 1 : 0);
if (DISABLED.equals(value)) {
mBlacklist.add(mBattery);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index 6a92b2f..369ce69 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -1,226 +1,94 @@
/*
- * Copyright (C) 2015 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.tuner;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.UserInfo;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
import android.os.UserHandle;
-import android.os.UserManager;
import android.provider.Settings;
-import android.provider.Settings.Secure;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import static com.android.systemui.BatteryMeterView.SHOW_PERCENT_SETTING;
+import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
import com.android.systemui.DemoMode;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.SysUiServiceProvider;
-import com.android.systemui.SystemUI;
-import com.android.systemui.SystemUIApplication;
-import com.android.systemui.settings.CurrentUserTracker;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.SystemUIDialog;
-import com.android.systemui.util.leak.LeakDetector;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
-
-
-public class TunerService {
+public abstract class TunerService {
public static final String ACTION_CLEAR = "com.android.systemui.action.CLEAR_TUNER";
- private static final String TUNER_VERSION = "sysui_tuner_version";
+ public abstract void clearAll();
+ public abstract void destroy();
- private static final int CURRENT_TUNER_VERSION = 1;
+ public abstract String getValue(String setting);
+ public abstract int getValue(String setting, int def);
+ public abstract String getValue(String setting, String def);
- private final Observer mObserver = new Observer();
- // Map of Uris we listen on to their settings keys.
- private final ArrayMap<Uri, String> mListeningUris = new ArrayMap<>();
- // Map of settings keys to the listener.
- private final HashMap<String, Set<Tunable>> mTunableLookup = new HashMap<>();
- // Set of all tunables, used for leak detection.
- private final HashSet<Tunable> mTunables = LeakDetector.ENABLED ? new HashSet<>() : null;
- private final Context mContext;
+ public abstract void setValue(String setting, String value);
+ public abstract void setValue(String setting, int value);
- private ContentResolver mContentResolver;
- private int mCurrentUser;
- private CurrentUserTracker mUserTracker;
+ public abstract void addTunable(Tunable tunable, String... keys);
+ public abstract void removeTunable(Tunable tunable);
- public TunerService(Context context) {
- mContext = context;
- mContentResolver = mContext.getContentResolver();
+ public interface Tunable {
+ void onTuningChanged(String key, String newValue);
+ }
- for (UserInfo user : UserManager.get(mContext).getUsers()) {
- mCurrentUser = user.getUserHandle().getIdentifier();
- if (getValue(TUNER_VERSION, 0) != CURRENT_TUNER_VERSION) {
- upgradeTuner(getValue(TUNER_VERSION, 0), CURRENT_TUNER_VERSION);
+ private static Context userContext(Context context) {
+ try {
+ return context.createPackageContextAsUser(context.getPackageName(), 0,
+ new UserHandle(ActivityManager.getCurrentUser()));
+ } catch (NameNotFoundException e) {
+ return context;
+ }
+ }
+
+ public static final void setTunerEnabled(Context context, boolean enabled) {
+ userContext(context).getPackageManager().setComponentEnabledSetting(
+ new ComponentName(context, TunerActivity.class),
+ enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+ : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP);
+
+ userContext(context).getPackageManager().setComponentEnabledSetting(
+ new ComponentName(context, TunerActivity.ACTIVITY_ALIAS_NAME),
+ enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+ : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP);
+ }
+
+ public static final boolean isTunerEnabled(Context context) {
+ return userContext(context).getPackageManager().getComponentEnabledSetting(
+ new ComponentName(context, TunerActivity.class))
+ == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+ }
+
+ public static class ClearReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_CLEAR.equals(intent.getAction())) {
+ Dependency.get(TunerService.class).clearAll();
}
}
-
- mCurrentUser = ActivityManager.getCurrentUser();
- mUserTracker = new CurrentUserTracker(mContext) {
- @Override
- public void onUserSwitched(int newUserId) {
- mCurrentUser = newUserId;
- reloadAll();
- reregisterAll();
- }
- };
- mUserTracker.startTracking();
- }
-
- public void destroy() {
- mUserTracker.stopTracking();
- }
-
- private void upgradeTuner(int oldVersion, int newVersion) {
- if (oldVersion < 1) {
- String blacklistStr = getValue(StatusBarIconController.ICON_BLACKLIST);
- if (blacklistStr != null) {
- ArraySet<String> iconBlacklist =
- StatusBarIconController.getIconBlacklist(blacklistStr);
-
- iconBlacklist.add("rotate");
- iconBlacklist.add("headset");
-
- Settings.Secure.putStringForUser(mContentResolver,
- StatusBarIconController.ICON_BLACKLIST,
- TextUtils.join(",", iconBlacklist), mCurrentUser);
- }
- }
- setValue(TUNER_VERSION, newVersion);
- }
-
- public String getValue(String setting) {
- return Settings.Secure.getStringForUser(mContentResolver, setting, mCurrentUser);
- }
-
- public void setValue(String setting, String value) {
- Settings.Secure.putStringForUser(mContentResolver, setting, value, mCurrentUser);
- }
-
- public int getValue(String setting, int def) {
- return Settings.Secure.getIntForUser(mContentResolver, setting, def, mCurrentUser);
- }
-
- public String getValue(String setting, String def) {
- String ret = Secure.getStringForUser(mContentResolver, setting, mCurrentUser);
- if (ret == null) return def;
- return ret;
- }
-
- public void setValue(String setting, int value) {
- Settings.Secure.putIntForUser(mContentResolver, setting, value, mCurrentUser);
- }
-
- public void addTunable(Tunable tunable, String... keys) {
- for (String key : keys) {
- addTunable(tunable, key);
- }
- }
-
- private void addTunable(Tunable tunable, String key) {
- if (!mTunableLookup.containsKey(key)) {
- mTunableLookup.put(key, new ArraySet<Tunable>());
- }
- mTunableLookup.get(key).add(tunable);
- if (LeakDetector.ENABLED) {
- mTunables.add(tunable);
- Dependency.get(LeakDetector.class).trackCollection(mTunables, "TunerService.mTunables");
- }
- Uri uri = Settings.Secure.getUriFor(key);
- if (!mListeningUris.containsKey(uri)) {
- mListeningUris.put(uri, key);
- mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);
- }
- // Send the first state.
- String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser);
- tunable.onTuningChanged(key, value);
- }
-
- public void removeTunable(Tunable tunable) {
- for (Set<Tunable> list : mTunableLookup.values()) {
- list.remove(tunable);
- }
- if (LeakDetector.ENABLED) {
- mTunables.remove(tunable);
- }
- }
-
- protected void reregisterAll() {
- if (mListeningUris.size() == 0) {
- return;
- }
- mContentResolver.unregisterContentObserver(mObserver);
- for (Uri uri : mListeningUris.keySet()) {
- mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);
- }
- }
-
- public void reloadSetting(Uri uri) {
- String key = mListeningUris.get(uri);
- Set<Tunable> tunables = mTunableLookup.get(key);
- if (tunables == null) {
- return;
- }
- String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser);
- for (Tunable tunable : tunables) {
- tunable.onTuningChanged(key, value);
- }
- }
-
- private void reloadAll() {
- for (String key : mTunableLookup.keySet()) {
- String value = Settings.Secure.getStringForUser(mContentResolver, key,
- mCurrentUser);
- for (Tunable tunable : mTunableLookup.get(key)) {
- tunable.onTuningChanged(key, value);
- }
- }
- }
-
- public void clearAll() {
- // A couple special cases.
- Settings.Global.putString(mContentResolver, DemoMode.DEMO_MODE_ALLOWED, null);
- Settings.System.putString(mContentResolver,
- SHOW_PERCENT_SETTING, null);
- Intent intent = new Intent(DemoMode.ACTION_DEMO);
- intent.putExtra(DemoMode.EXTRA_COMMAND, DemoMode.COMMAND_EXIT);
- mContext.sendBroadcast(intent);
-
- for (String key : mTunableLookup.keySet()) {
- Settings.Secure.putString(mContentResolver, key, null);
- }
}
public static final void showResetRequest(final Context context, final Runnable onDisabled) {
@@ -247,59 +115,4 @@
});
dialog.show();
}
-
- public static final void setTunerEnabled(Context context, boolean enabled) {
- userContext(context).getPackageManager().setComponentEnabledSetting(
- new ComponentName(context, TunerActivity.class),
- enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
- : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
- PackageManager.DONT_KILL_APP);
-
- userContext(context).getPackageManager().setComponentEnabledSetting(
- new ComponentName(context, TunerActivity.ACTIVITY_ALIAS_NAME),
- enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
- : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
- PackageManager.DONT_KILL_APP);
- }
-
- public static final boolean isTunerEnabled(Context context) {
- return userContext(context).getPackageManager().getComponentEnabledSetting(
- new ComponentName(context, TunerActivity.class))
- == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
- }
-
- private static Context userContext(Context context) {
- try {
- return context.createPackageContextAsUser(context.getPackageName(), 0,
- new UserHandle(ActivityManager.getCurrentUser()));
- } catch (NameNotFoundException e) {
- return context;
- }
- }
-
- private class Observer extends ContentObserver {
- public Observer() {
- super(new Handler(Looper.getMainLooper()));
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri, int userId) {
- if (userId == ActivityManager.getCurrentUser()) {
- reloadSetting(uri);
- }
- }
- }
-
- public interface Tunable {
- void onTuningChanged(String key, String newValue);
- }
-
- public static class ClearReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (ACTION_CLEAR.equals(intent.getAction())) {
- Dependency.get(TunerService.class).clearAll();
- }
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
new file mode 100644
index 0000000..8e584bc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -0,0 +1,242 @@
+/*
+ * 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.tuner;
+
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.UserInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.provider.Settings.Secure;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.systemui.DemoMode;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.SystemUI;
+import com.android.systemui.SystemUIApplication;
+import com.android.systemui.settings.CurrentUserTracker;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.util.leak.LeakDetector;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+
+public class TunerServiceImpl extends TunerService {
+
+ private static final String TUNER_VERSION = "sysui_tuner_version";
+
+ private static final int CURRENT_TUNER_VERSION = 1;
+
+ private final Observer mObserver = new Observer();
+ // Map of Uris we listen on to their settings keys.
+ private final ArrayMap<Uri, String> mListeningUris = new ArrayMap<>();
+ // Map of settings keys to the listener.
+ private final HashMap<String, Set<Tunable>> mTunableLookup = new HashMap<>();
+ // Set of all tunables, used for leak detection.
+ private final HashSet<Tunable> mTunables = LeakDetector.ENABLED ? new HashSet<>() : null;
+ private final Context mContext;
+
+ private ContentResolver mContentResolver;
+ private int mCurrentUser;
+ private CurrentUserTracker mUserTracker;
+
+ public TunerServiceImpl(Context context) {
+ mContext = context;
+ mContentResolver = mContext.getContentResolver();
+
+ for (UserInfo user : UserManager.get(mContext).getUsers()) {
+ mCurrentUser = user.getUserHandle().getIdentifier();
+ if (getValue(TUNER_VERSION, 0) != CURRENT_TUNER_VERSION) {
+ upgradeTuner(getValue(TUNER_VERSION, 0), CURRENT_TUNER_VERSION);
+ }
+ }
+
+ mCurrentUser = ActivityManager.getCurrentUser();
+ mUserTracker = new CurrentUserTracker(mContext) {
+ @Override
+ public void onUserSwitched(int newUserId) {
+ mCurrentUser = newUserId;
+ reloadAll();
+ reregisterAll();
+ }
+ };
+ mUserTracker.startTracking();
+ }
+
+ @Override
+ public void destroy() {
+ mUserTracker.stopTracking();
+ }
+
+ private void upgradeTuner(int oldVersion, int newVersion) {
+ if (oldVersion < 1) {
+ String blacklistStr = getValue(StatusBarIconController.ICON_BLACKLIST);
+ if (blacklistStr != null) {
+ ArraySet<String> iconBlacklist =
+ StatusBarIconController.getIconBlacklist(blacklistStr);
+
+ iconBlacklist.add("rotate");
+ iconBlacklist.add("headset");
+
+ Settings.Secure.putStringForUser(mContentResolver,
+ StatusBarIconController.ICON_BLACKLIST,
+ TextUtils.join(",", iconBlacklist), mCurrentUser);
+ }
+ }
+ setValue(TUNER_VERSION, newVersion);
+ }
+
+ @Override
+ public String getValue(String setting) {
+ return Settings.Secure.getStringForUser(mContentResolver, setting, mCurrentUser);
+ }
+
+ @Override
+ public void setValue(String setting, String value) {
+ Settings.Secure.putStringForUser(mContentResolver, setting, value, mCurrentUser);
+ }
+
+ @Override
+ public int getValue(String setting, int def) {
+ return Settings.Secure.getIntForUser(mContentResolver, setting, def, mCurrentUser);
+ }
+
+ @Override
+ public String getValue(String setting, String def) {
+ String ret = Secure.getStringForUser(mContentResolver, setting, mCurrentUser);
+ if (ret == null) return def;
+ return ret;
+ }
+
+ @Override
+ public void setValue(String setting, int value) {
+ Settings.Secure.putIntForUser(mContentResolver, setting, value, mCurrentUser);
+ }
+
+ @Override
+ public void addTunable(Tunable tunable, String... keys) {
+ for (String key : keys) {
+ addTunable(tunable, key);
+ }
+ }
+
+ private void addTunable(Tunable tunable, String key) {
+ if (!mTunableLookup.containsKey(key)) {
+ mTunableLookup.put(key, new ArraySet<Tunable>());
+ }
+ mTunableLookup.get(key).add(tunable);
+ if (LeakDetector.ENABLED) {
+ mTunables.add(tunable);
+ Dependency.get(LeakDetector.class).trackCollection(mTunables, "TunerService.mTunables");
+ }
+ Uri uri = Settings.Secure.getUriFor(key);
+ if (!mListeningUris.containsKey(uri)) {
+ mListeningUris.put(uri, key);
+ mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);
+ }
+ // Send the first state.
+ String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser);
+ tunable.onTuningChanged(key, value);
+ }
+
+ @Override
+ public void removeTunable(Tunable tunable) {
+ for (Set<Tunable> list : mTunableLookup.values()) {
+ list.remove(tunable);
+ }
+ if (LeakDetector.ENABLED) {
+ mTunables.remove(tunable);
+ }
+ }
+
+ protected void reregisterAll() {
+ if (mListeningUris.size() == 0) {
+ return;
+ }
+ mContentResolver.unregisterContentObserver(mObserver);
+ for (Uri uri : mListeningUris.keySet()) {
+ mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);
+ }
+ }
+
+ private void reloadSetting(Uri uri) {
+ String key = mListeningUris.get(uri);
+ Set<Tunable> tunables = mTunableLookup.get(key);
+ if (tunables == null) {
+ return;
+ }
+ String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser);
+ for (Tunable tunable : tunables) {
+ tunable.onTuningChanged(key, value);
+ }
+ }
+
+ private void reloadAll() {
+ for (String key : mTunableLookup.keySet()) {
+ String value = Settings.Secure.getStringForUser(mContentResolver, key,
+ mCurrentUser);
+ for (Tunable tunable : mTunableLookup.get(key)) {
+ tunable.onTuningChanged(key, value);
+ }
+ }
+ }
+
+ @Override
+ public void clearAll() {
+ // A couple special cases.
+ Settings.Global.putString(mContentResolver, DemoMode.DEMO_MODE_ALLOWED, null);
+ Intent intent = new Intent(DemoMode.ACTION_DEMO);
+ intent.putExtra(DemoMode.EXTRA_COMMAND, DemoMode.COMMAND_EXIT);
+ mContext.sendBroadcast(intent);
+
+ for (String key : mTunableLookup.keySet()) {
+ Settings.Secure.putString(mContentResolver, key, null);
+ }
+ }
+
+ private class Observer extends ContentObserver {
+ public Observer() {
+ super(new Handler(Looper.getMainLooper()));
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri, int userId) {
+ if (userId == ActivityManager.getCurrentUser()) {
+ reloadSetting(uri);
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index 760d875..576299f 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -49,7 +49,8 @@
mockito-updated-target-minus-junit4 \
SystemUI-proto \
SystemUI-tags \
- legacy-android-test
+ legacy-android-test \
+ testables
LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common android.car
diff --git a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
index 27955ec..c297ae8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
@@ -39,21 +39,21 @@
@Test
public void testClassDependency() {
FlashlightController f = mock(FlashlightController.class);
- injectTestDependency(FlashlightController.class, f);
+ mDependency.injectTestDependency(FlashlightController.class, f);
Assert.assertEquals(f, Dependency.get(FlashlightController.class));
}
@Test
public void testStringDependency() {
Looper l = Looper.getMainLooper();
- injectTestDependency(Dependency.BG_LOOPER, l);
+ mDependency.injectTestDependency(Dependency.BG_LOOPER, l);
assertEquals(l, Dependency.get(Dependency.BG_LOOPER));
}
@Test
public void testDump() {
Dumpable d = mock(Dumpable.class);
- injectTestDependency(DUMPABLE, d);
+ mDependency.injectTestDependency(DUMPABLE, d);
Dependency.get(DUMPABLE);
mDependency.dump(null, mock(PrintWriter.class), null);
verify(d).dump(eq(null), any(), eq(null));
@@ -62,7 +62,7 @@
@Test
public void testConfigurationChanged() {
ConfigurationChangedReceiver d = mock(ConfigurationChangedReceiver.class);
- injectTestDependency(CONFIGURATION_CHANGED_RECEIVER, d);
+ mDependency.injectTestDependency(CONFIGURATION_CHANGED_RECEIVER, d);
Dependency.get(CONFIGURATION_CHANGED_RECEIVER);
mDependency.onConfigurationChanged(null);
verify(d).onConfigurationChanged(eq(null));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
new file mode 100644
index 0000000..15cebc7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.systemui;
+
+import android.app.Fragment;
+import android.support.test.InstrumentationRegistry;
+import android.testing.BaseFragmentTest;
+
+import com.android.systemui.utils.leaks.LeakCheckedTest;
+import com.android.systemui.utils.leaks.LeakCheckedTest.SysuiLeakCheck;
+
+import org.junit.Before;
+import org.junit.Rule;
+
+public abstract class SysuiBaseFragmentTest extends BaseFragmentTest {
+
+ public static final Class<?>[] ALL_SUPPORTED_CLASSES = LeakCheckedTest.ALL_SUPPORTED_CLASSES;
+
+ @Rule
+ public final SysuiLeakCheck mLeakCheck = new SysuiLeakCheck();
+
+ protected final TestableDependency mDependency = new TestableDependency(mContext);
+ protected SysuiTestableContext mSysuiContext;
+
+ public SysuiBaseFragmentTest(Class<? extends Fragment> cls) {
+ super(cls);
+ }
+
+ @Before
+ public void SysuiSetup() {
+ System.setProperty("dexmaker.share_classloader", "true");
+ SystemUIFactory.createFromConfig(mContext);
+ // TODO: Figure out another way to give reference to a SysuiTestableContext.
+ mSysuiContext = (SysuiTestableContext) mContext;
+ }
+
+ @Override
+ protected SysuiTestableContext getContext() {
+ return new SysuiTestableContext(InstrumentationRegistry.getContext(), mLeakCheck);
+ }
+
+ public void injectLeakCheckedDependencies(Class<?>... cls) {
+ for (Class<?> c : cls) {
+ injectLeakCheckedDependency(c);
+ }
+ }
+
+ public <T> void injectLeakCheckedDependency(Class<T> c) {
+ mDependency.injectTestDependency(c, mLeakCheck.getLeakChecker(c));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index c0e7e80..aadae0f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -15,49 +15,38 @@
*/
package com.android.systemui;
-import static org.mockito.Mockito.mock;
-
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.MessageQueue;
import android.support.test.InstrumentationRegistry;
-import android.util.ArrayMap;
+import android.testing.LeakCheck;
-import com.android.systemui.Dependency.DependencyKey;
-import com.android.systemui.utils.TestableContext;
-import com.android.systemui.utils.leaks.Tracker;
-
-import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
/**
* Base class that does System UI specific setup.
*/
public abstract class SysuiTestCase {
- private Throwable mException;
private Handler mHandler;
- protected TestableContext mContext;
- protected TestDependency mDependency;
+ @Rule
+ public SysuiTestableContext mContext = new SysuiTestableContext(
+ InstrumentationRegistry.getContext(), getLeakCheck());
+ public TestableDependency mDependency = new TestableDependency(mContext);
@Before
public void SysuiSetup() throws Exception {
- mException = null;
System.setProperty("dexmaker.share_classloader", "true");
- mContext = new TestableContext(InstrumentationRegistry.getTargetContext(), this);
SystemUIFactory.createFromConfig(mContext);
- mDependency = new TestDependency();
- mDependency.mContext = mContext;
- mDependency.start();
}
- @After
- public void cleanup() throws Exception {
- mContext.getSettingsProvider().clearOverrides(this);
+ protected LeakCheck getLeakCheck() {
+ return null;
}
- protected Context getContext() {
+ public Context getContext() {
return mContext;
}
@@ -84,48 +73,11 @@
}
}
- // Used for leak tracking, returns null to indicate no leak tracking by default.
- public Tracker getTracker(String tag) {
- return null;
- }
-
- public <T> T injectMockDependency(Class<T> cls) {
- final T mock = mock(cls);
- mDependency.injectTestDependency(cls, mock);
- return mock;
- }
-
- public <T> void injectTestDependency(Class<T> cls, T obj) {
- mDependency.injectTestDependency(cls, obj);
- }
-
- public <T> void injectTestDependency(DependencyKey<T> key, T obj) {
- mDependency.injectTestDependency(key, obj);
- }
-
public static final class EmptyRunnable implements Runnable {
public void run() {
}
}
- public static class TestDependency extends Dependency {
- private final ArrayMap<Object, Object> mObjs = new ArrayMap<>();
-
- private <T> void injectTestDependency(DependencyKey<T> key, T obj) {
- mObjs.put(key, obj);
- }
-
- private <T> void injectTestDependency(Class<T> key, T obj) {
- mObjs.put(key, obj);
- }
-
- @Override
- protected <T> T createDependency(Object key) {
- if (mObjs.containsKey(key)) return (T) mObjs.get(key);
- return super.createDependency(key);
- }
- }
-
public static final class Idler implements MessageQueue.IdleHandler {
private final Runnable mCallback;
private boolean mIdle;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
new file mode 100644
index 0000000..b94a2fb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
@@ -0,0 +1,43 @@
+/*
+ * 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.systemui;
+
+import android.content.Context;
+import android.testing.LeakCheck;
+import android.testing.TestableContext;
+import android.util.ArrayMap;
+
+public class SysuiTestableContext extends TestableContext implements SysUiServiceProvider {
+
+ private ArrayMap<Class<?>, Object> mComponents;
+
+ public SysuiTestableContext(Context base) {
+ super(base);
+ }
+
+ public SysuiTestableContext(Context base, LeakCheck check) {
+ super(base, check);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T getComponent(Class<T> interfaceType) {
+ return (T) (mComponents != null ? mComponents.get(interfaceType) : null);
+ }
+
+ public <T, C extends T> void putComponent(Class<T> interfaceType, C component) {
+ if (mComponents == null) mComponents = new ArrayMap<>();
+ mComponents.put(interfaceType, component);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
new file mode 100644
index 0000000..53a7994
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
@@ -0,0 +1,52 @@
+/*
+ * 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.systemui;
+
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.util.ArrayMap;
+
+public class TestableDependency extends Dependency {
+ private final ArrayMap<Object, Object> mObjs = new ArrayMap<>();
+
+ public TestableDependency(Context context) {
+ mContext = context;
+ if (SystemUIFactory.getInstance() == null) {
+ SystemUIFactory.createFromConfig(context);
+ }
+ start();
+ }
+
+ public <T> T injectMockDependency(Class<T> cls) {
+ final T mock = mock(cls);
+ injectTestDependency(cls, mock);
+ return mock;
+ }
+
+ public <T> void injectTestDependency(DependencyKey<T> key, T obj) {
+ mObjs.put(key, obj);
+ }
+
+ public <T> void injectTestDependency(Class<T> key, T obj) {
+ mObjs.put(key, obj);
+ }
+
+ @Override
+ protected <T> T createDependency(Object key) {
+ if (mObjs.containsKey(key)) return (T) mObjs.get(key);
+ return super.createDependency(key);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java
index 5477afa8..048936b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java
@@ -47,7 +47,7 @@
return;
}
- mContext.getSettingsProvider().acquireOverridesBuilder(this)
+ mContext.getSettingsProvider().acquireOverridesBuilder()
.addSetting("secure", Settings.Secure.DOZE_ALWAYS_ON, null)
.build();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index ba39671..cdbde5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -39,19 +39,20 @@
import static org.mockito.Mockito.when;
import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
import android.view.Display;
-import com.android.systemui.SysUIRunner;
-import com.android.systemui.UiThreadTest;
import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.systemui.util.wakelock.WakeLockFake;
+import android.testing.UiThreadTest;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
@UiThreadTest
public class DozeMachineTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notification/PropertyAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/notification/PropertyAnimatorTest.java
index 53053fa..8484bed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notification/PropertyAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/notification/PropertyAnimatorTest.java
@@ -14,22 +14,25 @@
package com.android.systemui.notification;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
-import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-
+import android.testing.AndroidTestingRunner;
+import android.testing.UiThreadTest;
import android.util.FloatProperty;
import android.util.Property;
import android.view.View;
import android.view.animation.Interpolator;
-import com.android.systemui.SysUIRunner;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.UiThreadTest;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.stack.AnimationFilter;
import com.android.systemui.statusbar.stack.AnimationProperties;
@@ -39,18 +42,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
@SmallTest
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
@UiThreadTest
public class PropertyAnimatorTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
index 658966c..4f0815d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
@@ -72,7 +72,7 @@
private PackageManager mMockPm;
private PluginListener mMockListener;
private PluginInstanceManager mPluginInstanceManager;
- private PluginManager mMockManager;
+ private PluginManagerImpl mMockManager;
private VersionInfo mMockVersionInfo;
@Before
@@ -82,7 +82,7 @@
mContextWrapper = new MyContextWrapper(getContext());
mMockPm = mock(PackageManager.class);
mMockListener = mock(PluginListener.class);
- mMockManager = mock(PluginManager.class);
+ mMockManager = mock(PluginManagerImpl.class);
when(mMockManager.getClassLoader(any(), any()))
.thenReturn(getClass().getClassLoader());
mMockVersionInfo = mock(VersionInfo.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
index 053e5cf2..a3d5d5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
@@ -34,7 +34,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.annotations.ProvidesInterface;
import com.android.systemui.plugins.PluginInstanceManager.PluginInfo;
-import com.android.systemui.plugins.PluginManager.PluginInstanceManagerFactory;
+import com.android.systemui.plugins.PluginManagerImpl.PluginInstanceManagerFactory;
import org.junit.Before;
import org.junit.Test;
@@ -50,7 +50,7 @@
private PluginInstanceManagerFactory mMockFactory;
private PluginInstanceManager mMockPluginInstance;
- private PluginManager mPluginManager;
+ private PluginManagerImpl mPluginManager;
private PluginListener mMockListener;
private UncaughtExceptionHandler mRealExceptionHandler;
@@ -66,7 +66,8 @@
when(mMockFactory.createPluginInstanceManager(Mockito.any(), Mockito.any(), Mockito.any(),
Mockito.anyBoolean(), Mockito.any(), Mockito.any(), Mockito.any()))
.thenReturn(mMockPluginInstance);
- mPluginManager = new PluginManager(getContext(), mMockFactory, true, mMockExceptionHandler);
+ mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, true,
+ mMockExceptionHandler);
resetExceptionHandler();
mMockListener = mock(PluginListener.class);
}
@@ -98,7 +99,7 @@
@Test
public void testNonDebuggable() {
- mPluginManager = new PluginManager(getContext(), mMockFactory, false,
+ mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, false,
mMockExceptionHandler);
resetExceptionHandler();
@@ -148,7 +149,7 @@
ComponentName testComponent = new ComponentName(getContext().getPackageName(),
PluginManagerTest.class.getName());
- Intent intent = new Intent(PluginManager.DISABLE_PLUGIN);
+ Intent intent = new Intent(PluginManagerImpl.DISABLE_PLUGIN);
intent.setData(Uri.parse("package://" + testComponent.flattenToString()));
mPluginManager.onReceive(mContext, intent);
verify(nm).cancel(eq(testComponent.getClassName()), eq(SystemMessage.NOTE_PLUGIN));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 7153340..deb31da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -21,14 +21,16 @@
import com.android.keyguard.CarrierText;
import com.android.systemui.Dependency;
-import com.android.systemui.FragmentTestCase;
import com.android.systemui.R;
-import com.android.systemui.SysUIRunner;
+
+import android.testing.AndroidTestingRunner;
+
+import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.util.LayoutInflaterBuilder;
-import com.android.systemui.utils.TestableLooper;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
+import android.testing.LayoutInflaterBuilder;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
import org.junit.Before;
import org.junit.Test;
@@ -38,9 +40,9 @@
import android.view.View;
import android.widget.FrameLayout;
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
-public class QSFragmentTest extends FragmentTestCase {
+public class QSFragmentTest extends SysuiBaseFragmentTest {
public QSFragmentTest() {
super(QSFragment.class);
@@ -56,8 +58,9 @@
.replace(CarrierText.class, View.class)
.build());
- injectTestDependency(Dependency.BG_LOOPER, TestableLooper.get(this).getLooper());
- injectMockDependency(UserSwitcherController.class);
+ mDependency.injectTestDependency(Dependency.BG_LOOPER,
+ TestableLooper.get(this).getLooper());
+ mDependency.injectMockDependency(UserSwitcherController.class);
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index e38c30f..1ff373c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -33,8 +33,8 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.policy.SecurityController;
-import com.android.systemui.util.LayoutInflaterBuilder;
-import com.android.systemui.utils.TestableImageView;
+import android.testing.LayoutInflaterBuilder;
+import android.testing.TestableImageView;
import org.junit.Before;
import org.junit.Test;
@@ -57,8 +57,8 @@
@Before
public void setUp() {
- injectTestDependency(SecurityController.class, mSecurityController);
- injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
+ mDependency.injectTestDependency(SecurityController.class, mSecurityController);
+ mDependency.injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE,
new LayoutInflaterBuilder(mContext)
.replace("ImageView", TestableImageView.class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index d1e17f4..5ae107a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -21,13 +21,13 @@
import static org.mockito.Mockito.when;
import com.android.systemui.Dependency;
-import com.android.systemui.SysUIRunner;
+import android.testing.AndroidTestingRunner;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.State;
import com.android.systemui.qs.QSTileHost;
-import com.android.systemui.utils.TestableLooper;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
import org.junit.Before;
import org.junit.Test;
@@ -37,7 +37,7 @@
import android.test.suitebuilder.annotation.SmallTest;
@SmallTest
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class TileQueryHelperTest extends SysuiTestCase {
private TestableLooper mBGLooper;
@@ -46,7 +46,7 @@
@Before
public void setup() {
mBGLooper = TestableLooper.get(this);
- injectTestDependency(Dependency.BG_LOOPER, mBGLooper.getLooper());
+ mDependency.injectTestDependency(Dependency.BG_LOOPER, mBGLooper.getLooper());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index 6d7b50f..ddd6615 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -25,12 +25,12 @@
import android.service.quicksettings.Tile;
import android.test.suitebuilder.annotation.SmallTest;
-import com.android.systemui.SysUIRunner;
+import android.testing.AndroidTestingRunner;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.utils.TestableLooper;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
import org.junit.After;
import org.junit.Before;
@@ -42,7 +42,7 @@
import java.util.ArrayList;
@SmallTest
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
public class TileServicesTest extends SysuiTestCase {
private static int NUM_FAKES = TileServices.DEFAULT_MAX_BOUND * 2;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 6424a0a..bdd05c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar;
-import static android.support.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread;
-
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
@@ -34,12 +32,11 @@
import android.os.Looper;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
-import android.support.test.internal.runner.junit4.statement.UiThreadStatement;
import android.support.test.runner.AndroidJUnit4;
import android.view.View;
import android.view.ViewGroup;
-import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
@@ -124,7 +121,7 @@
when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false);
createController();
- final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
+ final KeyguardUpdateMonitorCallback monitor = mController.getKeyguardCallback();
reset(mDisclosure);
when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
@@ -186,12 +183,10 @@
});
mInstrumentation.waitForIdleSync();
- boolean[] held = new boolean[2];
+ Boolean[] held = new Boolean[1];
mInstrumentation.runOnMainSync(() -> {
held[0] = mWakeLock.isHeld();
- held[1] = true;
});
- assertFalse("wake lock still held", held[0]);
- assertTrue("held was not written yet", held[1]);
+ assertFalse("WakeLock expected: RELEASED, was: HELD", held[0]);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
index 8520bdb..5b9270d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
@@ -33,41 +33,35 @@
import static org.mockito.Mockito.when;
import android.app.INotificationManager;
-import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ParceledListSlice;
import android.graphics.drawable.Drawable;
import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
+import android.testing.AndroidTestingRunner;
+import android.testing.UiThreadTest;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.Switch;
import android.widget.TextView;
-import com.android.internal.util.CharSequences;
+
import com.android.systemui.R;
-import com.android.systemui.SysUIRunner;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.UiThreadTest;
import org.junit.Before;
-import org.junit.runner.RunWith;
import org.junit.Test;
-import org.mockito.Mockito;
-import java.util.Arrays;
+import org.junit.runner.RunWith;
+
import java.util.Collections;
import java.util.concurrent.CountDownLatch;
@SmallTest
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
@UiThreadTest
public class NotificationInfoTest extends SysuiTestCase {
private static final String TEST_PACKAGE_NAME = "test_package";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java
index b8be4fa..c2c6336 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java
@@ -14,17 +14,17 @@
package com.android.systemui.statusbar;
-import com.android.systemui.SysUIRunner;
-import com.android.systemui.utils.TestableLooper;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
-import com.android.systemui.utils.ViewUtils;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+import android.testing.ViewUtils;
import com.android.systemui.utils.leaks.LeakCheckedTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
public class NotificationMenuRowTest extends LeakCheckedTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 3ccb160..e41a827 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -22,7 +22,7 @@
import com.android.internal.app.NightDisplayController;
import com.android.systemui.Prefs;
import com.android.systemui.Prefs.Key;
-import com.android.systemui.SysUIRunner;
+import android.testing.AndroidTestingRunner;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.qs.QSTileHost;
@@ -32,7 +32,7 @@
import org.junit.runner.RunWith;
import org.mockito.Mockito;
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
public class AutoTileManagerTest extends SysuiTestCase {
private QSTileHost mQsTileHost;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index f55115e..a9acda3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -23,24 +23,22 @@
import android.view.View;
import android.view.ViewPropertyAnimator;
-import com.android.systemui.FragmentTestCase;
import com.android.systemui.R;
-import com.android.systemui.SysUIRunner;
+import android.testing.AndroidTestingRunner;
+
+import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.SecurityController;
import com.android.systemui.tuner.TunerService;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
+import android.testing.TestableLooper.RunWithLooper;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.junit.Test;
import org.mockito.Mockito;
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
-public class CollapsedStatusBarFragmentTest extends FragmentTestCase {
+public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
private NotificationIconAreaController mMockNotificiationAreaController;
private View mNotificationAreaInner;
@@ -51,9 +49,9 @@
@Before
public void setup() {
- mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
- mContext.putComponent(StatusBar.class, mock(StatusBar.class));
- mContext.putComponent(TunerService.class, mock(TunerService.class));
+ mSysuiContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
+ mSysuiContext.putComponent(StatusBar.class, mock(StatusBar.class));
+ mSysuiContext.putComponent(TunerService.class, mock(TunerService.class));
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
mMockNotificiationAreaController = mock(NotificationIconAreaController.class);
mNotificationAreaInner = mock(View.class);
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 1fa9846..e7cdcb5 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
@@ -19,24 +19,25 @@
import android.content.Context;
import android.os.Looper;
+import android.testing.AndroidTestingRunner;
import android.view.Display;
import android.view.WindowManager;
import com.android.systemui.Dependency;
-import com.android.systemui.FragmentTestCase;
-import com.android.systemui.SysUIRunner;
+
+import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.recents.Recents;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
+import android.testing.TestableLooper.RunWithLooper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
-public class NavigationBarFragmentTest extends FragmentTestCase {
+public class NavigationBarFragmentTest extends SysuiBaseFragmentTest {
public NavigationBarFragmentTest() {
super(NavigationBarFragment.class);
@@ -44,11 +45,11 @@
@Before
public void setup() {
- injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
- mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
- mContext.putComponent(StatusBar.class, mock(StatusBar.class));
- mContext.putComponent(Recents.class, mock(Recents.class));
- mContext.putComponent(Divider.class, mock(Divider.class));
+ mDependency.injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
+ mSysuiContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
+ mSysuiContext.putComponent(StatusBar.class, mock(StatusBar.class));
+ mSysuiContext.putComponent(Recents.class, mock(Recents.class));
+ mSysuiContext.putComponent(Divider.class, mock(Divider.class));
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
WindowManager windowManager = mock(WindowManager.class);
Display defaultDisplay = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerTest.java
index e3a5ef0..3e79a04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerTest.java
@@ -42,8 +42,8 @@
@Before
public void setup() {
- mPluginManager = injectMockDependency(PluginManager.class);
- mTunerService = injectMockDependency(TunerService.class);
+ mPluginManager = mDependency.injectMockDependency(PluginManager.class);
+ mTunerService = mDependency.injectMockDependency(TunerService.class);
mExtensionController = Dependency.get(ExtensionController.class);
}
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 8cbf95b..efa232b 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
@@ -18,7 +18,7 @@
import com.android.settingslib.Utils;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.utils.FakeSettingsProvider.SettingOverrider;
+import android.testing.TestableSettings.SettingOverrider;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -92,9 +92,9 @@
attr);
// Must set the Settings value before instantiating the NetworkControllerImpl due to bugs in
- // FakeSettingsProvider.
+ // TestableSettings.
SettingOverrider settingsOverrider =
- mContext.getSettingsProvider().acquireOverridesBuilder(this)
+ mContext.getSettingsProvider().acquireOverridesBuilder()
.addSetting("global", Settings.Global.NETWORK_SCORING_UI_ENABLED, "1")
.build();
super.setUp(); // re-instantiate NetworkControllImpl now that setting has been updated
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProvider.java b/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProvider.java
deleted file mode 100644
index f40fe4c..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProvider.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.utils;
-
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.provider.Settings;
-import android.support.annotation.VisibleForTesting;
-import android.test.mock.MockContentProvider;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.utils.FakeSettingsProvider.SettingOverrider.Builder;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Allows calls to android.provider.Settings to be tested easier. A SettingOverride
- * can be acquired and a set of specific settings can be set to a value (and not changed
- * in the system when set), so that they can be tested without breaking the test device.
- * <p>
- * To use, in the before method acquire the override add all settings that will affect if
- * your test passes or not.
- *
- * <pre class="prettyprint">
- * {@literal
- * mSettingOverride = mTestableContext.getSettingsProvider().acquireOverridesBuilder()
- * .addSetting("secure", Secure.USER_SETUP_COMPLETE, "0")
- * .build();
- * }
- * </pre>
- *
- * Then in the after free up the settings.
- *
- * <pre class="prettyprint">
- * {@literal
- * mSettingOverride.release();
- * }
- * </pre>
- */
-public class FakeSettingsProvider extends MockContentProvider {
-
- private static final String TAG = "FakeSettingsProvider";
- private static final boolean DEBUG = false;
-
- // Number of times to try to acquire a setting if in use.
- private static final int MAX_TRIES = 10;
- // Time to wait for each setting. WAIT_TIMEOUT * MAX_TRIES will be the maximum wait time
- // for a setting.
- private static final long WAIT_TIMEOUT = 1000;
-
- private final Map<String, SettingOverrider> mOverrideMap = new ArrayMap<>();
- private final Map<SysuiTestCase, List<SettingOverrider>> mOwners = new ArrayMap<>();
-
- private static FakeSettingsProvider sInstance;
- private final ContentProviderClient mSettings;
- private final ContentResolver mResolver;
-
- private FakeSettingsProvider(ContentProviderClient settings, ContentResolver resolver) {
- mSettings = settings;
- mResolver = resolver;
- }
-
- public Builder acquireOverridesBuilder(SysuiTestCase test) {
- return new Builder(this, test);
- }
-
- public void clearOverrides(SysuiTestCase test) {
- List<SettingOverrider> overrides = mOwners.remove(test);
- if (overrides != null) {
- overrides.forEach(override -> override.ensureReleased());
- }
- }
-
- public Bundle call(String method, String arg, Bundle extras) {
- // Methods are "GET_system", "GET_global", "PUT_secure", etc.
- final String[] commands = method.split("_", 2);
- final String op = commands[0];
- final String table = commands[1];
-
- synchronized (mOverrideMap) {
- SettingOverrider overrider = mOverrideMap.get(key(table, arg));
- if (overrider == null) {
- // Fall through to real settings.
- try {
- if (DEBUG) Log.d(TAG, "Falling through to real settings " + method);
- // TODO: Add our own version of caching to handle this.
- Bundle call = mSettings.call(method, arg, extras);
- call.remove(Settings.CALL_METHOD_TRACK_GENERATION_KEY);
- return call;
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
- String value;
- Bundle out = new Bundle();
- switch (op) {
- case "GET":
- value = overrider.get(table, arg);
- if (value != null) {
- out.putString(Settings.NameValueTable.VALUE, value);
- }
- break;
- case "PUT":
- value = extras.getString(Settings.NameValueTable.VALUE, null);
- if (value != null) {
- overrider.put(table, arg, value);
- } else {
- overrider.remove(table, arg);
- }
- break;
- default:
- throw new UnsupportedOperationException("Unknown command " + method);
- }
- return out;
- }
- }
-
- private void acquireSettings(SettingOverrider overridder, Set<String> keys,
- SysuiTestCase owner) throws AcquireTimeoutException {
- synchronized (mOwners) {
- List<SettingOverrider> list = mOwners.get(owner);
- if (list == null) {
- list = new ArrayList<>();
- mOwners.put(owner, list);
- }
- list.add(overridder);
- }
- synchronized (mOverrideMap) {
- for (int i = 0; i < MAX_TRIES; i++) {
- if (checkKeys(keys, false)) break;
- try {
- if (DEBUG) Log.d(TAG, "Waiting for contention to finish");
- mOverrideMap.wait(WAIT_TIMEOUT);
- } catch (InterruptedException e) {
- }
- }
- checkKeys(keys, true);
- for (String key : keys) {
- if (DEBUG) Log.d(TAG, "Acquiring " + key);
- mOverrideMap.put(key, overridder);
- }
- }
- }
-
- private void releaseSettings(Set<String> keys) {
- synchronized (mOverrideMap) {
- for (String key : keys) {
- if (DEBUG) Log.d(TAG, "Releasing " + key);
- mOverrideMap.remove(key);
- }
- if (DEBUG) Log.d(TAG, "Notifying");
- mOverrideMap.notify();
- }
- }
-
- @VisibleForTesting
- public Object getLock() {
- return mOverrideMap;
- }
-
- private boolean checkKeys(Set<String> keys, boolean shouldThrow)
- throws AcquireTimeoutException {
- for (String key : keys) {
- if (mOverrideMap.containsKey(key)) {
- if (shouldThrow) {
- throw new AcquireTimeoutException("Could not acquire " + key);
- }
- return false;
- }
- }
- return true;
- }
-
- public static class SettingOverrider {
- private final Set<String> mValidKeys;
- private final Map<String, String> mValueMap = new ArrayMap<>();
- private final FakeSettingsProvider mProvider;
- private boolean mReleased;
-
- private SettingOverrider(Set<String> keys, FakeSettingsProvider provider) {
- mValidKeys = new ArraySet<>(keys);
- mProvider = provider;
- }
-
- private void ensureReleased() {
- if (!mReleased) {
- release();
- }
- }
-
- public void release() {
- mProvider.releaseSettings(mValidKeys);
- mReleased = true;
- }
-
- private void putDirect(String key, String value) {
- mValueMap.put(key, value);
- }
-
- public void put(String table, String key, String value) {
- if (!mValidKeys.contains(key(table, key))) {
- throw new IllegalArgumentException("Key " + table + " " + key
- + " not acquired for this overrider");
- }
- mValueMap.put(key(table, key), value);
- }
-
- public void remove(String table, String key) {
- if (!mValidKeys.contains(key(table, key))) {
- throw new IllegalArgumentException("Key " + table + " " + key
- + " not acquired for this overrider");
- }
- mValueMap.remove(key(table, key));
- }
-
- public String get(String table, String key) {
- if (!mValidKeys.contains(key(table, key))) {
- throw new IllegalArgumentException("Key " + table + " " + key
- + " not acquired for this overrider");
- }
- Log.d(TAG, "Get " + table + " " + key + " " + mValueMap.get(key(table, key)));
- return mValueMap.get(key(table, key));
- }
-
- public static class Builder {
- private final FakeSettingsProvider mProvider;
- private final SysuiTestCase mOwner;
- private Set<String> mKeys = new ArraySet<>();
- private Map<String, String> mValues = new ArrayMap<>();
-
- private Builder(FakeSettingsProvider provider, SysuiTestCase test) {
- mProvider = provider;
- mOwner = test;
- }
-
- public Builder addSetting(String table, String key) {
- mKeys.add(key(table, key));
- return this;
- }
-
- public Builder addSetting(String table, String key, String value) {
- addSetting(table, key);
- mValues.put(key(table, key), value);
- return this;
- }
-
- public SettingOverrider build() throws AcquireTimeoutException {
- SettingOverrider overrider = new SettingOverrider(mKeys, mProvider);
- mProvider.acquireSettings(overrider, mKeys, mOwner);
- mValues.forEach((key, value) -> overrider.putDirect(key, value));
- return overrider;
- }
- }
- }
-
- public static class AcquireTimeoutException extends Exception {
- public AcquireTimeoutException(String str) {
- super(str);
- }
- }
-
- private static String key(String table, String key) {
- return table + "_" + key;
- }
-
- /**
- * Since the settings provider is cached inside android.provider.Settings, this must
- * be gotten statically to ensure there is only one instance referenced.
- * @param settings
- */
- public static FakeSettingsProvider getFakeSettingsProvider(ContentProviderClient settings,
- ContentResolver resolver) {
- if (sInstance == null) {
- sInstance = new FakeSettingsProvider(settings, resolver);
- }
- return sInstance;
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java
index b118fdc..d94ecc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 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
@@ -14,6 +14,9 @@
package com.android.systemui.utils.leaks;
+import android.testing.LeakCheck;
+import android.testing.LeakCheck.Tracker;
+
import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.policy.CallbackController;
@@ -24,7 +27,7 @@
private final Tracker mTracker;
- public BaseLeakChecker(LeakCheckedTest test, String tag) {
+ public BaseLeakChecker(LeakCheck test, String tag) {
mTracker = test.getTracker(tag);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
index fa07d33..a843cca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
@@ -15,6 +15,7 @@
package com.android.systemui.utils.leaks;
import android.os.Bundle;
+import android.testing.LeakCheck;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -24,7 +25,7 @@
public class FakeBatteryController extends BaseLeakChecker<BatteryStateChangeCallback>
implements BatteryController {
- public FakeBatteryController(LeakCheckedTest test) {
+ public FakeBatteryController(LeakCheck test) {
super(test, "battery");
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
index 6074a01..0ba0319 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
@@ -14,6 +14,8 @@
package com.android.systemui.utils.leaks;
+import android.testing.LeakCheck;
+
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.BluetoothController.Callback;
@@ -23,7 +25,7 @@
public class FakeBluetoothController extends BaseLeakChecker<Callback> implements
BluetoothController {
- public FakeBluetoothController(LeakCheckedTest test) {
+ public FakeBluetoothController(LeakCheck test) {
super(test, "bluetooth");
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeCastController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeCastController.java
index 08211f8..51149ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeCastController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeCastController.java
@@ -14,13 +14,15 @@
package com.android.systemui.utils.leaks;
+import android.testing.LeakCheck;
+
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.Callback;
import java.util.Set;
public class FakeCastController extends BaseLeakChecker<Callback> implements CastController {
- public FakeCastController(LeakCheckedTest test) {
+ public FakeCastController(LeakCheck test) {
super(test, "cast");
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeDataSaverController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeDataSaverController.java
index 857a785..886722e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeDataSaverController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeDataSaverController.java
@@ -14,12 +14,14 @@
package com.android.systemui.utils.leaks;
+import android.testing.LeakCheck;
+
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DataSaverController.Listener;
public class FakeDataSaverController extends BaseLeakChecker<Listener> implements DataSaverController {
- public FakeDataSaverController(LeakCheckedTest test) {
+ public FakeDataSaverController(LeakCheck test) {
super(test, "datasaver");
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeExtensionController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeExtensionController.java
index c0f5783..b9d188a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeExtensionController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeExtensionController.java
@@ -14,7 +14,8 @@
package com.android.systemui.utils.leaks;
-import static org.mockito.Mockito.mock;
+import android.testing.LeakCheck;
+import android.testing.LeakCheck.Tracker;
import com.android.systemui.statusbar.policy.ExtensionController;
@@ -25,7 +26,7 @@
private final Tracker mTracker;
- public FakeExtensionController(LeakCheckedTest test) {
+ public FakeExtensionController(LeakCheck test) {
mTracker = test.getTracker("extension");
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeFlashlightController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeFlashlightController.java
index 630abd7..f6fd2cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeFlashlightController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeFlashlightController.java
@@ -14,12 +14,14 @@
package com.android.systemui.utils.leaks;
+import android.testing.LeakCheck;
+
import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.policy.FlashlightController.FlashlightListener;
public class FakeFlashlightController extends BaseLeakChecker<FlashlightListener>
implements FlashlightController {
- public FakeFlashlightController(LeakCheckedTest test) {
+ public FakeFlashlightController(LeakCheck test) {
super(test, "flashlight");
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java
index 781960d..69e2361 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java
@@ -14,12 +14,14 @@
package com.android.systemui.utils.leaks;
+import android.testing.LeakCheck;
+
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.HotspotController.Callback;
public class FakeHotspotController extends BaseLeakChecker<Callback> implements HotspotController {
- public FakeHotspotController(LeakCheckedTest test) {
+ public FakeHotspotController(LeakCheck test) {
super(test, "hotspot");
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java
index 21871fc..51e35cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java
@@ -14,13 +14,15 @@
package com.android.systemui.utils.leaks;
+import android.testing.LeakCheck;
+
import com.android.systemui.statusbar.policy.KeyguardMonitor;
public class FakeKeyguardMonitor implements KeyguardMonitor {
private final BaseLeakChecker<Callback> mCallbackController;
- public FakeKeyguardMonitor(LeakCheckedTest test) {
+ public FakeKeyguardMonitor(LeakCheck test) {
mCallbackController = new BaseLeakChecker<Callback>(test, "keyguard");
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeLocationController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeLocationController.java
index eab436c..29d7f1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeLocationController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeLocationController.java
@@ -14,12 +14,14 @@
package com.android.systemui.utils.leaks;
+import android.testing.LeakCheck;
+
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.LocationController.LocationSettingsChangeCallback;
public class FakeLocationController extends BaseLeakChecker<LocationSettingsChangeCallback>
implements LocationController {
- public FakeLocationController(LeakCheckedTest test) {
+ public FakeLocationController(LeakCheck test) {
super(test, "location");
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeManagedProfileController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeManagedProfileController.java
index 0ec0d77..18b07cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeManagedProfileController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeManagedProfileController.java
@@ -14,12 +14,14 @@
package com.android.systemui.utils.leaks;
+import android.testing.LeakCheck;
+
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.phone.ManagedProfileController.Callback;
public class FakeManagedProfileController extends BaseLeakChecker<Callback> implements
ManagedProfileController {
- public FakeManagedProfileController(LeakCheckedTest test) {
+ public FakeManagedProfileController(LeakCheck test) {
super(test, "profile");
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
index 47ed5ca..64fe8dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
@@ -15,25 +15,23 @@
package com.android.systemui.utils.leaks;
import android.os.Bundle;
+import android.testing.LeakCheck;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
public class FakeNetworkController extends BaseLeakChecker<SignalCallback>
implements NetworkController {
private final FakeDataSaverController mDataSaverController;
private final BaseLeakChecker<EmergencyListener> mEmergencyChecker;
- public FakeNetworkController(LeakCheckedTest test) {
+ public FakeNetworkController(LeakCheck test) {
super(test, "network");
mDataSaverController = new FakeDataSaverController(test);
- mEmergencyChecker = new BaseLeakChecker<EmergencyListener>(test, "emergency");
+ mEmergencyChecker = new BaseLeakChecker<>(test, "emergency");
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNextAlarmController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNextAlarmController.java
index 707fc4b..5ae8e22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNextAlarmController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNextAlarmController.java
@@ -14,13 +14,15 @@
package com.android.systemui.utils.leaks;
+import android.testing.LeakCheck;
+
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
public class FakeNextAlarmController extends BaseLeakChecker<NextAlarmChangeCallback>
implements NextAlarmController {
- public FakeNextAlarmController(LeakCheckedTest test) {
+ public FakeNextAlarmController(LeakCheck test) {
super(test, "alarm");
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
index 59a9361..0a83a89 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
@@ -15,17 +15,17 @@
package com.android.systemui.utils.leaks;
import android.content.Context;
+import android.testing.LeakCheck;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.PluginManager;
-public class FakePluginManager extends PluginManager {
+public class FakePluginManager implements PluginManager {
private final BaseLeakChecker<PluginListener> mLeakChecker;
- public FakePluginManager(Context context, LeakCheckedTest test) {
- super(context);
+ public FakePluginManager(LeakCheck test) {
mLeakChecker = new BaseLeakChecker<>(test, "Plugin");
}
@@ -36,11 +36,38 @@
}
@Override
+ public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls) {
+ mLeakChecker.addCallback(listener);
+ }
+
+ @Override
+ public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
+ boolean allowMultiple) {
+ mLeakChecker.addCallback(listener);
+ }
+
+ @Override
+ public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
+ Class<?> cls) {
+ mLeakChecker.addCallback(listener);
+ }
+
+ @Override
public void removePluginListener(PluginListener<?> listener) {
mLeakChecker.removeCallback(listener);
}
@Override
+ public <T> boolean dependsOn(Plugin p, Class<T> cls) {
+ return false;
+ }
+
+ @Override
+ public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
+ return null;
+ }
+
+ @Override
public <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls) {
return null;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
index 00e2404..d60fe78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
@@ -14,12 +14,14 @@
package com.android.systemui.utils.leaks;
+import android.testing.LeakCheck;
+
import com.android.systemui.statusbar.policy.RotationLockController;
import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
public class FakeRotationLockController extends BaseLeakChecker<RotationLockControllerCallback>
implements RotationLockController {
- public FakeRotationLockController(LeakCheckedTest test) {
+ public FakeRotationLockController(LeakCheck test) {
super(test, "rotation");
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
index 2d53c77..157b8a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
@@ -14,12 +14,14 @@
package com.android.systemui.utils.leaks;
+import android.testing.LeakCheck;
+
import com.android.systemui.statusbar.policy.SecurityController;
import com.android.systemui.statusbar.policy.SecurityController.SecurityControllerCallback;
public class FakeSecurityController extends BaseLeakChecker<SecurityControllerCallback>
implements SecurityController {
- public FakeSecurityController(LeakCheckedTest test) {
+ public FakeSecurityController(LeakCheck test) {
super(test, "security");
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
index b13535f..6b501af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
@@ -14,6 +14,8 @@
package com.android.systemui.utils.leaks;
+import android.testing.LeakCheck;
+
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager;
@@ -21,7 +23,7 @@
public class FakeStatusBarIconController extends BaseLeakChecker<IconManager>
implements StatusBarIconController {
- public FakeStatusBarIconController(LeakCheckedTest test) {
+ public FakeStatusBarIconController(LeakCheck test) {
super(test, "StatusBarGroup");
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java
index b841ce9..8db82e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java
@@ -15,6 +15,7 @@
package com.android.systemui.utils.leaks;
import android.content.Context;
+import android.testing.LeakCheck;
import com.android.systemui.tuner.TunerService;
@@ -22,10 +23,8 @@
private final BaseLeakChecker<Tunable> mBaseLeakChecker;
- public FakeTunerService(Context context, LeakCheckedTest test) {
- super(context);
+ public FakeTunerService(LeakCheck test) {
mBaseLeakChecker = new BaseLeakChecker<>(test, "tunable");
- destroy();
}
@Override
@@ -40,4 +39,39 @@
public void removeTunable(Tunable tunable) {
mBaseLeakChecker.removeCallback(tunable);
}
+
+ @Override
+ public void clearAll() {
+
+ }
+
+ @Override
+ public void destroy() {
+
+ }
+
+ @Override
+ public String getValue(String setting) {
+ return null;
+ }
+
+ @Override
+ public int getValue(String setting, int def) {
+ return def;
+ }
+
+ @Override
+ public String getValue(String setting, String def) {
+ return def;
+ }
+
+ @Override
+ public void setValue(String setting, String value) {
+
+ }
+
+ @Override
+ public void setValue(String setting, int value) {
+
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeUserInfoController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeUserInfoController.java
index 578b310..f7ef653a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeUserInfoController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeUserInfoController.java
@@ -14,12 +14,14 @@
package com.android.systemui.utils.leaks;
+import android.testing.LeakCheck;
+
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
public class FakeUserInfoController extends BaseLeakChecker<OnUserInfoChangedListener>
implements UserInfoController {
- public FakeUserInfoController(LeakCheckedTest test) {
+ public FakeUserInfoController(LeakCheck test) {
super(test, "user_info");
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java
index 7581363..fb9bf7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java
@@ -18,12 +18,13 @@
import android.net.Uri;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.ZenRule;
+import android.testing.LeakCheck;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.policy.ZenModeController.Callback;
public class FakeZenModeController extends BaseLeakChecker<Callback> implements ZenModeController {
- public FakeZenModeController(LeakCheckedTest test) {
+ public FakeZenModeController(LeakCheck test) {
super(test, "zen");
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
index 6c51524..94af7733 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
@@ -15,8 +15,8 @@
package com.android.systemui.utils.leaks;
import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doAnswer;
+import android.testing.LeakCheck;
import android.util.ArrayMap;
import com.android.systemui.SysuiTestCase;
@@ -25,7 +25,6 @@
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.CallbackController;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.policy.HotspotController;
@@ -41,12 +40,7 @@
import org.junit.Assert;
import org.junit.Rule;
-import org.junit.rules.TestWatcher;
-import org.junit.runner.Description;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-import java.util.HashMap;
import java.util.Map;
/**
@@ -56,10 +50,7 @@
public abstract class LeakCheckedTest extends SysuiTestCase {
private static final String TAG = "LeakCheckedTest";
- private final Map<String, Tracker> mTrackers = new HashMap<>();
- private final Map<Class, Object> mLeakCheckers = new ArrayMap<>();
-
- public static final Class<?>[] ALL_SUPPORTED_CLASSES = new Class[] {
+ public static final Class<?>[] ALL_SUPPORTED_CLASSES = new Class[]{
BluetoothController.class,
LocationController.class,
RotationLockController.class,
@@ -80,71 +71,11 @@
};
@Rule
- public TestWatcher successWatcher = new TestWatcher() {
- @Override
- protected void succeeded(Description description) {
- verify();
- }
- };
-
- public <T> T getLeakChecker(Class<T> cls) {
- Object obj = mLeakCheckers.get(cls);
- if (obj == null) {
- // Lazy create checkers so we only have the ones we need.
- if (cls == BluetoothController.class) {
- obj = new FakeBluetoothController(this);
- } else if (cls == LocationController.class) {
- obj = new FakeLocationController(this);
- } else if (cls == RotationLockController.class) {
- obj = new FakeRotationLockController(this);
- } else if (cls == ZenModeController.class) {
- obj = new FakeZenModeController(this);
- } else if (cls == CastController.class) {
- obj = new FakeCastController(this);
- } else if (cls == HotspotController.class) {
- obj = new FakeHotspotController(this);
- } else if (cls == FlashlightController.class) {
- obj = new FakeFlashlightController(this);
- } else if (cls == UserInfoController.class) {
- obj = new FakeUserInfoController(this);
- } else if (cls == KeyguardMonitor.class) {
- obj = new FakeKeyguardMonitor(this);
- } else if (cls == BatteryController.class) {
- obj = new FakeBatteryController(this);
- } else if (cls == SecurityController.class) {
- obj = new FakeSecurityController(this);
- } else if (cls == ManagedProfileController.class) {
- obj = new FakeManagedProfileController(this);
- } else if (cls == NextAlarmController.class) {
- obj = new FakeNextAlarmController(this);
- } else if (cls == NetworkController.class) {
- obj = new FakeNetworkController(this);
- } else if (cls == PluginManager.class) {
- obj = new FakePluginManager(mContext, this);
- } else if (cls == TunerService.class) {
- obj = new FakeTunerService(mContext, this);
- } else if (cls == StatusBarIconController.class) {
- obj = new FakeStatusBarIconController(this);
- } else {
- Assert.fail(cls.getName() + " is not supported by LeakCheckedTest yet");
- }
- mLeakCheckers.put(cls, obj);
- }
- return (T) obj;
- }
+ public SysuiLeakCheck mLeakCheck = new SysuiLeakCheck();
@Override
- public Tracker getTracker(String tag) {
- Tracker t = mTrackers.get(tag);
- if (t == null) {
- t = new Tracker();
- mTrackers.put(tag, t);
- }
- return t;
- }
-
- public void verify() {
- mTrackers.values().forEach(Tracker::verify);
+ public LeakCheck getLeakCheck() {
+ return mLeakCheck;
}
public void injectLeakCheckedDependencies(Class<?>... cls) {
@@ -154,26 +85,61 @@
}
public <T> void injectLeakCheckedDependency(Class<T> c) {
- injectTestDependency(c, getLeakChecker(c));
+ mDependency.injectTestDependency(c, mLeakCheck.getLeakChecker(c));
}
- public <T extends CallbackController> T addListening(T mock, Class<T> cls, String tag) {
- doAnswer(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- getTracker(tag).getLeakInfo(invocation.getArguments()[0])
- .addAllocation(new Throwable());
- return null;
+ public static class SysuiLeakCheck extends LeakCheck {
+
+ private final Map<Class, Object> mLeakCheckers = new ArrayMap<>();
+
+ public SysuiLeakCheck() {
+ super();
+ }
+
+ public <T> T getLeakChecker(Class<T> cls) {
+ Object obj = mLeakCheckers.get(cls);
+ if (obj == null) {
+ // Lazy create checkers so we only have the ones we need.
+ if (cls == BluetoothController.class) {
+ obj = new FakeBluetoothController(this);
+ } else if (cls == LocationController.class) {
+ obj = new FakeLocationController(this);
+ } else if (cls == RotationLockController.class) {
+ obj = new FakeRotationLockController(this);
+ } else if (cls == ZenModeController.class) {
+ obj = new FakeZenModeController(this);
+ } else if (cls == CastController.class) {
+ obj = new FakeCastController(this);
+ } else if (cls == HotspotController.class) {
+ obj = new FakeHotspotController(this);
+ } else if (cls == FlashlightController.class) {
+ obj = new FakeFlashlightController(this);
+ } else if (cls == UserInfoController.class) {
+ obj = new FakeUserInfoController(this);
+ } else if (cls == KeyguardMonitor.class) {
+ obj = new FakeKeyguardMonitor(this);
+ } else if (cls == BatteryController.class) {
+ obj = new FakeBatteryController(this);
+ } else if (cls == SecurityController.class) {
+ obj = new FakeSecurityController(this);
+ } else if (cls == ManagedProfileController.class) {
+ obj = new FakeManagedProfileController(this);
+ } else if (cls == NextAlarmController.class) {
+ obj = new FakeNextAlarmController(this);
+ } else if (cls == NetworkController.class) {
+ obj = new FakeNetworkController(this);
+ } else if (cls == PluginManager.class) {
+ obj = new FakePluginManager(this);
+ } else if (cls == TunerService.class) {
+ obj = new FakeTunerService(this);
+ } else if (cls == StatusBarIconController.class) {
+ obj = new FakeStatusBarIconController(this);
+ } else {
+ Assert.fail(cls.getName() + " is not supported by LeakCheckedTest yet");
+ }
+ mLeakCheckers.put(cls, obj);
}
- }).when(mock).addCallback(any());
- doAnswer(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- getTracker(tag).getLeakInfo(invocation.getArguments()[0]).clearAllocations();
- return null;
- }
- }).when(mock).removeCallback(any());
- mLeakCheckers.put(cls, mock);
- return mock;
+ return (T) obj;
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakInfo.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakInfo.java
deleted file mode 100644
index 1d016fb..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakInfo.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.utils.leaks;
-
-import android.util.Log;
-
-import org.junit.Assert;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-public class LeakInfo {
- private static final String TAG = "LeakInfo";
- private List<Throwable> mThrowables = new ArrayList<>();
-
- LeakInfo() {
- }
-
- public void addAllocation(Throwable t) {
- // TODO: Drop off the first element in the stack trace here to have a cleaner stack.
- mThrowables.add(t);
- }
-
- public void clearAllocations() {
- mThrowables.clear();
- }
-
- void verify() {
- if (mThrowables.size() == 0) return;
- Log.e(TAG, "Listener or binding not properly released");
- for (Throwable t : mThrowables) {
- Log.e(TAG, "Allocation found", t);
- }
- StringWriter writer = new StringWriter();
- mThrowables.get(0).printStackTrace(new PrintWriter(writer));
- Assert.fail("Listener or binding not properly released\n"
- + writer.toString());
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/Tracker.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/Tracker.java
deleted file mode 100644
index 26ffd10..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/Tracker.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.utils.leaks;
-
-import android.util.ArrayMap;
-
-import com.android.systemui.utils.leaks.LeakInfo;
-
-import java.util.Map;
-
-public class Tracker {
- private Map<Object, LeakInfo> mObjects = new ArrayMap<>();
-
- public LeakInfo getLeakInfo(Object object) {
- LeakInfo leakInfo = mObjects.get(object);
- if (leakInfo == null) {
- leakInfo = new LeakInfo();
- mObjects.put(object, leakInfo);
- }
- return leakInfo;
- }
-
- void verify() {
- mObjects.values().forEach(LeakInfo::verify);
- }
-}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 42446d1..03582ea 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -44,6 +44,18 @@
// The view or control was updated.
TYPE_UPDATE = 6;
+
+ // Type for APP_TRANSITION event: The transition started a new activity for which it's process
+ // wasn't running.
+ TYPE_TRANSITION_COLD_LAUNCH = 7;
+
+ // Type for APP_TRANSITION event: The transition started a new activity for which it's process
+ // was already running.
+ TYPE_TRANSITION_WARM_LAUNCH = 8;
+
+ // Type for APP_TRANSITION event: The transition brought an already existing activity to the
+ // front.
+ TYPE_TRANSITION_HOT_LAUNCH = 9;
}
// Known visual elements: views or controls.
@@ -3544,6 +3556,54 @@
// internal platform metrics use.
RESERVED_FOR_LOGBUILDER_PID = 865;
+ // ACTION: Settings > Connected devices > Bluetooth -> Available devices
+ ACTION_SETTINGS_BLUETOOTH_PAIR = 866;
+
+ // ACTION: Settings > Connected devices > Bluetooth -> Paired devices
+ ACTION_SETTINGS_BLUETOOTH_CONNECT = 867;
+
+ // ACTION: Settings > Connected devices > Bluetooth -> Connected device
+ ACTION_SETTINGS_BLUETOOTH_DISCONNECT = 868;
+
+ // ACTION: Settings > Connected devices > Bluetooth -> Error dialog
+ ACTION_SETTINGS_BLUETOOTH_CONNECT_ERROR = 869;
+
+ // ACTION: Settings > Connected devices > Bluetooth master switch Toggle
+ ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE = 870;
+
+ // The name of the activity being launched in an app transition event.
+ APP_TRANSITION_ACTIVITY_NAME = 871;
+
+ // ACTION: Settings > App detail > Uninstall
+ ACTION_SETTINGS_UNINSTALL_APP = 872;
+
+ // ACTION: Settings > App detail > Uninstall Device admin app
+ ACTION_SETTINGS_UNINSTALL_DEVICE_ADMIN = 873;
+
+ // ACTION: Settings > App detail > Disable app
+ ACTION_SETTINGS_DISABLE_APP = 874;
+
+ // ACTION: Settings > App detail > Enable app
+ ACTION_SETTINGS_ENABLE_APP = 875;
+
+ // ACTION: Settings > App detail > Clear data
+ ACTION_SETTINGS_CLEAR_APP_DATA = 876;
+
+ // ACTION: Settings > App detail > Clear cache
+ ACTION_SETTINGS_CLEAR_APP_CACHE = 877;
+
+ // ACTION: Clicking on any search result in Settings.
+ ACTION_CLICK_SETTINGS_SEARCH_INLINE_RESULT = 878;
+
+ // FIELD: Settings inline search result name
+ FIELD_SETTINGS_SEARCH_INLINE_RESULT_NAME = 879;
+
+ // FIELD: Settings inline search result value
+ FIELD_SETTINGS_SEARCH_INLINE_RESULT_VALUE = 880;
+
+ // ACTION: Settings > Search > Click saved queries
+ ACTION_CLICK_SETTINGS_SEARCH_SAVED_QUERY = 881;
+
// ---- End O Constants, all O constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index e0d7806..98ce00e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -50,6 +50,7 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManager;
+import android.hardware.fingerprint.IFingerprintService;
import android.hardware.input.InputManager;
import android.net.Uri;
import android.os.Binder;
@@ -69,7 +70,6 @@
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.provider.Settings;
-import android.hardware.fingerprint.IFingerprintService;
import android.provider.SettingsStringUtil.ComponentNameSet;
import android.provider.SettingsStringUtil.SettingStringHelper;
import android.text.TextUtils;
@@ -100,7 +100,9 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
+import com.android.internal.util.IntPair;
import com.android.server.LocalServices;
import com.android.server.policy.AccessibilityShortcutController;
import com.android.server.statusbar.StatusBarManagerInternal;
@@ -120,6 +122,7 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Consumer;
/**
* This class is instantiated by the system as a system level service and can be
@@ -413,10 +416,12 @@
removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
} else if (Intent.ACTION_USER_PRESENT.equals(action)) {
// We will update when the automation service dies.
- UserState userState = getCurrentUserStateLocked();
- if (!userState.isUiAutomationSuppressingOtherServices()) {
- if (readConfigurationForUserStateLocked(userState)) {
- onUserStateChangedLocked(userState);
+ synchronized (mLock) {
+ UserState userState = getCurrentUserStateLocked();
+ if (!userState.isUiAutomationSuppressingOtherServices()) {
+ if (readConfigurationForUserStateLocked(userState)) {
+ onUserStateChangedLocked(userState);
+ }
}
}
} else if (Intent.ACTION_SETTING_RESTORED.equals(action)) {
@@ -434,7 +439,7 @@
}
@Override
- public int addClient(IAccessibilityManagerClient client, int userId) {
+ public long addClient(IAccessibilityManagerClient client, int userId) {
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
@@ -450,7 +455,8 @@
if (DEBUG) {
Slog.i(LOG_TAG, "Added global client for pid:" + Binder.getCallingPid());
}
- return userState.getClientState();
+ return IntPair.of(
+ userState.getClientState(), userState.mLastSentRelevantEventTypes);
} else {
userState.mUserClients.register(client);
// If this client is not for the current user we do not
@@ -460,7 +466,9 @@
Slog.i(LOG_TAG, "Added user client for pid:" + Binder.getCallingPid()
+ " and userId:" + mCurrentUserId);
}
- return (resolvedUserId == mCurrentUserId) ? userState.getClientState() : 0;
+ return IntPair.of(
+ (resolvedUserId == mCurrentUserId) ? userState.getClientState() : 0,
+ userState.mLastSentRelevantEventTypes);
}
}
}
@@ -1323,6 +1331,35 @@
scheduleNotifyClientsOfServicesStateChange(userState);
}
+ private void updateRelevantEventsLocked(UserState userState) {
+ int relevantEventTypes = AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK;
+ for (Service service : userState.mBoundServices) {
+ relevantEventTypes |= service.mEventTypes;
+ }
+ int finalRelevantEventTypes = relevantEventTypes;
+
+ if (userState.mLastSentRelevantEventTypes != finalRelevantEventTypes) {
+ userState.mLastSentRelevantEventTypes = finalRelevantEventTypes;
+ mMainHandler.obtainMessage(MainHandler.MSG_SEND_RELEVANT_EVENTS_CHANGED_TO_CLIENTS,
+ userState.mUserId, finalRelevantEventTypes);
+ mMainHandler.post(() -> {
+ broadcastToClients(userState, (client) -> {
+ try {
+ client.setRelevantEventTypes(finalRelevantEventTypes);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ });
+ });
+ }
+ }
+
+ private void broadcastToClients(
+ UserState userState, Consumer<IAccessibilityManagerClient> clientAction) {
+ mGlobalClients.broadcast(clientAction);
+ userState.mUserClients.broadcast(clientAction);
+ }
+
/**
* Determines if given event can be dispatched to a service based on the package of the
* event source. Specifically, a service is notified if it is interested in events from the
@@ -1633,6 +1670,7 @@
scheduleUpdateFingerprintGestureHandling(userState);
scheduleUpdateInputFilter(userState);
scheduleUpdateClientsIfNeededLocked(userState);
+ updateRelevantEventsLocked(userState);
}
private void updateAccessibilityFocusBehaviorLocked(UserState userState) {
@@ -2281,6 +2319,7 @@
public static final int MSG_CLEAR_ACCESSIBILITY_FOCUS = 9;
public static final int MSG_SEND_SERVICES_STATE_CHANGED_TO_CLIENTS = 10;
public static final int MSG_UPDATE_FINGERPRINT = 11;
+ public static final int MSG_SEND_RELEVANT_EVENTS_CHANGED_TO_CLIENTS = 12;
public MainHandler(Looper looper) {
super(looper);
@@ -2351,6 +2390,22 @@
case MSG_UPDATE_FINGERPRINT: {
updateFingerprintGestureHandling((UserState) msg.obj);
} break;
+
+ case MSG_SEND_RELEVANT_EVENTS_CHANGED_TO_CLIENTS: {
+ final int userId = msg.arg1;
+ final int relevantEventTypes = msg.arg2;
+ final UserState userState;
+ synchronized (mLock) {
+ userState = getUserStateLocked(userId);
+ }
+ broadcastToClients(userState, (client) -> {
+ try {
+ client.setRelevantEventTypes(relevantEventTypes);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ });
+ } break;
}
}
@@ -2380,19 +2435,13 @@
private void sendStateToClients(int clientState,
RemoteCallbackList<IAccessibilityManagerClient> clients) {
- try {
- final int userClientCount = clients.beginBroadcast();
- for (int i = 0; i < userClientCount; i++) {
- IAccessibilityManagerClient client = clients.getBroadcastItem(i);
- try {
- client.setState(clientState);
- } catch (RemoteException re) {
- /* ignore */
- }
+ clients.broadcast((client) -> {
+ try {
+ client.setState(clientState);
+ } catch (RemoteException re) {
+ /* ignore */
}
- } finally {
- clients.finishBroadcast();
- }
+ });
}
private void notifyClientsOfServicesStateChange(
@@ -4710,6 +4759,8 @@
public final CopyOnWriteArrayList<Service> mBoundServices =
new CopyOnWriteArrayList<>();
+ public int mLastSentRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK;
+
public final Map<ComponentName, Service> mComponentNameToServiceMap =
new HashMap<>();
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
similarity index 88%
rename from services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
rename to services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index b90a2a2..a372f95 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -67,13 +67,13 @@
* Entry point service for autofill management.
*
* <p>This service provides the {@link IAutoFillManager} implementation and keeps a list of
- * {@link AutoFillManagerServiceImpl} per user; the real work is done by
- * {@link AutoFillManagerServiceImpl} itself.
+ * {@link AutofillManagerServiceImpl} per user; the real work is done by
+ * {@link AutofillManagerServiceImpl} itself.
*/
// TODO(b/33197203): Handle removing of packages
-public final class AutoFillManagerService extends SystemService {
+public final class AutofillManagerService extends SystemService {
- private static final String TAG = "AutoFillManagerService";
+ private static final String TAG = "AutofillManagerService";
static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
@@ -83,7 +83,7 @@
private final Object mLock = new Object();
/**
- * Cache of {@link AutoFillManagerServiceImpl} per user id.
+ * Cache of {@link AutofillManagerServiceImpl} per user id.
* <p>
* It has to be mapped by user id because the same current user could have simultaneous sessions
* associated to different user profiles (for example, in a multi-window environment or when
@@ -97,7 +97,7 @@
*/
// TODO(b/33197203): Update the above comment
@GuardedBy("mLock")
- private SparseArray<AutoFillManagerServiceImpl> mServicesCache = new SparseArray<>();
+ 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(20);
@@ -115,7 +115,7 @@
}
};
- public AutoFillManagerService(Context context) {
+ public AutofillManagerService(Context context) {
super(context);
mContext = context;
mUi = new AutoFillUI(mContext);
@@ -157,10 +157,11 @@
*
* @return service instance.
*/
- @NonNull AutoFillManagerServiceImpl getServiceForUserLocked(int userId) {
- AutoFillManagerServiceImpl service = mServicesCache.get(userId);
+ @NonNull
+ AutofillManagerServiceImpl getServiceForUserLocked(int userId) {
+ AutofillManagerServiceImpl service = mServicesCache.get(userId);
if (service == null) {
- service = new AutoFillManagerServiceImpl(mContext, mLock,
+ service = new AutofillManagerServiceImpl(mContext, mLock,
mRequestsHistory, userId, mUi);
mServicesCache.put(userId, service);
}
@@ -174,7 +175,7 @@
final IBinder activityToken = getTopActivityForUser();
if (activityToken != null) {
synchronized (mLock) {
- final AutoFillManagerServiceImpl service = mServicesCache.get(userId);
+ final AutofillManagerServiceImpl service = mServicesCache.get(userId);
if (service == null) {
Log.w(TAG, "handleSaveForUser(): no cached service for userId " + userId);
return;
@@ -258,7 +259,7 @@
* Removes a cached service for a given user.
*/
private void removeCachedServiceLocked(int userId) {
- final AutoFillManagerServiceImpl service = mServicesCache.get(userId);
+ final AutofillManagerServiceImpl service = mServicesCache.get(userId);
if (service != null) {
mServicesCache.delete(userId);
service.destroyLocked();
@@ -269,7 +270,7 @@
* Updates a cached service for a given user.
*/
private void updateCachedServiceLocked(int userId) {
- AutoFillManagerServiceImpl service = mServicesCache.get(userId);
+ AutofillManagerServiceImpl service = mServicesCache.get(userId);
if (service != null) {
service.updateLocked();
}
@@ -299,7 +300,7 @@
@Override
public void setAuthenticationResult(Bundle data, IBinder activityToken, int userId) {
synchronized (mLock) {
- final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
+ final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
service.setAuthenticationResultLocked(data, activityToken);
}
}
@@ -307,7 +308,7 @@
@Override
public void setHasCallback(IBinder activityToken, int userId, boolean hasIt) {
synchronized (mLock) {
- final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
+ final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
service.setHasCallback(activityToken, hasIt);
}
}
@@ -319,7 +320,7 @@
// TODO(b/33197203): make sure it's called by resumed / focused activity
synchronized (mLock) {
- final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
+ final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
service.startSessionLocked(activityToken, windowToken, appCallback,
autofillId, bounds, value, hasCallback);
}
@@ -329,7 +330,7 @@
public void updateSession(IBinder activityToken, AutofillId id, Rect bounds,
AutofillValue value, int flags, int userId) {
synchronized (mLock) {
- final AutoFillManagerServiceImpl service = mServicesCache.get(
+ final AutofillManagerServiceImpl service = mServicesCache.get(
UserHandle.getCallingUserId());
if (service != null) {
service.updateSessionLocked(activityToken, id, bounds, value, flags);
@@ -340,7 +341,7 @@
@Override
public void finishSession(IBinder activityToken, int userId) {
synchronized (mLock) {
- final AutoFillManagerServiceImpl service = mServicesCache.get(
+ final AutofillManagerServiceImpl service = mServicesCache.get(
UserHandle.getCallingUserId());
if (service != null) {
service.finishSessionLocked(activityToken);
@@ -349,6 +350,17 @@
}
@Override
+ public void cancelSession(IBinder activityToken, int userId) {
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service = mServicesCache.get(
+ UserHandle.getCallingUserId());
+ if (service != null) {
+ service.cancelSessionLocked(activityToken);
+ }
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingPermission(
Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
@@ -366,7 +378,7 @@
pw.println(size);
for (int i = 0; i < size; i++) {
pw.print("\nService at index "); pw.println(i);
- final AutoFillManagerServiceImpl impl = mServicesCache.valueAt(i);
+ final AutofillManagerServiceImpl impl = mServicesCache.valueAt(i);
impl.dumpLocked(" ", pw);
}
}
@@ -379,7 +391,7 @@
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
- (new AutoFillManagerServiceShellCommand(AutoFillManagerService.this)).exec(
+ (new AutofillManagerServiceShellCommand(AutofillManagerService.this)).exec(
this, in, out, err, args, callback, resultReceiver);
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
similarity index 96%
rename from services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
rename to services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index e691623..411aee0 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -19,8 +19,8 @@
import static android.service.autofill.AutofillService.EXTRA_ACTIVITY_TOKEN;
import static android.service.voice.VoiceInteractionSession.KEY_RECEIVER_EXTRAS;
import static android.service.voice.VoiceInteractionSession.KEY_STRUCTURE;
-import static android.view.autofill.AutofillManager.FLAG_FOCUS_GAINED;
-import static android.view.autofill.AutofillManager.FLAG_FOCUS_LOST;
+import static android.view.autofill.AutofillManager.FLAG_VIEW_ENTERED;
+import static android.view.autofill.AutofillManager.FLAG_VIEW_EXITED;
import static android.view.autofill.AutofillManager.FLAG_START_SESSION;
import static android.view.autofill.AutofillManager.FLAG_VALUE_CHANGED;
@@ -60,6 +60,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.LocalLog;
+import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.view.autofill.AutofillId;
@@ -77,13 +78,13 @@
import java.util.Map.Entry;
/**
- * Bridge between the {@code system_server}'s {@link AutoFillManagerService} and the
+ * Bridge between the {@code system_server}'s {@link AutofillManagerService} and the
* app's {@link IAutoFillService} implementation.
*
*/
-final class AutoFillManagerServiceImpl {
+final class AutofillManagerServiceImpl {
- private static final String TAG = "AutoFillManagerServiceImpl";
+ private static final String TAG = "AutofillManagerServiceImpl";
private static final int MSG_SERVICE_SAVE = 1;
@@ -173,7 +174,7 @@
}
};
- AutoFillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory,
+ AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory,
int userId, AutoFillUI ui) {
mContext = context;
mLock = lock;
@@ -240,7 +241,7 @@
}
/**
- * Used by {@link AutoFillManagerServiceShellCommand} to request save for the current top app.
+ * Used by {@link AutofillManagerServiceShellCommand} to request save for the current top app.
*/
void requestSaveForUserLocked(IBinder activityToken) {
if (!hasService()) {
@@ -321,6 +322,20 @@
session.showSaveLocked();
}
+ void cancelSessionLocked(IBinder activityToken) {
+ if (!hasService()) {
+ return;
+ }
+
+ final Session session = mSessions.get(activityToken);
+ if (session == null) {
+ Slog.w(TAG, "cancelSessionLocked(): no session for " + activityToken);
+ return;
+ }
+
+ session.destroyLocked();
+ }
+
private Session createSessionByTokenLocked(IBinder activityToken, IBinder windowToken,
IBinder appCallbackToken, boolean hasCallback) {
final Session newSession = new Session(mContext, activityToken,
@@ -457,7 +472,7 @@
@Override
public String toString() {
- return "AutoFillManagerServiceImpl: [userId=" + mUserId
+ return "AutofillManagerServiceImpl: [userId=" + mUserId
+ ", component=" + (mInfo != null
? mInfo.getServiceInfo().getComponentName() : null) + "]";
}
@@ -610,7 +625,7 @@
private AssistStructure mStructure;
/**
- * Whether the client has an {@link android.view.autofill.AutoFillManager.AutofillCallback}.
+ * Whether the client has an {@link android.view.autofill.AutofillManager.AutofillCallback}.
*/
private boolean mHasCallback;
@@ -905,7 +920,7 @@
return;
}
- if ((flags & FLAG_FOCUS_GAINED) != 0) {
+ if ((flags & FLAG_VIEW_ENTERED) != 0) {
// Remove the UI if the ViewState has changed.
if (mCurrentViewState != viewState) {
mUi.hideFillUi(mCurrentViewState != null ? mCurrentViewState.mId : null);
@@ -923,7 +938,7 @@
return;
}
- if ((flags & FLAG_FOCUS_LOST) != 0) {
+ if ((flags & FLAG_VIEW_EXITED) != 0) {
if (mCurrentViewState == viewState) {
mUi.hideFillUi(viewState.mId);
mCurrentViewState = null;
@@ -940,9 +955,10 @@
String filterText = "";
if (value != null) {
// TODO(b/33197203): Handle other AutofillValue types
- final CharSequence text = value.getTextValue();
- if (text != null) {
- filterText = text.toString();
+ if (value.isText()) {
+ filterText = value.getTextValue().toString();
+ } else {
+ Log.w(TAG, value + " could not be autofilled into " + this);
}
}
@@ -1001,7 +1017,7 @@
}
CharSequence getServiceName() {
- return AutoFillManagerServiceImpl.this.getServiceName();
+ return AutofillManagerServiceImpl.this.getServiceName();
}
private Intent createAuthFillInIntent(AssistStructure structure) {
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
similarity index 96%
rename from services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java
rename to services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
index 76d9aea..80560f1 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
@@ -16,7 +16,7 @@
package com.android.server.autofill;
-import static com.android.server.autofill.AutoFillManagerService.RECEIVER_BUNDLE_EXTRA_SESSIONS;
+import static com.android.server.autofill.AutofillManagerService.RECEIVER_BUNDLE_EXTRA_SESSIONS;
import android.app.ActivityManager;
import android.os.Bundle;
@@ -30,11 +30,11 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-public final class AutoFillManagerServiceShellCommand extends ShellCommand {
+public final class AutofillManagerServiceShellCommand extends ShellCommand {
- private final AutoFillManagerService mService;
+ private final AutofillManagerService mService;
- public AutoFillManagerServiceShellCommand(AutoFillManagerService service) {
+ public AutofillManagerServiceShellCommand(AutofillManagerService service) {
mService = service;
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 76385b1..47251db 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -182,7 +182,7 @@
return;
}
if (!isBound()) {
- if (mPendingRequest != null && mPendingRequest != pendingRequest) {
+ if (mPendingRequest != null) {
mPendingRequest.cancel();
}
mPendingRequest = pendingRequest;
@@ -240,7 +240,7 @@
}
mBinding = false;
if (isBound()) {
- // TODO(b/33197203, b/35395043): synchronize access instead
+ // TODO(b/33197203): synchronize access instead?
// Need to double check if it's null, since it could be set on onServiceDisconnected()
if (mAutoFillService != null) {
try {
@@ -322,7 +322,7 @@
}
try {
- // TODO(b/33197203, b/35395043): synchronize access instead
+ // TODO(b/33197203): synchronize access instead?
// Need to double check if it's null, since it could be set on
// onServiceDisconnected()
if (mAutoFillService != null) {
@@ -337,9 +337,10 @@
Slog.w(LOG_TAG, "Exception calling onConnected(): " + e);
}
-
if (mPendingRequest != null) {
- handlePendingRequest(mPendingRequest);
+ PendingRequest pendingRequest = mPendingRequest;
+ mPendingRequest = null;
+ handlePendingRequest(pendingRequest);
}
mServiceDied = false;
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 4a380c5..3fdcd9e 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -44,6 +44,8 @@
final class FillUi {
private static final String TAG = "FillUi";
+ private static final int VISIBLE_OPTIONS_MAX_COUNT = 3;
+
interface Callback {
void onResponsePicked(@NonNull FillResponse response);
void onDatasetPicked(@NonNull Dataset dataset);
@@ -56,6 +58,8 @@
private final @NonNull Callback mCallback;
+ private final @NonNull ListView mListView;
+
private final @Nullable ArrayAdapter<ViewItem> mAdapter;
private @Nullable String mFilterText;
@@ -73,6 +77,9 @@
mCallback = callback;
if (response.getAuthentication() != null) {
+ mListView = null;
+ mAdapter = null;
+
final View content;
try {
content = response.getPresentation().apply(context, null);
@@ -80,7 +87,6 @@
callback.onCanceled();
Slog.e(TAG, "Error inflating remote views", e);
mWindow = null;
- mAdapter = null;
return;
}
final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
@@ -89,10 +95,9 @@
content.setOnClickListener(v -> mCallback.onResponsePicked(response));
mContentWidth = content.getMeasuredWidth();
mContentHeight = content.getMeasuredHeight();
- mAdapter = null;
mWindow = new AnchoredWindow(windowToken, content);
- mWindow.update(mContentWidth, mContentHeight, mAnchorBounds);
+ mWindow.show(mContentWidth, mContentHeight, mAnchorBounds);
} else {
final int datasetCount = response.getDatasets().size();
final ArrayList<ViewItem> items = new ArrayList<>(datasetCount);
@@ -121,16 +126,16 @@
};
final LayoutInflater inflater = LayoutInflater.from(context);
- final ListView listView = (ListView) inflater.inflate(
+ mListView = (ListView) inflater.inflate(
com.android.internal.R.layout.autofill_dataset_picker, null);
- listView.setAdapter(mAdapter);
- listView.setOnItemClickListener((adapter, view, position, id) -> {
+ mListView.setAdapter(mAdapter);
+ mListView.setOnItemClickListener((adapter, view, position, id) -> {
final ViewItem vi = mAdapter.getItem(position);
mCallback.onDatasetPicked(vi.getDataset());
});
filter(filterText);
- mWindow = new AnchoredWindow(windowToken, listView);
+ mWindow = new AnchoredWindow(windowToken, mListView);
}
}
@@ -138,7 +143,7 @@
throwIfDestroyed();
if (!mAnchorBounds.equals(anchorBounds)) {
mAnchorBounds.set(anchorBounds);
- mWindow.update(mContentWidth, mContentHeight, anchorBounds);
+ mWindow.show(mContentWidth, mContentHeight, anchorBounds);
}
}
@@ -156,10 +161,16 @@
return;
}
if (count <= 0) {
- mCallback.onCanceled();
+ mWindow.hide();
} else {
if (updateContentSize()) {
- mWindow.update(mContentWidth, mContentHeight, mAnchorBounds);
+ mWindow.show(mContentWidth, mContentHeight, mAnchorBounds);
+ }
+ if (mAdapter.getCount() > VISIBLE_OPTIONS_MAX_COUNT) {
+ mListView.setVerticalScrollBarEnabled(true);
+ mListView.onVisibilityAggregated(true);
+ } else {
+ mListView.setVerticalScrollBarEnabled(false);
}
}
});
@@ -167,7 +178,7 @@
public void destroy() {
throwIfDestroyed();
- mWindow.destroy();
+ mWindow.hide();
mDestroyed = true;
}
@@ -193,7 +204,7 @@
final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
- final int itemCount = mAdapter.getCount();
+ final int itemCount = Math.min(mAdapter.getCount(), VISIBLE_OPTIONS_MAX_COUNT);
for (int i = 0; i < itemCount; i++) {
View view = mAdapter.getItem(i).getView();
view.measure(widthMeasureSpec, heightMeasureSpec);
@@ -266,7 +277,7 @@
/**
* Hides the window.
*/
- void destroy() {
+ void hide() {
if (mContentView.isAttachedToWindow()) {
mContentView.setOnTouchListener(null);
mWm.removeView(mContentView);
@@ -283,7 +294,7 @@
return false;
}
- public void update(int desiredWidth, int desiredHeight, Rect anchorBounds) {
+ public void show(int desiredWidth, int desiredHeight, Rect anchorBounds) {
final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.setTitle("FillUi");
params.token = mActivityToken;
@@ -293,7 +304,6 @@
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
- params.format = PixelFormat.TRANSLUCENT;
mWm.getDefaultDisplay().getRealSize(mTempPoint);
final int screenWidth = mTempPoint.x;
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index de11f36..a5e357c 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -235,6 +235,7 @@
}
public AppOpsService(File storagePath, Handler handler) {
+ LockGuard.installLock(this, LockGuard.INDEX_APP_OPS);
mFile = new AtomicFile(storagePath);
mHandler = handler;
readState();
diff --git a/services/core/java/com/android/server/LockGuard.java b/services/core/java/com/android/server/LockGuard.java
index 3a381ae..b744917 100644
--- a/services/core/java/com/android/server/LockGuard.java
+++ b/services/core/java/com/android/server/LockGuard.java
@@ -53,10 +53,29 @@
* <li>A guarded synchronized block takes 50ns when disabled.
* <li>A guarded synchronized block takes 460ns per lock checked when enabled.
* </ul>
+ * <p>
+ * This class also supports a second simpler mode of operation where well-known
+ * locks are explicitly registered and checked via indexes.
*/
public class LockGuard {
private static final String TAG = "LockGuard";
+ public static final boolean ENABLED = false;
+
+ /**
+ * Well-known locks ordered by fixed index. Locks with a specific index
+ * should never be acquired while holding a lock of a lower index.
+ */
+ public static final int INDEX_APP_OPS = 0;
+ public static final int INDEX_POWER = 1;
+ public static final int INDEX_USER = 2;
+ public static final int INDEX_PACKAGES = 3;
+ public static final int INDEX_STORAGE = 4;
+ public static final int INDEX_WINDOW = 5;
+ public static final int INDEX_ACTIVITY = 6;
+
+ private static Object[] sKnownFixed = new Object[INDEX_ACTIVITY + 1];
+
private static ArrayMap<Object, LockInfo> sKnown = new ArrayMap<>(0, true);
private static class LockInfo {
@@ -119,11 +138,41 @@
}
/**
+ * Yell if any lower-level locks are being held by the calling thread that
+ * is about to acquire the given lock.
+ */
+ public static void guard(int index) {
+ for (int i = 0; i < index; i++) {
+ final Object lock = sKnownFixed[i];
+ if (lock != null && Thread.holdsLock(lock)) {
+ Slog.w(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding "
+ + lockToString(i) + " while trying to acquire "
+ + lockToString(index), new Throwable());
+ }
+ }
+ }
+
+ /**
* Report the given lock with a well-known label.
*/
- public static void installLock(Object lock, String label) {
+ public static Object installLock(Object lock, String label) {
final LockInfo info = findOrCreateLockInfo(lock);
info.label = label;
+ return lock;
+ }
+
+ /**
+ * Report the given lock with a well-known index.
+ */
+ public static Object installLock(Object lock, int index) {
+ sKnownFixed[index] = lock;
+ return lock;
+ }
+
+ public static Object installNewLock(int index) {
+ final Object lock = new Object();
+ installLock(lock, index);
+ return lock;
}
private static String lockToString(Object lock) {
@@ -135,6 +184,19 @@
}
}
+ private static String lockToString(int index) {
+ switch (index) {
+ case INDEX_APP_OPS: return "APP_OPS";
+ case INDEX_POWER: return "POWER";
+ case INDEX_USER: return "USER";
+ case INDEX_PACKAGES: return "PACKAGES";
+ case INDEX_STORAGE: return "STORAGE";
+ case INDEX_WINDOW: return "WINDOW";
+ case INDEX_ACTIVITY: return "ACTIVITY";
+ default: return Integer.toString(index);
+ }
+ }
+
public static void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
for (int i = 0; i < sKnown.size(); i++) {
final Object lock = sKnown.keyAt(i);
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 32136bb..3667ecd 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -308,7 +308,7 @@
* <em>Never</em> hold the lock while performing downcalls into vold, since
* unsolicited events can suddenly appear to update data structures.
*/
- private final Object mLock = new Object();
+ private final Object mLock = LockGuard.installNewLock(LockGuard.INDEX_STORAGE);
/** Set of users that we know are unlocked. */
@GuardedBy("mLock")
@@ -3275,8 +3275,13 @@
if (uid != Binder.getCallingUid()) {
mContext.enforceCallingPermission(android.Manifest.permission.STORAGE_INTERNAL, TAG);
}
- // TODO: wire up to cache quota once merged
- return 64 * TrafficStats.MB_IN_BYTES;
+ final long token = Binder.clearCallingIdentity();
+ final StorageStatsManager stats = mContext.getSystemService(StorageStatsManager.class);
+ try {
+ return stats.getCacheQuotaBytes(volumeUuid, uid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 0d5a3e0..4670eed 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -4003,12 +4003,12 @@
public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
int callingUid = Binder.getCallingUid();
mAppOpsManager.checkPackage(callingUid, opPackageName);
- return getAccountsAsUser(type, userId, opPackageName /* callingPackage */, -1,
+ return getAccountsAsUserForPackage(type, userId, opPackageName /* callingPackage */, -1,
opPackageName, false /* includeUserManagedNotVisible */);
}
@NonNull
- private Account[] getAccountsAsUser(
+ private Account[] getAccountsAsUserForPackage(
String type,
int userId,
String callingPackage,
@@ -4061,7 +4061,7 @@
return getAccountsInternal(
accounts,
callingUid,
- callingPackage,
+ opPackageName,
visibleAccountTypes,
includeUserManagedNotVisible);
} finally {
@@ -4178,7 +4178,7 @@
throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
+ callingUid + " with uid=" + uid);
}
- return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid,
+ return getAccountsAsUserForPackage(null, UserHandle.getCallingUserId(), packageName, uid,
opPackageName, true /* includeUserManagedNotVisible */);
}
@@ -4197,11 +4197,10 @@
return EMPTY_ACCOUNT_ARRAY;
}
if (!UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)
- && !isAccountManagedByCaller(type, callingUid, userId)) {
- return EMPTY_ACCOUNT_ARRAY;
+ && (type != null && !isAccountManagedByCaller(type, callingUid, userId))) {
+ return EMPTY_ACCOUNT_ARRAY;
}
-
- return getAccountsAsUser(type, userId,
+ return getAccountsAsUserForPackage(type, userId,
packageName, packageUid, opPackageName, true /* includeUserManagedNotVisible */);
}
@@ -5371,10 +5370,13 @@
@NonNull
private Account[] filterAccounts(UserAccounts accounts, Account[] unfiltered, int callingUid,
String callingPackage, boolean includeManagedNotVisible) {
- // filter based on visibility.
+ String visibilityFilterPackage = callingPackage;
+ if (visibilityFilterPackage == null) {
+ visibilityFilterPackage = getPackageNameForUid(callingUid);
+ }
Map<Account, Integer> firstPass = new LinkedHashMap<>();
for (Account account : unfiltered) {
- int visibility = resolveAccountVisibility(account, callingPackage, accounts);
+ int visibility = resolveAccountVisibility(account, visibilityFilterPackage, accounts);
if ((visibility == AccountManager.VISIBILITY_VISIBLE
|| visibility == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE)
|| (includeManagedNotVisible
@@ -5394,7 +5396,7 @@
@NonNull
private Map<Account, Integer> filterSharedAccounts(UserAccounts userAccounts,
@NonNull Map<Account, Integer> unfiltered, int callingUid,
- String callingPackage) {
+ @Nullable String callingPackage) {
// first part is to filter shared accounts.
// unfiltered type check is not necessary.
if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
@@ -5474,7 +5476,7 @@
*/
@NonNull
protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
- int callingUid, String callingPackage, boolean includeManagedNotVisible) {
+ int callingUid, @Nullable String callingPackage, boolean includeManagedNotVisible) {
if (callingPackage == null) {
callingPackage = getPackageNameForUid(callingUid);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index fef7b51..55d661c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -691,6 +691,9 @@
Process.setThreadPriority(tid, -2);
}
state.regionCounter++;
+ if (LockGuard.ENABLED) {
+ LockGuard.guard(LockGuard.INDEX_ACTIVITY);
+ }
}
static void resetPriorityAfterLockedSection() {
@@ -2657,6 +2660,7 @@
// Note: This method is invoked on the main thread but may need to attach various
// handlers to other threads. So take care to be explicit about the looper.
public ActivityManagerService(Context systemContext) {
+ LockGuard.installLock(this, LockGuard.INDEX_ACTIVITY);
mContext = systemContext;
mFactoryTest = FactoryTest.getMode();
mSystemThread = ActivityThread.currentActivityThread();
@@ -6687,16 +6691,15 @@
: 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
- // permission we push down the value.
+ // We deprecated Build.SERIAL and it is not accessible to
+ // apps that target the v2 security sandbox. Since access to
+ // the serial is now behind a permission we push down the value.
String buildSerial = Build.UNKNOWN;
- // TODO: SHTOPSHIP Uncomment the check when clients migrate
-// if (appInfo.targetSdkVersion <= Build.VERSION_CODES.N_MR1) {
+ if (appInfo.targetSandboxVersion != 2) {
buildSerial = IDeviceIdentifiersPolicyService.Stub.asInterface(
ServiceManager.getService(Context.DEVICE_IDENTIFIERS_SERVICE))
.getSerial();
-// }
+ }
// Check if this is a secondary process that should be incorporated into some
// currently active instrumentation. (Note we do this AFTER all of the profiling
@@ -7808,9 +7811,14 @@
r.pictureInPictureArgs.copyOnlySet(args);
if (r.getStack().getStackId() == PINNED_STACK_ID) {
// If the activity is already in picture-in-picture, update the pinned stack now
+ // if it is not already expanding to fullscreen. Otherwise, the arguments will
+ // be used the next time the activity enters PiP
final PinnedActivityStack stack = r.getStack();
- stack.setPictureInPictureAspectRatio(r.pictureInPictureArgs.getAspectRatio());
- stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
+ if (!stack.isBoundsAnimatingToFullscreen()) {
+ stack.setPictureInPictureAspectRatio(
+ r.pictureInPictureArgs.getAspectRatio());
+ stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
+ }
}
logPictureInPictureArgs(args);
}
@@ -8528,13 +8536,21 @@
// Third... does the caller itself have permission to access
// this uri?
- if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
- if (!checkHoldingPermissionsLocked(pm, pi, grantUri, callingUid, modeFlags)) {
- // Require they hold a strong enough Uri permission
- if (!checkUriPermissionLocked(grantUri, callingUid, modeFlags)) {
- throw new SecurityException("Uid " + callingUid
- + " does not have permission to uri " + grantUri);
- }
+ final int callingAppId = UserHandle.getAppId(callingUid);
+ if ((callingAppId == Process.SYSTEM_UID) || (callingAppId == Process.ROOT_UID)) {
+ if ("com.android.settings.files".equals(grantUri.uri.getAuthority())) {
+ // Exempted authority for cropping user photos in Settings app
+ } else {
+ Slog.w(TAG, "For security reasons, the system cannot issue a Uri permission"
+ + " grant to " + grantUri + "; use startActivityAsCaller() instead");
+ return -1;
+ }
+ }
+ if (!checkHoldingPermissionsLocked(pm, pi, grantUri, callingUid, modeFlags)) {
+ // Require they hold a strong enough Uri permission
+ if (!checkUriPermissionLocked(grantUri, callingUid, modeFlags)) {
+ throw new SecurityException("Uid " + callingUid
+ + " does not have permission to uri " + grantUri);
}
}
return targetUid;
@@ -17996,6 +18012,25 @@
// BROADCASTS
// =========================================================
+ private boolean isInstantApp(ProcessRecord record, String callerPackage, int uid) {
+ if (UserHandle.getAppId(uid) < Process.FIRST_APPLICATION_UID) {
+ return false;
+ }
+ // Easy case -- we have the app's ProcessRecord.
+ if (record != null) {
+ return record.info.isInstantApp();
+ }
+ // Otherwise check with PackageManager.
+ mAppOpsService.checkPackage(uid, callerPackage);
+ try {
+ IPackageManager pm = AppGlobals.getPackageManager();
+ return pm.isInstantApp(callerPackage, UserHandle.getUserId(uid));
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error looking up if " + callerPackage + " is an instant app.", e);
+ return true;
+ }
+ }
+
boolean isPendingBroadcastProcessLocked(int pid) {
return mFgBroadcastQueue.isPendingBroadcastProcessLocked(pid)
|| mBgBroadcastQueue.isPendingBroadcastProcessLocked(pid);
@@ -18018,12 +18053,14 @@
}
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
- IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
+ IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
+ boolean visibleToInstantApps) {
enforceNotIsolatedCaller("registerReceiver");
ArrayList<Intent> stickyIntents = null;
ProcessRecord callerApp = null;
int callingUid;
int callingPid;
+ boolean instantApp;
synchronized(this) {
if (caller != null) {
callerApp = getRecordForAppLocked(caller);
@@ -18047,6 +18084,7 @@
callingPid = Binder.getCallingPid();
}
+ instantApp = isInstantApp(callerApp, callerPackage, callingUid);
userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
@@ -18082,6 +18120,11 @@
// Look for any matching sticky broadcasts...
for (int i = 0, N = stickyIntents.size(); i < N; i++) {
Intent intent = stickyIntents.get(i);
+ // Don't provided intents that aren't available to instant apps.
+ if (instantApp &&
+ (intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) == 0) {
+ continue;
+ }
// If intent has scheme "content", it will need to acccess
// provider that needs to lock mProviderMap in ActivityThread
// and also it may need to wait application response, so we
@@ -18140,7 +18183,7 @@
+ " callerPackage is " + callerPackage);
}
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
- permission, callingUid, userId);
+ permission, callingUid, userId, instantApp, visibleToInstantApps);
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadcast");
@@ -18158,7 +18201,7 @@
Intent intent = allSticky.get(i);
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
- null, -1, -1, null, null, AppOpsManager.OP_NONE, null, receivers,
+ null, -1, -1, false, null, null, AppOpsManager.OP_NONE, null, receivers,
null, 0, null, null, false, true, true, -1);
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
@@ -18399,6 +18442,12 @@
boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
intent = new Intent(intent);
+ final boolean callerInstantApp = isInstantApp(callerApp, callerPackage, callingUid);
+ // Instant Apps cannot use FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS
+ if (callerInstantApp) {
+ intent.setFlags(intent.getFlags() & ~Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+ }
+
// By default broadcasts do not go to stopped apps.
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
@@ -18888,9 +18937,9 @@
}
final BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
- callerPackage, callingPid, callingUid, resolvedType, requiredPermissions,
- appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,
- resultExtras, ordered, sticky, false, userId);
+ callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
+ requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
+ resultCode, resultData, resultExtras, ordered, sticky, false, userId);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
final boolean replaced = replacePending
&& (queue.replaceParallelBroadcastLocked(r) != null);
@@ -18984,7 +19033,7 @@
|| resultTo != null) {
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
- callerPackage, callingPid, callingUid, resolvedType,
+ callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
resultData, resultExtras, ordered, sticky, false, userId);
@@ -22907,16 +22956,9 @@
}
@Override
- public void notifyStartingWindowDrawn() {
+ public void notifyAppTransitionStarting(SparseIntArray reasons) {
synchronized (ActivityManagerService.this) {
- mStackSupervisor.mActivityMetricsLogger.notifyStartingWindowDrawn();
- }
- }
-
- @Override
- public void notifyAppTransitionStarting(int reason) {
- synchronized (ActivityManagerService.this) {
- mStackSupervisor.mActivityMetricsLogger.notifyTransitionStarting(reason);
+ mStackSupervisor.mActivityMetricsLogger.notifyTransitionStarting(reasons);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index ff796a54..ebbce02 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -1,24 +1,36 @@
package com.android.server.am;
+import static android.app.ActivityManager.START_SUCCESS;
+import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-
+import static android.app.ActivityManagerInternal.APP_TRANSITION_TIMEOUT;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_ACTIVITY_NAME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_DELAY_MS;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_STARTING_WINDOW_DELAY_MS;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_COLD_LAUNCH;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_HOT_LAUNCH;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_WARM_LAUNCH;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
-import android.annotation.Nullable;
import android.app.ActivityManager.StackId;
import android.content.Context;
+import android.metrics.LogMaker;
import android.os.SystemClock;
import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
-import android.metrics.LogMaker;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import java.util.ArrayList;
@@ -48,12 +60,27 @@
private long mLastLogTimeSecs;
private final ActivityStackSupervisor mSupervisor;
private final Context mContext;
+ private final MetricsLogger mMetricsLogger = new MetricsLogger();
private long mCurrentTransitionStartTime = INVALID_START_TIME;
- private boolean mLoggedWindowsDrawn;
- private boolean mLoggedStartingWindowDrawn;
+
+ private int mCurrentTransitionDeviceUptime;
+ private int mCurrentTransitionDelayMs;
private boolean mLoggedTransitionStarting;
+ private final SparseArray<StackTransitionInfo> mStackTransitionInfo = new SparseArray<>();
+
+ private final class StackTransitionInfo {
+ private ActivityRecord launchedActivity;
+ private int startResult;
+ private boolean currentTransitionProcessRunning;
+ private int windowsDrawnDelayMs;
+ private int startingWindowDelayMs;
+ private int reason = APP_TRANSITION_TIMEOUT;
+ private boolean loggedWindowsDrawn;
+ private boolean loggedStartingWindowDrawn;
+ }
+
ActivityMetricsLogger(ActivityStackSupervisor supervisor, Context context) {
mLastLogTimeSecs = SystemClock.elapsedRealtime() / 1000;
mSupervisor = supervisor;
@@ -102,7 +129,9 @@
* activity.
*/
void notifyActivityLaunching() {
- mCurrentTransitionStartTime = System.currentTimeMillis();
+ if (!isAnyTransitionActive()) {
+ mCurrentTransitionStartTime = System.currentTimeMillis();
+ }
}
/**
@@ -118,9 +147,6 @@
launchedActivity.appInfo.uid)
: null;
final boolean processRunning = processRecord != null;
- final String componentName = launchedActivity != null
- ? launchedActivity.shortComponentName
- : null;
// We consider this a "process switch" if the process of the activity that gets launched
// didn't have an activity that was in started state. In this case, we assume that lot
@@ -129,7 +155,7 @@
final boolean processSwitch = processRecord == null
|| !hasStartedActivity(processRecord, launchedActivity);
- notifyActivityLaunched(resultCode, componentName, processRunning, processSwitch);
+ notifyActivityLaunched(resultCode, launchedActivity, processRunning, processSwitch);
}
private boolean hasStartedActivity(ProcessRecord record, ActivityRecord launchedActivity) {
@@ -151,92 +177,120 @@
*
* @param resultCode one of the ActivityManager.START_* flags, indicating the result of the
* launch
- * @param componentName the component name of the activity being launched
+ * @param launchedActivity the activity being launched
* @param processRunning whether the process that will contains the activity is already running
* @param processSwitch whether the process that will contain the activity didn't have any
* activity that was stopped, i.e. the started activity is "switching"
* processes
*/
- private void notifyActivityLaunched(int resultCode, @Nullable String componentName,
+ private void notifyActivityLaunched(int resultCode, ActivityRecord launchedActivity,
boolean processRunning, boolean processSwitch) {
- if (resultCode < 0 || componentName == null || !processSwitch) {
-
- // Failed to launch or it was not a process switch, so we don't care about the timing.
- reset();
+ // If we are already in an existing transition, only update the activity name, but not the
+ // other attributes.
+ final int stackId = launchedActivity != null && launchedActivity.getStack() != null
+ ? launchedActivity.getStack().mStackId
+ : INVALID_STACK_ID;
+ final StackTransitionInfo info = mStackTransitionInfo.get(stackId);
+ if (launchedActivity != null && info != null) {
+ info.launchedActivity = launchedActivity;
return;
}
- MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_COMPONENT_NAME,
- componentName);
- MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_PROCESS_RUNNING,
- processRunning);
- MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS,
- (int) (SystemClock.uptimeMillis() / 1000));
+ final boolean otherStacksLaunching = mStackTransitionInfo.size() > 0 && info == null;
+ if ((resultCode < 0 || launchedActivity == null || !processSwitch
+ || stackId == INVALID_STACK_ID) && !otherStacksLaunching) {
- LogMaker builder = new LogMaker(MetricsEvent.APP_TRANSITION);
- builder.addTaggedData(MetricsEvent.APP_TRANSITION_COMPONENT_NAME, componentName);
- builder.addTaggedData(MetricsEvent.APP_TRANSITION_PROCESS_RUNNING, processRunning ? 1 : 0);
- builder.addTaggedData(MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS,
- SystemClock.uptimeMillis() / 1000);
- MetricsLogger.action(builder);
+ // Failed to launch or it was not a process switch, so we don't care about the timing.
+ reset(true /* abort */);
+ return;
+ } else if (otherStacksLaunching) {
+ // Don't log this stack but continue with the other stacks.
+ return;
+ }
+
+ final StackTransitionInfo newInfo = new StackTransitionInfo();
+ newInfo.launchedActivity = launchedActivity;
+ newInfo.currentTransitionProcessRunning = processRunning;
+ newInfo.startResult = resultCode;
+ mStackTransitionInfo.append(stackId, newInfo);
+ mCurrentTransitionDeviceUptime = (int) (SystemClock.uptimeMillis() / 1000);
}
/**
* Notifies the tracker that all windows of the app have been drawn.
*/
- void notifyWindowsDrawn() {
- if (!isTransitionActive() || mLoggedWindowsDrawn) {
+ void notifyWindowsDrawn(int stackId) {
+ final StackTransitionInfo info = mStackTransitionInfo.get(stackId);
+ if (info == null || info.loggedWindowsDrawn) {
return;
}
- MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS,
- calculateCurrentDelay());
- mLoggedWindowsDrawn = true;
- if (mLoggedTransitionStarting) {
- reset();
+ info.windowsDrawnDelayMs = calculateCurrentDelay();
+ info.loggedWindowsDrawn = true;
+ if (allStacksWindowsDrawn() && mLoggedTransitionStarting) {
+ reset(false /* abort */);
}
}
/**
* Notifies the tracker that the starting window was drawn.
*/
- void notifyStartingWindowDrawn() {
- if (!isTransitionActive() || mLoggedStartingWindowDrawn) {
+ void notifyStartingWindowDrawn(int stackId) {
+ final StackTransitionInfo info = mStackTransitionInfo.get(stackId);
+ if (info == null || info.loggedStartingWindowDrawn) {
return;
}
- mLoggedStartingWindowDrawn = true;
- MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_STARTING_WINDOW_DELAY_MS,
- calculateCurrentDelay());
+ info.loggedStartingWindowDrawn = true;
+ info.startingWindowDelayMs = calculateCurrentDelay();
}
/**
* Notifies the tracker that the app transition is starting.
*
- * @param reason The reason why we started it. Must be on of
- * ActivityManagerInternal.APP_TRANSITION_* reasons.
+ * @param stackIdReasons A map from stack id to a reason integer, which must be on of
+ * ActivityManagerInternal.APP_TRANSITION_* reasons.
*/
- void notifyTransitionStarting(int reason) {
- if (!isTransitionActive() || mLoggedTransitionStarting) {
+ void notifyTransitionStarting(SparseIntArray stackIdReasons) {
+ if (!isAnyTransitionActive() || mLoggedTransitionStarting) {
return;
}
- MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_REASON, reason);
- MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_DELAY_MS,
- calculateCurrentDelay());
+ mCurrentTransitionDelayMs = calculateCurrentDelay();
mLoggedTransitionStarting = true;
- if (mLoggedWindowsDrawn) {
- reset();
+ for (int index = stackIdReasons.size() - 1; index >= 0; index--) {
+ final int stackId = stackIdReasons.keyAt(index);
+ final StackTransitionInfo info = mStackTransitionInfo.get(stackId);
+ if (info == null) {
+ continue;
+ }
+ info.reason = stackIdReasons.valueAt(index);
+ }
+ if (allStacksWindowsDrawn()) {
+ reset(false /* abort */);
}
}
- private boolean isTransitionActive() {
- return mCurrentTransitionStartTime != INVALID_START_TIME;
+ private boolean allStacksWindowsDrawn() {
+ for (int index = mStackTransitionInfo.size() - 1; index >= 0; index--) {
+ if (!mStackTransitionInfo.valueAt(index).loggedWindowsDrawn) {
+ return false;
+ }
+ }
+ return true;
}
- private void reset() {
+ private boolean isAnyTransitionActive() {
+ return mCurrentTransitionStartTime != INVALID_START_TIME
+ && mStackTransitionInfo.size() > 0;
+ }
+
+ private void reset(boolean abort) {
+ if (!abort && isAnyTransitionActive()) {
+ logAppTransitionMultiEvents();
+ }
mCurrentTransitionStartTime = INVALID_START_TIME;
- mLoggedWindowsDrawn = false;
+ mCurrentTransitionDelayMs = -1;
mLoggedTransitionStarting = false;
- mLoggedStartingWindowDrawn = false;
+ mStackTransitionInfo.clear();
}
private int calculateCurrentDelay() {
@@ -244,4 +298,41 @@
// Shouldn't take more than 25 days to launch an app, so int is fine here.
return (int) (System.currentTimeMillis() - mCurrentTransitionStartTime);
}
+
+ private void logAppTransitionMultiEvents() {
+ for (int index = mStackTransitionInfo.size() - 1; index >= 0; index--) {
+ final StackTransitionInfo info = mStackTransitionInfo.valueAt(index);
+ final int type = getTransitionType(info);
+ if (type == -1) {
+ return;
+ }
+ final LogMaker builder = new LogMaker(APP_TRANSITION);
+ builder.setPackageName(info.launchedActivity.packageName);
+ builder.addTaggedData(APP_TRANSITION_ACTIVITY_NAME, info.launchedActivity.info.name);
+ builder.setType(type);
+ builder.addTaggedData(APP_TRANSITION_DEVICE_UPTIME_SECONDS,
+ mCurrentTransitionDeviceUptime);
+ builder.addTaggedData(APP_TRANSITION_DELAY_MS, mCurrentTransitionDelayMs);
+ builder.setSubtype(info.reason);
+ if (info.startingWindowDelayMs != -1) {
+ builder.addTaggedData(APP_TRANSITION_STARTING_WINDOW_DELAY_MS,
+ info.startingWindowDelayMs);
+ }
+ builder.addTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS, info.windowsDrawnDelayMs);
+ mMetricsLogger.write(builder);
+ }
+ }
+
+ private int getTransitionType(StackTransitionInfo info) {
+ if (info.currentTransitionProcessRunning) {
+ if (info.startResult == START_SUCCESS) {
+ return TYPE_TRANSITION_WARM_LAUNCH;
+ } else if (info.startResult == START_TASK_TO_FRONT) {
+ return TYPE_TRANSITION_HOT_LAUNCH;
+ }
+ } else if (info.startResult == START_SUCCESS) {
+ return TYPE_TRANSITION_COLD_LAUNCH;
+ }
+ return -1;
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 77564bb..2e26bed 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -23,6 +23,7 @@
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.HOME_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE;
@@ -890,6 +891,10 @@
return task != null ? (T) task.getStack() : null;
}
+ private int getStackId() {
+ return getStack() != null ? getStack().mStackId : INVALID_STACK_ID;
+ }
+
boolean changeWindowTranslucency(boolean toOpaque) {
if (fullscreen == toOpaque) {
return false;
@@ -1706,9 +1711,16 @@
}
@Override
+ public void onStartingWindowDrawn() {
+ synchronized (service) {
+ mStackSupervisor.mActivityMetricsLogger.notifyStartingWindowDrawn(getStackId());
+ }
+ }
+
+ @Override
public void onWindowsDrawn() {
synchronized (service) {
- mStackSupervisor.mActivityMetricsLogger.notifyWindowsDrawn();
+ mStackSupervisor.mActivityMetricsLogger.notifyWindowsDrawn(getStackId());
if (displayStartTime != 0) {
reportLaunchTimeLocked(SystemClock.uptimeMillis());
}
diff --git a/services/core/java/com/android/server/am/BroadcastFilter.java b/services/core/java/com/android/server/am/BroadcastFilter.java
index 986b8ea..f96b06f 100644
--- a/services/core/java/com/android/server/am/BroadcastFilter.java
+++ b/services/core/java/com/android/server/am/BroadcastFilter.java
@@ -29,15 +29,20 @@
final String requiredPermission;
final int owningUid;
final int owningUserId;
+ final boolean instantApp;
+ final boolean visibleToInstantApp;
BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
- String _packageName, String _requiredPermission, int _owningUid, int _userId) {
+ String _packageName, String _requiredPermission, int _owningUid, int _userId,
+ boolean _instantApp, boolean _visibleToInstantApp) {
super(_filter);
receiverList = _receiverList;
packageName = _packageName;
requiredPermission = _requiredPermission;
owningUid = _owningUid;
owningUserId = _userId;
+ instantApp = _instantApp;
+ visibleToInstantApp = _visibleToInstantApp;
}
public void dump(PrintWriter pw, String prefix) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index c9d19cb..2787895 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -623,6 +623,37 @@
skip = true;
}
+ // Ensure that broadcasts are only sent to other Instant Apps if they are marked as
+ // visible to Instant Apps.
+ final boolean visibleToInstantApps =
+ (r.intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;
+
+ if (!skip && !visibleToInstantApps && filter.instantApp
+ && filter.receiverList.uid != r.callingUid) {
+ Slog.w(TAG, "Instant App Denial: receiving "
+ + r.intent.toString()
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")"
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")"
+ + " not specifying FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS");
+ skip = true;
+ }
+
+ if (!skip && !filter.visibleToInstantApp && r.callerInstantApp
+ && filter.receiverList.uid != r.callingUid) {
+ Slog.w(TAG, "Instant App Denial: receiving "
+ + r.intent.toString()
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")"
+ + " requires receiver be visible to instant apps"
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ skip = true;
+ }
+
if (skip) {
r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
return;
@@ -1121,6 +1152,30 @@
skip = true;
}
}
+ final boolean visibleToInstantApps =
+ (r.intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;
+ if (!skip && info.activityInfo.applicationInfo.isInstantApp()
+ && !visibleToInstantApps
+ && r.callingUid != info.activityInfo.applicationInfo.uid) {
+ Slog.w(TAG, "Instant App Denial: receiving "
+ + r.intent
+ + " to " + component.flattenToShortString()
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")"
+ + " not specifying FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS");
+ skip = true;
+ }
+ if (!skip && r.callerInstantApp
+ && (info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL) == 0
+ && r.callingUid != info.activityInfo.applicationInfo.uid) {
+ Slog.w(TAG, "Instant App Denial: receiving "
+ + r.intent
+ + " to " + component.flattenToShortString()
+ + " requires receiver have visibleToInstantApps set"
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ skip = true;
+ }
if (!skip) {
r.manifestCount++;
} else {
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 1e7911a..7764be7 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -48,6 +48,7 @@
final String callerPackage; // who sent this
final int callingPid; // the pid of who sent this
final int callingUid; // the uid of who sent this
+ final boolean callerInstantApp; // caller is an Instant App?
final boolean ordered; // serialize the send to receivers?
final boolean sticky; // originated from existing sticky data?
final boolean initialSticky; // initial broadcast from register to sticky?
@@ -214,11 +215,10 @@
BroadcastRecord(BroadcastQueue _queue,
Intent _intent, ProcessRecord _callerApp, String _callerPackage,
- int _callingPid, int _callingUid, String _resolvedType, String[] _requiredPermissions,
- int _appOp, BroadcastOptions _options, List _receivers, IIntentReceiver _resultTo,
- int _resultCode, String _resultData, Bundle _resultExtras, boolean _serialized,
- boolean _sticky, boolean _initialSticky,
- int _userId) {
+ int _callingPid, int _callingUid, boolean _callerInstantApp, String _resolvedType,
+ String[] _requiredPermissions, int _appOp, BroadcastOptions _options, List _receivers,
+ IIntentReceiver _resultTo, int _resultCode, String _resultData, Bundle _resultExtras,
+ boolean _serialized, boolean _sticky, boolean _initialSticky, int _userId) {
if (_intent == null) {
throw new NullPointerException("Can't construct with a null intent");
}
@@ -229,6 +229,7 @@
callerPackage = _callerPackage;
callingPid = _callingPid;
callingUid = _callingUid;
+ callerInstantApp = _callerInstantApp;
resolvedType = _resolvedType;
requiredPermissions = _requiredPermissions;
appOp = _appOp;
diff --git a/services/core/java/com/android/server/am/PinnedActivityStack.java b/services/core/java/com/android/server/am/PinnedActivityStack.java
index aa7ab15..1708fe5 100644
--- a/services/core/java/com/android/server/am/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/am/PinnedActivityStack.java
@@ -52,4 +52,8 @@
void setPictureInPictureActions(List<RemoteAction> actions) {
getWindowContainerController().setPictureInPictureActions(actions);
}
+
+ boolean isBoundsAnimatingToFullscreen() {
+ return getWindowContainerController().isBoundsAnimatingToFullscreen();
+ }
}
diff --git a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
index 14f2e86..fb355b9 100644
--- a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
+++ b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
@@ -83,6 +83,7 @@
if (inLockoutMode) {
try {
Slog.w(TAG, "Forcing lockout (fp driver code should do this!)");
+ stop(false); // cancel fingerprint authentication
receiver.onError(getHalDeviceId(),
FingerprintManager.FINGERPRINT_ERROR_LOCKOUT, 0 /* vendorCode */);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index d81e092..a50ec49f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -72,7 +72,8 @@
@ServiceThreadOnly
private boolean mArcEstablished = false;
- // Stores whether ARC feature is enabled per port. True by default for all the ARC-enabled ports.
+ // Stores whether ARC feature is enabled per port.
+ // True by default for all the ARC-enabled ports.
private final SparseBooleanArray mArcFeatureEnabled = new SparseBooleanArray();
// Whether System audio mode is activated or not.
@@ -80,6 +81,10 @@
@GuardedBy("mLock")
private boolean mSystemAudioActivated = false;
+ // Whether the System Audio Control feature is enabled or not. True by default.
+ @GuardedBy("mLock")
+ private boolean mSystemAudioControlFeatureEnabled;
+
// The previous port id (input) before switching to the new one. This is remembered in order to
// be able to switch to it upon receiving <Inactive Source> from currently active source.
// This remains valid only when the active source was switched via one touch play operation
@@ -186,6 +191,8 @@
mAutoDeviceOff = mService.readBooleanSetting(Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
true);
mAutoWakeup = mService.readBooleanSetting(Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, true);
+ mSystemAudioControlFeatureEnabled =
+ mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);
mStandbyHandler = new HdmiCecStandbyModeHandler(service, this);
}
@@ -778,14 +785,11 @@
addAndStartAction(new HotplugDetectionAction(HdmiCecLocalDeviceTv.this));
addAndStartAction(new PowerStatusMonitorAction(HdmiCecLocalDeviceTv.this));
- // If there is AVR, initiate System Audio Auto initiation action,
- // which turns on and off system audio according to last system
- // audio setting.
HdmiDeviceInfo avr = getAvrDeviceInfo();
if (avr != null) {
onNewAvrAdded(avr);
} else {
- setSystemAudioMode(false, true);
+ setSystemAudioMode(false);
}
}
});
@@ -818,13 +822,13 @@
void changeSystemAudioMode(boolean enabled, IHdmiControlCallback callback) {
assertRunOnServiceThread();
if (!mService.isControlEnabled() || hasAction(DeviceDiscoveryAction.class)) {
- setSystemAudioMode(false, true);
+ setSystemAudioMode(false);
invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
return;
}
HdmiDeviceInfo avr = getAvrDeviceInfo();
if (avr == null) {
- setSystemAudioMode(false, true);
+ setSystemAudioMode(false);
invokeCallback(callback, HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE);
return;
}
@@ -834,12 +838,13 @@
}
// # Seq 25
- void setSystemAudioMode(boolean on, boolean updateSetting) {
- HdmiLogger.debug("System Audio Mode change[old:%b new:%b]", mSystemAudioActivated, on);
-
- if (updateSetting) {
- mService.writeBooleanSetting(Global.HDMI_SYSTEM_AUDIO_ENABLED, on);
+ void setSystemAudioMode(boolean on) {
+ if (!isSystemAudioControlFeatureEnabled() && on) {
+ HdmiLogger.debug("Cannot turn on system audio mode "
+ + "because the System Audio Control feature is disabled.");
+ return;
}
+ HdmiLogger.debug("System Audio Mode change[old:%b new:%b]", mSystemAudioActivated, on);
updateAudioManagerForSystemAudio(on);
synchronized (mLock) {
if (mSystemAudioActivated != on) {
@@ -863,8 +868,21 @@
}
}
- boolean getSystemAudioModeSetting() {
- return mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_ENABLED, false);
+ @ServiceThreadOnly
+ void setSystemAudioControlFeatureEnabled(boolean enabled) {
+ assertRunOnServiceThread();
+ synchronized (mLock) {
+ mSystemAudioControlFeatureEnabled = enabled;
+ }
+ if (hasSystemAudioDevice()) {
+ changeSystemAudioMode(enabled, null);
+ }
+ }
+
+ boolean isSystemAudioControlFeatureEnabled() {
+ synchronized (mLock) {
+ return mSystemAudioControlFeatureEnabled;
+ }
}
/**
@@ -1112,6 +1130,7 @@
@ServiceThreadOnly
protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
assertRunOnServiceThread();
+ boolean systemAudioStatus = HdmiUtils.parseCommandParamSystemAudioStatus(message);
if (!isMessageForSystemAudio(message)) {
if (getAvrDeviceInfo() == null) {
// AVR may not have been discovered yet. Delay the message processing.
@@ -1121,10 +1140,15 @@
mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
}
return true;
+ } else if (systemAudioStatus && !isSystemAudioControlFeatureEnabled()) {
+ HdmiLogger.debug("Ignoring <Set System Audio Mode> message "
+ + "because the System Audio Control feature is disabled: %s", message);
+ mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+ return true;
}
removeAction(SystemAudioAutoInitiationAction.class);
SystemAudioActionFromAvr action = new SystemAudioActionFromAvr(this,
- message.getSource(), HdmiUtils.parseCommandParamSystemAudioStatus(message), null);
+ message.getSource(), systemAudioStatus, null);
addAndStartAction(action);
return true;
}
@@ -1138,7 +1162,7 @@
// Ignore this message.
return true;
}
- setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message), true);
+ setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message));
return true;
}
@@ -1882,6 +1906,7 @@
pw.println("mArcFeatureEnabled: " + mArcFeatureEnabled);
pw.println("mSystemAudioActivated: " + mSystemAudioActivated);
pw.println("mSystemAudioMute: " + mSystemAudioMute);
+ pw.println("mSystemAudioControlFeatureEnabled: " + mSystemAudioControlFeatureEnabled);
pw.println("mAutoDeviceOff: " + mAutoDeviceOff);
pw.println("mAutoWakeup: " + mAutoWakeup);
pw.println("mSkipRoutingControl: " + mSkipRoutingControl);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 89b10ac..6864e1e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -486,7 +486,7 @@
Global.HDMI_CONTROL_ENABLED,
Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
- Global.HDMI_SYSTEM_AUDIO_ENABLED,
+ Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
Global.MHL_INPUT_SWITCHING_ENABLED,
Global.MHL_POWER_CHARGE_ENABLED
};
@@ -525,9 +525,9 @@
}
// No need to propagate to HAL.
break;
- case Global.HDMI_SYSTEM_AUDIO_ENABLED:
- if (isTvDeviceEnabled() && tv().isSystemAudioActivated() != enabled) {
- tv().changeSystemAudioMode(enabled, null);
+ case Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED:
+ if (isTvDeviceEnabled()) {
+ tv().setSystemAudioControlFeatureEnabled(enabled);
}
break;
case Global.MHL_INPUT_SWITCHING_ENABLED:
diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
index e1bcd99..7670dcc 100644
--- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
+++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
@@ -262,8 +262,7 @@
return;
}
- // Turn off system audio mode and update settings.
- tv().setSystemAudioMode(false, true);
+ tv().setSystemAudioMode(false);
if (tv().isArcEstablished()) {
tv().enableAudioReturnChannel(false);
addAndStartAction(new RequestArcTerminationAction(localDevice(), address));
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
index af1a85d..449b208 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
@@ -133,7 +133,7 @@
}
protected void setSystemAudioMode(boolean mode) {
- tv().setSystemAudioMode(mode, true);
+ tv().setSystemAudioMode(mode);
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
index 01063b7..d347a91 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
@@ -50,7 +50,7 @@
@Override
public void onSendCompleted(int error) {
if (error != SendMessageResult.SUCCESS) {
- tv().setSystemAudioMode(false, true);
+ tv().setSystemAudioMode(false);
finish();
}
}
@@ -71,18 +71,24 @@
return false;
}
- private void handleSystemAudioModeStatusMessage(boolean isSystemAudioModeOn) {
+ private void handleSystemAudioModeStatusMessage(boolean currentSystemAudioMode) {
if (!canChangeSystemAudio()) {
HdmiLogger.debug("Cannot change system audio mode in auto initiation action.");
finish();
return;
}
- boolean systemAudioModeSetting = tv().getSystemAudioModeSetting();
- if (systemAudioModeSetting && !isSystemAudioModeOn) {
- addAndStartAction(new SystemAudioActionFromTv(tv(), mAvrAddress, systemAudioModeSetting, null));
+ // If System Audio Control feature is enabled, turn on system audio mode when new AVR is
+ // detected. Otherwise, turn off system audio mode.
+ boolean targetSystemAudioMode = tv().isSystemAudioControlFeatureEnabled();
+ if (currentSystemAudioMode != targetSystemAudioMode) {
+ // Start System Audio Control feature actions only if necessary.
+ addAndStartAction(
+ new SystemAudioActionFromTv(tv(), mAvrAddress, targetSystemAudioMode, null));
} else {
- tv().setSystemAudioMode(isSystemAudioModeOn, true);
+ // If AVR already has correct system audio mode, update target system audio mode
+ // immediately rather than starting feature action.
+ tv().setSystemAudioMode(targetSystemAudioMode);
}
finish();
}
@@ -101,13 +107,15 @@
}
private void handleSystemAudioModeStatusTimeout() {
- if (tv().getSystemAudioModeSetting()) {
- if (canChangeSystemAudio()) {
- addAndStartAction(new SystemAudioActionFromTv(tv(), mAvrAddress, true, null));
- }
- } else {
- tv().setSystemAudioMode(false, true);
+ if (!canChangeSystemAudio()) {
+ HdmiLogger.debug("Cannot change system audio mode in auto initiation action.");
+ finish();
+ return;
}
+ // If we can't get the current system audio mode status, just try to turn on/off system
+ // audio mode according to the system audio control setting.
+ addAndStartAction(new SystemAudioActionFromTv(tv(), mAvrAddress,
+ tv().isSystemAudioControlFeatureEnabled(), null));
finish();
}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 6097071..4b1804c 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -510,9 +510,6 @@
if (r == null) {
throw new IllegalArgumentException("Invalid package");
}
- if (IMPORTANCE_NONE == r.importance) {
- throw new IllegalArgumentException("Package blocked");
- }
if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) {
throw new IllegalArgumentException("NotificationChannelGroup doesn't exist");
}
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index 6365d15..bb7ffda 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -207,9 +207,35 @@
}
public void grantDefaultPermissions(int userId) {
- grantPermissionsToSysComponentsAndPrivApps(userId);
- grantDefaultSystemHandlerPermissions(userId);
- grantDefaultPermissionExceptions(userId);
+ if (mService.hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) {
+ grantAllRuntimePermissions(userId);
+ } else {
+ grantPermissionsToSysComponentsAndPrivApps(userId);
+ grantDefaultSystemHandlerPermissions(userId);
+ grantDefaultPermissionExceptions(userId);
+ }
+ }
+
+ private void grantRuntimePermissionsForPackageLocked(int userId, PackageParser.Package pkg) {
+ Set<String> permissions = new ArraySet<>();
+ for (String permission : pkg.requestedPermissions) {
+ BasePermission bp = mService.mSettings.mPermissions.get(permission);
+ if (bp != null && bp.isRuntime()) {
+ permissions.add(permission);
+ }
+ }
+ if (!permissions.isEmpty()) {
+ grantRuntimePermissionsLPw(pkg, permissions, true, userId);
+ }
+ }
+
+ private void grantAllRuntimePermissions(int userId) {
+ Log.i(TAG, "Granting all runtime permissions for user " + userId);
+ synchronized (mService.mPackages) {
+ for (PackageParser.Package pkg : mService.mPackages.values()) {
+ grantRuntimePermissionsForPackageLocked(userId, pkg);
+ }
+ }
}
public void scheduleReadDefaultPermissionExceptions() {
@@ -226,18 +252,7 @@
|| pkg.requestedPermissions.isEmpty()) {
continue;
}
- Set<String> permissions = new ArraySet<>();
- final int permissionCount = pkg.requestedPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- String permission = pkg.requestedPermissions.get(i);
- BasePermission bp = mService.mSettings.mPermissions.get(permission);
- if (bp != null && bp.isRuntime()) {
- permissions.add(permission);
- }
- }
- if (!permissions.isEmpty()) {
- grantRuntimePermissionsLPw(pkg, permissions, true, userId);
- }
+ grantRuntimePermissionsForPackageLocked(userId, pkg);
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index fd731c3..0ec85aa 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -56,7 +56,9 @@
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.Process;
+import android.os.ProxyFileDescriptorCallback;
import android.os.RemoteException;
+import android.os.RevocableFileDescriptor;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.system.ErrnoException;
@@ -148,7 +150,9 @@
private String mFinalMessage;
@GuardedBy("mLock")
- private ArrayList<FileBridge> mBridges = new ArrayList<>();
+ private final ArrayList<RevocableFileDescriptor> mFds = new ArrayList<>();
+ @GuardedBy("mLock")
+ private final ArrayList<FileBridge> mBridges = new ArrayList<>();
@GuardedBy("mLock")
private IPackageInstallObserver2 mRemoteObserver;
@@ -430,12 +434,20 @@
// Quick sanity check of state, and allocate a pipe for ourselves. We
// then do heavy disk allocation outside the lock, but this open pipe
// will block any attempted install transitions.
+ final RevocableFileDescriptor fd;
final FileBridge bridge;
synchronized (mLock) {
assertPreparedAndNotSealed("openWrite");
- bridge = new FileBridge();
- mBridges.add(bridge);
+ if (PackageInstaller.ENABLE_REVOCABLE_FD) {
+ fd = new RevocableFileDescriptor();
+ bridge = null;
+ mFds.add(fd);
+ } else {
+ fd = null;
+ bridge = new FileBridge();
+ mBridges.add(bridge);
+ }
}
try {
@@ -468,9 +480,14 @@
Libcore.os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET);
}
- bridge.setTargetFile(targetFd);
- bridge.start();
- return new ParcelFileDescriptor(bridge.getClientSocket());
+ if (PackageInstaller.ENABLE_REVOCABLE_FD) {
+ fd.init(mContext, targetFd);
+ return fd.getRevocableFileDescriptor();
+ } else {
+ bridge.setTargetFile(targetFd);
+ bridge.start();
+ return new ParcelFileDescriptor(bridge.getClientSocket());
+ }
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
@@ -512,6 +529,11 @@
wasSealed = mSealed;
if (!mSealed) {
// Verify that all writers are hands-off
+ for (RevocableFileDescriptor fd : mFds) {
+ if (!fd.isRevoked()) {
+ throw new SecurityException("Files still open");
+ }
+ }
for (FileBridge bridge : mBridges) {
if (!bridge.isClosed()) {
throw new SecurityException("Files still open");
@@ -1170,6 +1192,9 @@
mDestroyed = true;
// Force shut down all bridges
+ for (RevocableFileDescriptor fd : mFds) {
+ fd.revoke();
+ }
for (FileBridge bridge : mBridges) {
bridge.forceClose();
}
@@ -1211,6 +1236,7 @@
pw.printPair("mPermissionsAccepted", mPermissionsAccepted);
pw.printPair("mRelinquished", mRelinquished);
pw.printPair("mDestroyed", mDestroyed);
+ pw.printPair("mFds", mFds.size());
pw.printPair("mBridges", mBridges.size());
pw.printPair("mFinalStatus", mFinalStatus);
pw.printPair("mFinalMessage", mFinalMessage);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e8af310..c27806d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -266,6 +266,7 @@
import com.android.server.FgThread;
import com.android.server.IntentResolver;
import com.android.server.LocalServices;
+import com.android.server.LockGuard;
import com.android.server.ServiceThread;
import com.android.server.SystemConfig;
import com.android.server.SystemServerInitThreadPool;
@@ -2212,6 +2213,7 @@
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
+ LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "create package manager");
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
SystemClock.uptimeMillis());
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 627fa54..b9fcf4e 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -94,6 +94,7 @@
import com.android.internal.util.XmlUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
+import com.android.server.LockGuard;
import com.android.server.SystemService;
import com.android.server.am.UserState;
import com.android.server.storage.DeviceStorageMonitorInternal;
@@ -227,7 +228,7 @@
private final Object mPackagesLock;
private final UserDataPreparer mUserDataPreparer;
// Short-term lock for internal state, when interaction/sync with PM is not required
- private final Object mUsersLock = new Object();
+ private final Object mUsersLock = LockGuard.installNewLock(LockGuard.INDEX_USER);
private final Object mRestrictionsLock = new Object();
private final Handler mHandler;
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 755c486..83dd392 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -432,7 +432,7 @@
// Ignore framework code.
// TODO(calin): is there a better way to detect it?
if (dexPath.startsWith("/system/framework/")) {
- new DexSearchResult("framework", DEX_SEARCH_NOT_FOUND);
+ return new DexSearchResult("framework", DEX_SEARCH_NOT_FOUND);
}
// First, check if the package which loads the dex file actually owns it.
diff --git a/services/core/java/com/android/server/policy/AccessibilityShortcutController.java b/services/core/java/com/android/server/policy/AccessibilityShortcutController.java
index eec1fef..cd55f50 100644
--- a/services/core/java/com/android/server/policy/AccessibilityShortcutController.java
+++ b/services/core/java/com/android/server/policy/AccessibilityShortcutController.java
@@ -29,6 +29,7 @@
import android.media.RingtoneManager;
import android.os.Handler;
import android.os.UserHandle;
+import android.os.Vibrator;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Slog;
@@ -48,6 +49,11 @@
*/
public class AccessibilityShortcutController {
private static final String TAG = "AccessibilityShortcutController";
+ private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ .setUsage(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY)
+ .build();
+
private final Context mContext;
private AlertDialog mAlertDialog;
@@ -100,6 +106,8 @@
final int userId = ActivityManager.getCurrentUser();
final int dialogAlreadyShown = Settings.Secure.getIntForUser(
cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0, userId);
+
+ // Play a notification tone
final Ringtone tone =
RingtoneManager.getRingtone(mContext, Settings.System.DEFAULT_NOTIFICATION_URI);
if (tone != null) {
@@ -108,6 +116,18 @@
.build());
tone.play();
}
+
+ // Play a notification vibration
+ Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
+ if ((vibrator != null) && vibrator.hasVibrator()) {
+ // Don't check if haptics are disabled, as we need to alert the user that their
+ // way of interacting with the phone may change if they activate the shortcut
+ long[] vibePattern = PhoneWindowManager.getLongIntArray(mContext.getResources(),
+ R.array.config_safeModeDisabledVibePattern);
+ vibrator.vibrate(vibePattern, -1, VIBRATION_ATTRIBUTES);
+ }
+
+
if (dialogAlreadyShown == 0) {
// The first time, we show a warning rather than toggle the service to give the user a
// chance to turn off this feature before stuff gets enabled.
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 8a59726..548fa1e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1806,7 +1806,7 @@
mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler);
mOrientationListener = new MyOrientationListener(mContext, mHandler);
try {
- mOrientationListener.setCurrentRotation(windowManager.getRotation());
+ mOrientationListener.setCurrentRotation(windowManager.getDefaultDisplayRotation());
} catch (RemoteException ex) { }
mSettingsObserver = new SettingsObserver(mHandler);
mSettingsObserver.observe();
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 24f6f89..4f67e8c 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -76,6 +76,7 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.server.EventLogTags;
+import com.android.server.LockGuard;
import com.android.server.RescueParty;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
@@ -210,7 +211,7 @@
private DreamManagerInternal mDreamManager;
private Light mAttentionLight;
- private final Object mLock = new Object();
+ private final Object mLock = LockGuard.installNewLock(LockGuard.INDEX_POWER);
// A bitfield that indicates what parts of the power state have
// changed and need to be recalculated.
diff --git a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
index c064392..7720e24 100644
--- a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
+++ b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
@@ -41,6 +41,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.text.format.DateUtils;
+import android.util.ArrayMap;
import android.util.Pair;
import android.util.Slog;
import android.util.Xml;
@@ -64,6 +65,7 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
/**
* CacheQuotaStrategy is a strategy for determining cache quotas using usage stats and foreground
@@ -85,15 +87,18 @@
private final Context mContext;
private final UsageStatsManagerInternal mUsageStats;
private final Installer mInstaller;
+ private final Map<String, Map<Integer, Long>> mQuotaMap;
private ServiceConnection mServiceConnection;
private ICacheQuotaService mRemoteService;
private AtomicFile mPreviousValuesFile;
public CacheQuotaStrategy(
- Context context, UsageStatsManagerInternal usageStatsManager, Installer installer) {
+ Context context, UsageStatsManagerInternal usageStatsManager, Installer installer,
+ Map<String, Map<Integer, Long>> quotaMap) {
mContext = Preconditions.checkNotNull(context);
mUsageStats = Preconditions.checkNotNull(usageStatsManager);
mInstaller = Preconditions.checkNotNull(installer);
+ mQuotaMap = Preconditions.checkNotNull(quotaMap);
mPreviousValuesFile = new AtomicFile(new File(
new File(Environment.getDataDirectory(), "system"), "cachequota.xml"));
}
@@ -221,6 +226,9 @@
mInstaller.setAppQuota(request.getVolumeUuid(),
UserHandle.getUserId(uid),
UserHandle.getAppId(uid), proposedQuota);
+ insertIntoQuotaMap(request.getVolumeUuid(),
+ UserHandle.getUserId(uid),
+ UserHandle.getAppId(uid), proposedQuota);
} catch (Installer.InstallerException ex) {
Slog.w(TAG,
"Failed to set cache quota for " + request.getUid(),
@@ -231,6 +239,15 @@
disconnectService();
}
+ private void insertIntoQuotaMap(String volumeUuid, int userId, int appId, long quota) {
+ Map<Integer, Long> volumeMap = mQuotaMap.get(volumeUuid);
+ if (volumeMap == null) {
+ volumeMap = new ArrayMap<>();
+ mQuotaMap.put(volumeUuid, volumeMap);
+ }
+ volumeMap.put(UserHandle.getUid(userId, appId), quota);
+ }
+
private void disconnectService() {
if (mServiceConnection != null) {
mContext.unbindService(mServiceConnection);
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index b7479da..1510dd1 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -156,9 +156,9 @@
}
}
- public void onRotationChangedLocked(DisplayContent displayContent, int rotation) {
+ public void onRotationChangedLocked(DisplayContent displayContent) {
if (mDisplayMagnifier != null) {
- mDisplayMagnifier.onRotationChangedLocked(displayContent, rotation);
+ mDisplayMagnifier.onRotationChangedLocked(displayContent);
}
if (mWindowsForAccessibilityObserver != null) {
mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
@@ -312,9 +312,10 @@
mWindowManagerService.scheduleAnimationLocked();
}
- public void onRotationChangedLocked(DisplayContent displayContent, int rotation) {
+ public void onRotationChangedLocked(DisplayContent displayContent) {
if (DEBUG_ROTATION) {
- Slog.i(LOG_TAG, "Rotaton: " + Surface.rotationToString(rotation)
+ final int rotation = displayContent.getRotation();
+ Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
+ " displayId: " + displayContent.getDisplayId());
}
mMagnifedViewport.onRotationChangedLocked();
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 4eb8e02..75a7385 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -692,7 +692,7 @@
private void getDefaultNextAppTransitionStartRect(Rect rect) {
if (mDefaultNextAppTransitionAnimationSpec == null ||
mDefaultNextAppTransitionAnimationSpec.rect == null) {
- Slog.wtf(TAG, "Starting rect for app requested, but none available", new Throwable());
+ Slog.e(TAG, "Starting rect for app requested, but none available", new Throwable());
rect.setEmpty();
} else {
rect.set(mDefaultNextAppTransitionAnimationSpec.rect);
@@ -705,7 +705,7 @@
spec = mDefaultNextAppTransitionAnimationSpec;
}
if (spec == null || spec.rect == null) {
- Slog.wtf(TAG, "Starting rect for task: " + taskId + " requested, but not available",
+ Slog.e(TAG, "Starting rect for task: " + taskId + " requested, but not available",
new Throwable());
rect.setEmpty();
} else {
@@ -2058,15 +2058,7 @@
* @return whether the transition should show the thumbnail being scaled down.
*/
private boolean shouldScaleDownThumbnailTransition(int uiMode, int orientation) {
- return isTvUiMode(uiMode)
- || mGridLayoutRecentsEnabled
+ return mGridLayoutRecentsEnabled
|| orientation == Configuration.ORIENTATION_PORTRAIT;
}
-
- /**
- * @return whether the specified {@param uiMode} is the TV mode.
- */
- private boolean isTvUiMode(int uiMode) {
- return (uiMode & Configuration.UI_MODE_TYPE_TELEVISION) > 0;
- }
}
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 266ab4c..b90a82a 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -59,6 +59,15 @@
private final IApplicationToken mToken;
private final Handler mHandler;
+ private final Runnable mOnStartingWindowDrawn = () -> {
+ if (mListener == null) {
+ return;
+ }
+ if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in "
+ + AppWindowContainerController.this.mToken);
+ mListener.onStartingWindowDrawn();
+ };
+
private final Runnable mOnWindowsDrawn = () -> {
if (mListener == null) {
return;
@@ -655,6 +664,9 @@
}
}
+ void reportStartingWindowDrawn() {
+ mHandler.post(mOnStartingWindowDrawn);
+ }
void reportWindowsDrawn() {
mHandler.post(mOnWindowsDrawn);
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerListener.java b/services/core/java/com/android/server/wm/AppWindowContainerListener.java
index 12d4b2f..9d459cf 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerListener.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerListener.java
@@ -24,6 +24,12 @@
void onWindowsVisible();
/** Called when the windows associated app window container are no longer visible. */
void onWindowsGone();
+
+ /**
+ * Called when the starting window for this container is drawn.
+ */
+ void onStartingWindowDrawn();
+
/**
* Called when the key dispatching to a window associated with the app window container
* timed-out.
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 4aa013a..2f221df 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -42,7 +42,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN;
-import static com.android.server.wm.WindowManagerService.H.NOTIFY_STARTING_WINDOW_DRAWN;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.logWithStack;
@@ -668,6 +667,15 @@
return (Task) getParent();
}
+ TaskStack getStack() {
+ final Task task = getTask();
+ if (task != null) {
+ return task.mStack;
+ } else {
+ return null;
+ }
+ }
+
@Override
void onParentSet() {
super.onParentSet();
@@ -1329,7 +1337,9 @@
}
}
} else if (w.isDrawnLw()) {
- mService.mH.sendEmptyMessage(NOTIFY_STARTING_WINDOW_DRAWN);
+ if (getController() != null) {
+ getController().reportStartingWindowDrawn();
+ }
startingDisplayed = true;
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 5486aa8..cb3a663 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -182,6 +182,42 @@
private final Display mDisplay;
private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+ /**
+ * Current rotation of the display.
+ * Constants as per {@link android.view.Surface.Rotation}.
+ *
+ * @see WindowManagerService#updateRotationUncheckedLocked(boolean, int)
+ */
+ private int mRotation = 0;
+ /**
+ * Last applied orientation of the display.
+ * Constants as per {@link android.content.pm.ActivityInfo.ScreenOrientation}.
+ *
+ * @see WindowManagerService#updateOrientationFromAppTokensLocked(boolean, int)
+ */
+ private int mLastOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+ /**
+ * Flag indicating that the application is receiving an orientation that has different metrics
+ * than it expected. E.g. Portrait instead of Landscape.
+ *
+ * @see WindowManagerService#updateRotationUncheckedLocked(boolean, int)
+ */
+ private boolean mAltOrientation = false;
+ /**
+ * Orientation forced by some window. If there is no visible window that specifies orientation
+ * it is set to {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED}.
+ *
+ * @see NonAppWindowContainers#getOrientation()
+ */
+ private int mLastWindowForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+ /**
+ * Last orientation forced by the keyguard. It is applied when keyguard is shown and is not
+ * occluded.
+ *
+ * @see NonAppWindowContainers#getOrientation()
+ */
+ private int mLastKeyguardForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+
Rect mBaseDisplayRect = new Rect();
private Rect mContentRect = new Rect();
@@ -764,6 +800,34 @@
return mDisplayMetrics;
}
+ int getRotation() {
+ return mRotation;
+ }
+
+ void setRotation(int newRotation) {
+ mRotation = newRotation;
+ }
+
+ int getLastOrientation() {
+ return mLastOrientation;
+ }
+
+ void setLastOrientation(int orientation) {
+ mLastOrientation = orientation;
+ }
+
+ boolean getAltOrientation() {
+ return mAltOrientation;
+ }
+
+ void setAltOrientation(boolean altOrientation) {
+ mAltOrientation = altOrientation;
+ }
+
+ int getLastWindowForcedOrientation() {
+ return mLastWindowForcedOrientation;
+ }
+
DockedStackDividerController getDockedDividerController() {
return mDividerControllerLocked;
}
@@ -884,14 +948,14 @@
final WindowManagerPolicy policy = mService.mPolicy;
if (mService.mDisplayFrozen) {
- if (mService.mLastWindowForcedOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
+ if (mLastWindowForcedOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
- "Display is frozen, return " + mService.mLastWindowForcedOrientation);
+ "Display is frozen, return " + mLastWindowForcedOrientation);
// If the display is frozen, some activities may be in the middle of restarting, and
// thus have removed their old window. If the window has the flag to hide the lock
// screen, then the lock screen can re-appear and inflict its own orientation on us.
// Keep the orientation stable until this all settles down.
- return mService.mLastWindowForcedOrientation;
+ return mLastWindowForcedOrientation;
} else if (policy.isKeyguardLocked()) {
// Use the last orientation the while the display is frozen with the keyguard
// locked. This could be the keyguard forced orientation or from a SHOW_WHEN_LOCKED
@@ -899,8 +963,8 @@
// things aren't stable while the display is frozen, for example the window could be
// momentarily unavailable due to activity relaunch.
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display is frozen while keyguard locked, "
- + "return " + mService.mLastOrientation);
- return mService.mLastOrientation;
+ + "return " + mLastOrientation);
+ return mLastOrientation;
}
} else {
final int orientation = mAboveAppWindowsContainers.getOrientation();
@@ -2051,7 +2115,7 @@
Slog.v(TAG, "performLayout: needed=" + isLayoutNeeded() + " dw=" + dw + " dh=" + dh);
}
- mService.mPolicy.beginLayoutLw(isDefaultDisplay, dw, dh, mService.mRotation,
+ mService.mPolicy.beginLayoutLw(isDefaultDisplay, dw, dh, mRotation,
getConfiguration().uiMode);
if (isDefaultDisplay) {
// Not needed on non-default displays.
@@ -2712,10 +2776,10 @@
}
if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
- "No app is requesting an orientation, return " + mService.mLastOrientation);
+ "No app is requesting an orientation, return " + mLastOrientation);
// The next app has not been requested to be visible, so we keep the current orientation
// to prevent freezing/unfreezing the display too early.
- return mService.mLastOrientation;
+ return mLastOrientation;
}
}
@@ -2766,15 +2830,15 @@
final int req = win.mAttrs.screenOrientation;
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, win + " forcing orientation to " + req);
if (policy.isKeyguardHostWindow(win.mAttrs)) {
- mService.mLastKeyguardForcedOrientation = req;
+ mLastKeyguardForcedOrientation = req;
}
- return (mService.mLastWindowForcedOrientation = req);
+ return (mLastWindowForcedOrientation = req);
}
- mService.mLastWindowForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+ mLastWindowForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
if (policy.isKeyguardShowingAndNotOccluded()) {
- return mService.mLastKeyguardForcedOrientation;
+ return mLastKeyguardForcedOrientation;
}
return SCREEN_ORIENTATION_UNSET;
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
index 71f88de..6a0e353 100644
--- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
@@ -109,6 +109,13 @@
}
/**
+ * @return whether the bounds are currently animating to fullscreen.
+ */
+ public boolean isBoundsAnimatingToFullscreen() {
+ return mContainer.isBoundsAnimatingToFullscreen();
+ }
+
+ /**
* Checks the {@param bounds} and retirms non-null fullscreen bounds for the pinned stack
* animation if necessary.
*/
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 126e080..0222b3d 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -802,6 +802,7 @@
mHoldScreenWindow = null;
mObscuringWindow = null;
+ // TODO(multi-display): Support these features on secondary screens.
if (mService.mWatermark != null) {
mService.mWatermark.positionSurface(defaultDw, defaultDh);
}
@@ -809,11 +810,12 @@
mService.mStrictModeFlash.positionSurface(defaultDw, defaultDh);
}
if (mService.mCircularDisplayMask != null) {
- mService.mCircularDisplayMask.positionSurface(defaultDw, defaultDh, mService.mRotation);
+ mService.mCircularDisplayMask.positionSurface(defaultDw, defaultDh,
+ mService.getDefaultDisplayRotation());
}
if (mService.mEmulatorDisplayOverlay != null) {
mService.mEmulatorDisplayOverlay.positionSurface(defaultDw, defaultDh,
- mService.mRotation);
+ mService.getDefaultDisplayRotation());
}
boolean focusDisplayed = false;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 76fb522..c0598ca 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -152,6 +152,14 @@
private void drawSnapshot(GraphicBuffer snapshot) {
mSurface.attachAndQueueBuffer(snapshot);
+ final boolean reportNextDraw;
+ synchronized (mService.mWindowMap) {
+ mHasDrawn = true;
+ reportNextDraw = mReportNextDraw;
+ }
+ if (reportNextDraw) {
+ reportDrawn();
+ }
mSurface.release();
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 993a918..57fb81c 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -161,7 +161,7 @@
// We just finished rotation animation which means we did not announce
// the rotation and waited for it to end, announce now.
accessibilityController.onRotationChangedLocked(
- mService.getDefaultDisplayContentLocked(), mService.mRotation);
+ mService.getDefaultDisplayContentLocked());
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c0cc923..64614fe 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -16,21 +16,15 @@
package com.android.server.wm;
-import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.MANAGE_APP_TOKENS;
import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.StatusBarManager.DISABLE_MASK;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import static android.content.Intent.ACTION_USER_REMOVED;
-import static android.content.Intent.EXTRA_PACKAGE_NAME;
-import static android.content.Intent.EXTRA_UID;
import static android.content.Intent.EXTRA_USER_HANDLE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.UserHandle.USER_NULL;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.DOCKED_INVALID;
@@ -69,8 +63,6 @@
import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_END;
import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_START;
-import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
-import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
import static com.android.server.wm.KeyguardDisableHandler.KEYGUARD_POLICY_CHANGED;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
@@ -112,7 +104,6 @@
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.IActivityManager;
-import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -222,6 +213,7 @@
import com.android.server.EventLogTags;
import com.android.server.FgThread;
import com.android.server.LocalServices;
+import com.android.server.LockGuard;
import com.android.server.UiThread;
import com.android.server.Watchdog;
import com.android.server.input.InputManagerService;
@@ -528,11 +520,6 @@
// The root of the device window hierarchy.
RootWindowContainer mRoot;
- // TODO: Move several of this states to the RootWindowContainer or DisplayContent
- int mRotation = 0;
- int mLastOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
- boolean mAltOrientation = false;
-
int mDockedStackCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
Rect mDockedStackCreateBounds;
@@ -546,13 +533,17 @@
}
class RotationWatcher {
- IRotationWatcher watcher;
- IBinder.DeathRecipient deathRecipient;
- RotationWatcher(IRotationWatcher w, IBinder.DeathRecipient d) {
- watcher = w;
- deathRecipient = d;
+ IRotationWatcher mWatcher;
+ IBinder.DeathRecipient mDeathRecipient;
+ int mDisplayId;
+ RotationWatcher(IRotationWatcher watcher, IBinder.DeathRecipient deathRecipient,
+ int displayId) {
+ mWatcher = watcher;
+ mDeathRecipient = deathRecipient;
+ mDisplayId = displayId;
}
}
+
ArrayList<RotationWatcher> mRotationWatchers = new ArrayList<>();
int mDeferredRotationPauseCount;
@@ -573,8 +564,6 @@
boolean mClientFreezingScreen = false;
int mAppsFreezingScreen = 0;
- int mLastWindowForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
- int mLastKeyguardForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
int mLayoutSeq = 0;
@@ -952,6 +941,7 @@
private WindowManagerService(Context context, InputManagerService inputManager,
boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
WindowManagerPolicy policy) {
+ LockGuard.installLock(this, LockGuard.INDEX_WINDOW);
mRoot = new RootWindowContainer(this);
mContext = context;
mHaveInputMethods = haveInputMethods;
@@ -1450,7 +1440,7 @@
} else {
taskBounds = null;
}
- if (mPolicy.getInsetHintLw(win.mAttrs, taskBounds, mRotation,
+ if (mPolicy.getInsetHintLw(win.mAttrs, taskBounds, displayInfo.rotation,
displayInfo.logicalWidth, displayInfo.logicalHeight, outContentInsets,
outStableInsets, outOutsets)) {
res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
@@ -2505,12 +2495,16 @@
boolean updateOrientationFromAppTokensLocked(boolean inTransaction, int displayId) {
long ident = Binder.clearCallingIdentity();
try {
- final int req = mRoot.getDisplayContent(displayId).getOrientation();
- if (req != mLastOrientation) {
- mLastOrientation = req;
+ final DisplayContent dc = mRoot.getDisplayContent(displayId);
+ final int req = dc.getOrientation();
+ if (req != dc.getLastOrientation()) {
+ dc.setLastOrientation(req);
//send a message to Policy indicating orientation change to take
//action like disabling/enabling sensors etc.,
- mPolicy.setCurrentOrientationLw(req);
+ // TODO(multi-display): Implement policy for secondary displays.
+ if (dc.isDefaultDisplay) {
+ mPolicy.setCurrentOrientationLw(req);
+ }
if (updateRotationUncheckedLocked(inTransaction, displayId)) {
// changed
return true;
@@ -2527,10 +2521,18 @@
// changed the real orientation our applied our screen rotation animation.
// For example, because a previous screen rotation was in progress.
boolean rotationNeedsUpdateLocked() {
- int rotation = mPolicy.rotationForOrientationLw(mLastOrientation, mRotation);
+ // TODO(multi-display): Check for updates on all displays. Need to have per-display policy
+ // to implement WindowManagerPolicy#rotationForOrientationLw() correctly.
+ final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
+ final int lastOrientation = defaultDisplayContent.getLastOrientation();
+ final int oldRotation = defaultDisplayContent.getRotation();
+ final boolean oldAltOrientation = defaultDisplayContent.getAltOrientation();
+
+ final int rotation = mPolicy.rotationForOrientationLw(lastOrientation,
+ oldRotation);
boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw(
- mLastOrientation, rotation);
- if (mRotation == rotation && mAltOrientation == altOrientation) {
+ lastOrientation, rotation);
+ if (oldRotation == rotation && oldAltOrientation == altOrientation) {
return false;
}
return true;
@@ -3758,6 +3760,7 @@
*/
@Override
public void freezeRotation(int rotation) {
+ // TODO(multi-display): Track which display is rotated.
if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
"freezeRotation()")) {
throw new SecurityException("Requires SET_ORIENTATION permission");
@@ -3767,12 +3770,14 @@
+ "rotation constant.");
}
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "freezeRotation: mRotation=" + mRotation);
+ final int defaultDisplayRotation = getDefaultDisplayRotation();
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "freezeRotation: mRotation="
+ + defaultDisplayRotation);
long origId = Binder.clearCallingIdentity();
try {
mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_LOCKED,
- rotation == -1 ? mRotation : rotation);
+ rotation == -1 ? defaultDisplayRotation : rotation);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -3791,7 +3796,8 @@
throw new SecurityException("Requires SET_ORIENTATION permission");
}
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "thawRotation: mRotation=" + mRotation);
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "thawRotation: mRotation="
+ + getDefaultDisplayRotation());
long origId = Binder.clearCallingIdentity();
try {
@@ -3915,8 +3921,10 @@
final DisplayContent dc = mRoot.getDisplayContent(displayId);
- final int oldRotation = mRotation;
- int rotation = mPolicy.rotationForOrientationLw(mLastOrientation, mRotation);
+ final int oldRotation = dc.getRotation();
+ final int lastOrientation = dc.getLastOrientation();
+ final boolean oldAltOrientation = dc.getAltOrientation();
+ int rotation = mPolicy.rotationForOrientationLw(lastOrientation, oldRotation);
final boolean rotateSeamlessly;
if (mPolicy.shouldRotateSeamlessly(oldRotation, rotation)) {
@@ -3956,29 +3964,30 @@
// an orientation that has different metrics than it expected.
// eg. Portrait instead of Landscape.
- boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw(
- mLastOrientation, rotation);
+ boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw(lastOrientation, rotation);
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Selected orientation " + mLastOrientation
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Selected orientation " + lastOrientation
+ ", got rotation " + rotation + " which has "
+ (altOrientation ? "incompatible" : "compatible") + " metrics");
- if (mRotation == rotation && mAltOrientation == altOrientation) {
+ if (oldRotation == rotation && oldAltOrientation == altOrientation) {
// No change.
return false;
}
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Rotation changed to " + rotation
- + (altOrientation ? " (alt)" : "") + " from " + mRotation
- + (mAltOrientation ? " (alt)" : "") + ", lastOrientation=" + mLastOrientation);
+ + (altOrientation ? " (alt)" : "") + " from " + oldRotation
+ + (oldAltOrientation ? " (alt)" : "") + ", lastOrientation=" + lastOrientation);
- if (DisplayContent.deltaRotation(rotation, mRotation) != 2) {
+ if (DisplayContent.deltaRotation(rotation, oldRotation) != 2) {
mWaitingForConfig = true;
}
- mRotation = rotation;
- mAltOrientation = altOrientation;
- mPolicy.setRotationLw(mRotation);
+ dc.setRotation(rotation);
+ dc.setAltOrientation(altOrientation);
+ if (dc.isDefaultDisplay) {
+ mPolicy.setRotationLw(rotation);
+ }
mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
@@ -4037,7 +4046,7 @@
if (rotateSeamlessly) {
dc.forAllWindows(w -> {
- w.mWinAnimator.seamlesslyRotateWindow(oldRotation, mRotation);
+ w.mWinAnimator.seamlesslyRotateWindow(oldRotation, rotation);
}, true /* traverseTopToBottom */);
}
@@ -4070,10 +4079,13 @@
mH.sendEmptyMessageDelayed(H.SEAMLESS_ROTATION_TIMEOUT, SEAMLESS_ROTATION_TIMEOUT_DURATION);
}
- for (int i=mRotationWatchers.size()-1; i>=0; i--) {
- try {
- mRotationWatchers.get(i).watcher.onRotationChanged(rotation);
- } catch (RemoteException e) {
+ for (int i = mRotationWatchers.size() - 1; i >= 0; i--) {
+ final RotationWatcher rotationWatcher = mRotationWatchers.get(i);
+ if (rotationWatcher.mDisplayId == displayId) {
+ try {
+ rotationWatcher.mWatcher.onRotationChanged(rotation);
+ } catch (RemoteException e) {
+ }
}
}
@@ -4082,16 +4094,17 @@
// windows in final state. Otherwise, we make this call at the rotation end.
if (screenRotationAnimation == null && mAccessibilityController != null
&& dc.getDisplayId() == DEFAULT_DISPLAY) {
- mAccessibilityController.onRotationChangedLocked(getDefaultDisplayContentLocked(),
- rotation);
+ mAccessibilityController.onRotationChangedLocked(getDefaultDisplayContentLocked());
}
return true;
}
@Override
- public int getRotation() {
- return mRotation;
+ public int getDefaultDisplayRotation() {
+ synchronized (mWindowMap) {
+ return getDefaultDisplayContentLocked().getRotation();
+ }
}
@Override
@@ -4100,16 +4113,16 @@
}
@Override
- public int watchRotation(IRotationWatcher watcher) {
+ public int watchRotation(IRotationWatcher watcher, int displayId) {
final IBinder watcherBinder = watcher.asBinder();
IBinder.DeathRecipient dr = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
synchronized (mWindowMap) {
for (int i=0; i<mRotationWatchers.size(); i++) {
- if (watcherBinder == mRotationWatchers.get(i).watcher.asBinder()) {
+ if (watcherBinder == mRotationWatchers.get(i).mWatcher.asBinder()) {
RotationWatcher removed = mRotationWatchers.remove(i);
- IBinder binder = removed.watcher.asBinder();
+ IBinder binder = removed.mWatcher.asBinder();
if (binder != null) {
binder.unlinkToDeath(this, 0);
}
@@ -4123,12 +4136,12 @@
synchronized (mWindowMap) {
try {
watcher.asBinder().linkToDeath(dr, 0);
- mRotationWatchers.add(new RotationWatcher(watcher, dr));
+ mRotationWatchers.add(new RotationWatcher(watcher, dr, displayId));
} catch (RemoteException e) {
// Client died, no cleanup needed.
}
- return mRotation;
+ return getDefaultDisplayRotation();
}
}
@@ -4138,11 +4151,11 @@
synchronized (mWindowMap) {
for (int i=0; i<mRotationWatchers.size(); i++) {
RotationWatcher rotationWatcher = mRotationWatchers.get(i);
- if (watcherBinder == rotationWatcher.watcher.asBinder()) {
+ if (watcherBinder == rotationWatcher.mWatcher.asBinder()) {
RotationWatcher removed = mRotationWatchers.remove(i);
- IBinder binder = removed.watcher.asBinder();
+ IBinder binder = removed.mWatcher.asBinder();
if (binder != null) {
- binder.unlinkToDeath(removed.deathRecipient, 0);
+ binder.unlinkToDeath(removed.mDeathRecipient, 0);
}
i--;
}
@@ -4170,10 +4183,9 @@
@Override
public int getPreferredOptionsPanelGravity() {
synchronized (mWindowMap) {
- final int rotation = getRotation();
-
// TODO(multidisplay): Assume that such devices physical keys are on the main screen.
final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ final int rotation = displayContent.getRotation();
if (displayContent.mInitialDisplayWidth < displayContent.mInitialDisplayHeight) {
// On devices with a natural orientation of portrait
switch (rotation) {
@@ -4707,9 +4719,14 @@
private DisplayInfo updateDisplayAndOrientationLocked(int uiMode, int displayId) {
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ // TODO(multi-display): Implement rotation for secondary displays.
+ final boolean isDefaultDisplay = displayContent.isDefaultDisplay;
+ final int displayRotation = displayContent.getRotation();
+ final boolean altDisplayOrientation = displayContent.getAltOrientation();
+
// Use the effective "visual" dimensions based on current rotation
- final boolean rotated = (mRotation == Surface.ROTATION_90
- || mRotation == Surface.ROTATION_270);
+ final boolean rotated = (displayRotation == Surface.ROTATION_90
+ || displayRotation == Surface.ROTATION_270);
final int realdw = rotated ?
displayContent.mBaseDisplayHeight : displayContent.mBaseDisplayWidth;
final int realdh = rotated ?
@@ -4717,7 +4734,7 @@
int dw = realdw;
int dh = realdh;
- if (mAltOrientation) {
+ if (altDisplayOrientation) {
if (realdw > realdh) {
// Turn landscape into portrait.
int maxw = (int)(realdh/1.3f);
@@ -4734,18 +4751,21 @@
}
// Update application display metrics.
- final int appWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation, uiMode, displayId);
- final int appHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation, uiMode,
+ final int appWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, displayRotation, uiMode,
+ displayId);
+ final int appHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, displayRotation, uiMode,
displayId);
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- displayInfo.rotation = mRotation;
+ displayInfo.rotation = displayRotation;
displayInfo.logicalWidth = dw;
displayInfo.logicalHeight = dh;
displayInfo.logicalDensityDpi = displayContent.mBaseDisplayDensity;
displayInfo.appWidth = appWidth;
displayInfo.appHeight = appHeight;
- displayInfo.getLogicalMetrics(mRealDisplayMetrics,
- CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+ if (isDefaultDisplay) {
+ displayInfo.getLogicalMetrics(mRealDisplayMetrics,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+ }
displayInfo.getAppMetrics(mDisplayMetrics);
if (displayContent.mDisplayScalingDisabled) {
displayInfo.flags |= Display.FLAG_SCALING_DISABLED;
@@ -4761,8 +4781,10 @@
Slog.i(TAG_WM, "Set app display size: " + appWidth + " x " + appHeight);
}
- mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(mDisplayMetrics,
- mCompatDisplayMetrics);
+ if (isDefaultDisplay) {
+ mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(mDisplayMetrics,
+ mCompatDisplayMetrics);
+ }
return displayInfo;
}
@@ -4775,13 +4797,13 @@
config.orientation = (dw <= dh) ? Configuration.ORIENTATION_PORTRAIT :
Configuration.ORIENTATION_LANDSCAPE;
config.screenWidthDp =
- (int)(mPolicy.getConfigDisplayWidth(dw, dh, mRotation, config.uiMode, displayId) /
- mDisplayMetrics.density);
+ (int)(mPolicy.getConfigDisplayWidth(dw, dh, displayInfo.rotation, config.uiMode,
+ displayId) / mDisplayMetrics.density);
config.screenHeightDp =
- (int)(mPolicy.getConfigDisplayHeight(dw, dh, mRotation, config.uiMode, displayId) /
- mDisplayMetrics.density);
- final boolean rotated = (mRotation == Surface.ROTATION_90
- || mRotation == Surface.ROTATION_270);
+ (int)(mPolicy.getConfigDisplayHeight(dw, dh, displayInfo.rotation, config.uiMode,
+ displayId) / mDisplayMetrics.density);
+ final boolean rotated = (displayInfo.rotation == Surface.ROTATION_90
+ || displayInfo.rotation == Surface.ROTATION_270);
computeSizeRangesAndScreenLayout(displayInfo, displayId, rotated, config.uiMode, dw, dh,
mDisplayMetrics.density, config);
@@ -5231,7 +5253,6 @@
public static final int NOTIFY_APP_TRANSITION_STARTING = 47;
public static final int NOTIFY_APP_TRANSITION_CANCELLED = 48;
public static final int NOTIFY_APP_TRANSITION_FINISHED = 49;
- public static final int NOTIFY_STARTING_WINDOW_DRAWN = 50;
public static final int UPDATE_ANIMATION_SCALE = 51;
public static final int WINDOW_HIDE_TIMEOUT = 52;
public static final int NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED = 53;
@@ -5660,7 +5681,7 @@
}
}
case NOTIFY_APP_TRANSITION_STARTING: {
- mAmInternal.notifyAppTransitionStarting(msg.arg1);
+ mAmInternal.notifyAppTransitionStarting((SparseIntArray) msg.obj);
}
break;
case NOTIFY_APP_TRANSITION_CANCELLED: {
@@ -5671,10 +5692,6 @@
mAmInternal.notifyAppTransitionFinished();
}
break;
- case NOTIFY_STARTING_WINDOW_DRAWN: {
- mAmInternal.notifyStartingWindowDrawn();
- }
- break;
case WINDOW_HIDE_TIMEOUT: {
final WindowState window = (WindowState) msg.obj;
synchronized(mWindowMap) {
@@ -7002,10 +7019,14 @@
pw.print(" client="); pw.print(mClientFreezingScreen);
pw.print(" apps="); pw.print(mAppsFreezingScreen);
pw.print(" waitingForConfig="); pw.println(mWaitingForConfig);
- pw.print(" mRotation="); pw.print(mRotation);
- pw.print(" mAltOrientation="); pw.println(mAltOrientation);
- pw.print(" mLastWindowForcedOrientation="); pw.print(mLastWindowForcedOrientation);
- pw.print(" mLastOrientation="); pw.println(mLastOrientation);
+ final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
+ pw.print(" mRotation="); pw.print(defaultDisplayContent.getRotation());
+ pw.print(" mAltOrientation=");
+ pw.println(defaultDisplayContent.getAltOrientation());
+ pw.print(" mLastWindowForcedOrientation=");
+ pw.print(defaultDisplayContent.getLastWindowForcedOrientation());
+ pw.print(" mLastOrientation=");
+ pw.println(defaultDisplayContent.getLastOrientation());
pw.print(" mDeferredRotationPauseCount="); pw.println(mDeferredRotationPauseCount);
pw.print(" Animation settings: disabled="); pw.print(mAnimationsDisabled);
pw.print(" window="); pw.print(mWindowAnimationScaleSetting);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 7825903..ccbc5ba 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2835,7 +2835,7 @@
// Sometimes we save surfaces due to layout invisible directly after rotation occurs.
// However this means the surface was never laid out in the new orientation.
// We can only restore to the last rotation we were laid out as visible in.
- if (mLastVisibleLayoutRotation != mService.mRotation) {
+ if (mLastVisibleLayoutRotation != getDisplayContent().getRotation()) {
destroySavedSurface();
return false;
}
@@ -4359,7 +4359,7 @@
mWinAnimator.mEnterAnimationPending = true;
}
- mLastVisibleLayoutRotation = mService.mRotation;
+ mLastVisibleLayoutRotation = getDisplayContent().getRotation();
mWinAnimator.mEnteringAnimation = true;
if ((result & RELAYOUT_RES_FIRST_TIME) != 0) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index f7d3343..c9863c5 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -566,7 +566,10 @@
pw.print(" rect=("); pw.print(mSurfaceX);
pw.print(","); pw.print(mSurfaceY);
pw.print(") "); pw.print(mSurfaceW);
- pw.print(" x "); pw.println(mSurfaceH);
+ pw.print(" x "); pw.print(mSurfaceH);
+ pw.print(" transform=("); pw.print(mLastDsdx); pw.print(", ");
+ pw.print(mLastDtdx); pw.print(", "); pw.print(mLastDsdy);
+ pw.print(", "); pw.print(mLastDtdy); pw.println(")");
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index f247ebe..3cb96a1 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -1,9 +1,10 @@
package com.android.server.wm;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManagerInternal.APP_TRANSITION_SAVED_SURFACE;
-import static android.app.ActivityManagerInternal.APP_TRANSITION_STARTING_WINDOW;
-import static android.app.ActivityManagerInternal.APP_TRANSITION_TIMEOUT;
+import static android.app.ActivityManagerInternal.APP_TRANSITION_SNAPSHOT;
+import static android.app.ActivityManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
import static android.app.ActivityManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
@@ -45,6 +46,7 @@
import android.os.Trace;
import android.util.ArraySet;
import android.util.Slog;
+import android.util.SparseIntArray;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.Surface;
@@ -93,6 +95,7 @@
private final LayerAndToken mTmpLayerAndToken = new LayerAndToken();
private final ArrayList<SurfaceControl> mPendingDestroyingSurfaces = new ArrayList<>();
+ private final SparseIntArray mTempTransitionReasons = new SparseIntArray();
public WindowSurfacePlacer(WindowManagerService service) {
mService = service;
@@ -495,7 +498,7 @@
mService.mAnimator.getScreenRotationAnimationLocked(
Display.DEFAULT_DISPLAY);
- int reason = APP_TRANSITION_TIMEOUT;
+ final SparseIntArray reasons = mTempTransitionReasons;
if (!mService.mAppTransition.isTimeout()) {
// Imagine the case where we are changing orientation due to an app transition, but a previous
// orientation change is still in progress. We won't process the orientation change
@@ -526,11 +529,15 @@
if (!allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) {
return false;
}
+ final TaskStack stack = wtoken.getStack();
+ final int stackId = stack != null ? stack.mStackId : INVALID_STACK_ID;
if (allDrawn) {
- reason = drawnBeforeRestoring ? APP_TRANSITION_WINDOWS_DRAWN
- : APP_TRANSITION_SAVED_SURFACE;
+ reasons.put(stackId, drawnBeforeRestoring ? APP_TRANSITION_WINDOWS_DRAWN
+ : APP_TRANSITION_SAVED_SURFACE);
} else {
- reason = APP_TRANSITION_STARTING_WINDOW;
+ reasons.put(stackId, wtoken.startingData instanceof SplashScreenStartingData
+ ? APP_TRANSITION_SPLASH_SCREEN
+ : APP_TRANSITION_SNAPSHOT);
}
}
@@ -552,12 +559,13 @@
boolean wallpaperReady = !mWallpaperControllerLocked.isWallpaperVisible() ||
mWallpaperControllerLocked.wallpaperTransitionReady();
if (wallpaperReady) {
- mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING, reason, 0).sendToTarget();
+ mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING, reasons.clone())
+ .sendToTarget();
return true;
}
return false;
}
- mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING, reason, 0).sendToTarget();
+ mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING, reasons.clone()).sendToTarget();
return true;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7ad0292..0e6a542 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3141,6 +3141,10 @@
throw new IllegalArgumentException("Only apps in internal storage can be active admin: "
+ adminReceiver);
}
+ if (info.getActivityInfo().applicationInfo.isInstantApp()) {
+ throw new IllegalArgumentException("Instant apps cannot be device admins: "
+ + adminReceiver);
+ }
synchronized (this) {
long ident = mInjector.binderClearCallingIdentity();
try {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 1a0aff7..a8423e2 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -195,7 +195,7 @@
private static final String WALLPAPER_SERVICE_CLASS =
"com.android.server.wallpaper.WallpaperManagerService$Lifecycle";
private static final String AUTO_FILL_MANAGER_SERVICE_CLASS =
- "com.android.server.autofill.AutoFillManagerService";
+ "com.android.server.autofill.AutofillManagerService";
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index 450f9b6..5f215f9 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -427,13 +427,8 @@
public void testCreateChannel_blocked() throws Exception {
mHelper.setImportance(pkg, uid, NotificationManager.IMPORTANCE_NONE);
- try {
- mHelper.createNotificationChannel(pkg, uid,
- new NotificationChannel(pkg, "", IMPORTANCE_LOW), true);
- fail("Channel creation should fail");
- } catch (IllegalArgumentException e) {
- // pass
- }
+ mHelper.createNotificationChannel(pkg, uid,
+ new NotificationChannel(pkg, "bananas", IMPORTANCE_LOW), true);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java
index 60842a6..340c624 100644
--- a/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java
@@ -34,6 +34,8 @@
import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
+import com.android.internal.util.IntPair;
+
/**
* This test exercises the
* {@link com.android.server.accessibility.AccessibilityManagerService} by mocking the
@@ -109,7 +111,7 @@
// invoke the method under test
final int stateFlagsDisabled =
- mManagerService.addClient(mockClient, UserHandle.USER_CURRENT);
+ IntPair.first(mManagerService.addClient(mockClient, UserHandle.USER_CURRENT));
boolean enabledAccessibilityDisabled =
(stateFlagsDisabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
@@ -122,7 +124,7 @@
// invoke the method under test
final int stateFlagsEnabled =
- mManagerService.addClient(mockClient, UserHandle.USER_CURRENT);
+ IntPair.first(mManagerService.addClient(mockClient, UserHandle.USER_CURRENT));
boolean enabledAccessibilityEnabled =
(stateFlagsEnabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
@@ -144,7 +146,7 @@
// invoke the method under test
final int stateFlagsEnabled =
- mManagerService.addClient(mockClient, UserHandle.USER_CURRENT);
+ IntPair.first(mManagerService.addClient(mockClient, UserHandle.USER_CURRENT));
boolean enabledAccessibilityEnabled =
(stateFlagsEnabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
@@ -157,7 +159,7 @@
// invoke the method under test
final int stateFlagsDisabled =
- mManagerService.addClient(mockClient, UserHandle.USER_CURRENT);
+ IntPair.first(mManagerService.addClient(mockClient, UserHandle.USER_CURRENT));
boolean enabledAccessibilityDisabled =
(stateFlagsDisabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
@@ -572,8 +574,9 @@
public void notifyServicesStateChanged() {}
- public void setTouchExplorationEnabled(boolean enabled) {
- }
+ public void setRelevantEventTypes(int eventTypes) {}
+
+ public void setTouchExplorationEnabled(boolean enabled) {}
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java b/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java
index 6e3e6c6..9261771 100644
--- a/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java
@@ -18,7 +18,6 @@
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -32,6 +31,8 @@
import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
+import com.android.internal.util.IntPair;
+
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -60,10 +61,12 @@
private AccessibilityManager createManager(boolean enabled) throws Exception {
if (enabled) {
when(mMockService.addClient(any(IAccessibilityManagerClient.class), anyInt()))
- .thenReturn(AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED);
+ .thenReturn(
+ IntPair.of(AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED,
+ AccessibilityEvent.TYPES_ALL_MASK));
} else {
when(mMockService.addClient(any(IAccessibilityManagerClient.class), anyInt()))
- .thenReturn(0);
+ .thenReturn(IntPair.of(0, AccessibilityEvent.TYPES_ALL_MASK));
}
AccessibilityManager manager =
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 fa0bd39..72fb78e 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
@@ -61,7 +61,6 @@
@Before
public void setup() {
-
mUser0 = 0;
mUser1 = 1;
@@ -352,6 +351,15 @@
assertNull(pui);
}
+ @Test
+ public void testNotifyFrameworkLoad() {
+ String frameworkDex = "/system/framework/com.android.location.provider.jar";
+ // Load a dex file from framework.
+ notifyDexLoad(mFooUser0, Arrays.asList(frameworkDex), mUser0);
+ // The dex file should not be recognized as a package.
+ assertNull(mDexManager.getPackageUseInfo(frameworkDex));
+ }
+
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/policy/AccessibilityShortcutControllerTest.java b/services/tests/servicestests/src/com/android/server/policy/AccessibilityShortcutControllerTest.java
index 8329d68..4d5f783 100644
--- a/services/tests/servicestests/src/com/android/server/policy/AccessibilityShortcutControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/AccessibilityShortcutControllerTest.java
@@ -21,9 +21,11 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.os.Handler;
+import android.os.Vibrator;
import android.provider.Settings;
import android.support.test.runner.AndroidJUnit4;
@@ -54,6 +56,7 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.AdditionalMatchers.aryEq;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyObject;
@@ -67,6 +70,11 @@
@RunWith(AndroidJUnit4.class)
public class AccessibilityShortcutControllerTest {
private static final String SERVICE_NAME_STRING = "fake.package/fake.service.name";
+ private static final long VIBRATOR_PATTERN_1 = 100L;
+ private static final long VIBRATOR_PATTERN_2 = 150L;
+ private static final int[] VIBRATOR_PATTERN_INT = {(int) VIBRATOR_PATTERN_1,
+ (int) VIBRATOR_PATTERN_2};
+ private static final long[] VIBRATOR_PATTERN_LONG = {VIBRATOR_PATTERN_1, VIBRATOR_PATTERN_2};
private @Mock Context mContext;
private @Mock FrameworkObjectProvider mFrameworkObjectProvider;
@@ -77,6 +85,8 @@
private @Mock AccessibilityServiceInfo mServiceInfo;
private @Mock Resources mResources;
private @Mock Toast mToast;
+ private @Mock Vibrator mVibrator;
+ private @Mock ApplicationInfo mApplicationInfo;
private MockContentResolver mContentResolver;
private WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams();
@@ -85,10 +95,15 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ when(mVibrator.hasVibrator()).thenReturn(true);
+
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo);
+ when(mContext.getSystemService(Context.VIBRATOR_SERVICE)).thenReturn(mVibrator);
+
mContentResolver = new MockContentResolver(mContext);
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
when(mContext.getContentResolver()).thenReturn(mContentResolver);
- when(mContext.getResources()).thenReturn(mResources);
when(mAccessibilityManagerService.getInstalledAccessibilityServiceList(anyInt()))
.thenReturn(Collections.singletonList(mServiceInfo));
@@ -104,6 +119,8 @@
.thenReturn(mToast);
when(mResources.getString(anyInt())).thenReturn("Howdy %s");
+ when(mResources.getIntArray(anyInt())).thenReturn(VIBRATOR_PATTERN_INT);
+
ResolveInfo resolveInfo = mock(ResolveInfo.class);
when(resolveInfo.loadLabel(anyObject())).thenReturn("Service name");
when(mServiceInfo.getResolveInfo()).thenReturn(resolveInfo);
@@ -171,6 +188,14 @@
}
@Test
+ public void testOnAccessibilityShortcut_vibrates() {
+ configureShortcutEnabled();
+ AccessibilityShortcutController accessibilityShortcutController = getController();
+ accessibilityShortcutController.performAccessibilityShortcut();
+ verify(mVibrator).vibrate(aryEq(VIBRATOR_PATTERN_LONG), eq(-1), anyObject());
+ }
+
+ @Test
public void testOnAccessibilityShortcut_firstTime_showsWarningDialog()
throws Exception {
configureShortcutEnabled();
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
index e15d40e..9f50a2c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -112,7 +112,7 @@
appWindowToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
sWm.updateOrientationFromAppTokens(sDisplayContent.getOverrideConfiguration(), null,
sDisplayContent.getDisplayId());
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, sWm.mLastOrientation);
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, sDisplayContent.getLastOrientation());
appWindow.resizeReported = false;
// Update the orientation to perform 180 degree rotation and check that resize was reported.
@@ -120,7 +120,7 @@
sWm.updateOrientationFromAppTokens(sDisplayContent.getOverrideConfiguration(), null,
sDisplayContent.getDisplayId());
sWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
- assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, sWm.mLastOrientation);
+ assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, sDisplayContent.getLastOrientation());
assertTrue(appWindow.resizeReported);
appWindow.removeImmediately();
}
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index 8166c15..ecc2b7f 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -28,6 +28,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageStats;
import android.content.pm.UserInfo;
+import android.net.TrafficStats;
import android.os.Binder;
import android.os.Environment;
import android.os.FileUtils;
@@ -43,6 +44,7 @@
import android.os.storage.VolumeInfo;
import android.provider.Settings;
import android.text.format.DateUtils;
+import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -56,6 +58,7 @@
import com.android.server.storage.CacheQuotaStrategy;
import java.io.IOException;
+import java.util.Map;
public class StorageStatsService extends IStorageStatsManager.Stub {
private static final String TAG = "StorageStatsService";
@@ -84,6 +87,7 @@
private final UserManager mUser;
private final PackageManager mPackage;
private final StorageManager mStorage;
+ private final Map<String, Map<Integer, Long>> mCacheQuotas;
private final Installer mInstaller;
private final H mHandler;
@@ -94,6 +98,7 @@
mUser = Preconditions.checkNotNull(context.getSystemService(UserManager.class));
mPackage = Preconditions.checkNotNull(context.getPackageManager());
mStorage = Preconditions.checkNotNull(context.getSystemService(StorageManager.class));
+ mCacheQuotas = new ArrayMap<>();
mInstaller = new Installer(context);
mInstaller.onStart();
@@ -178,6 +183,21 @@
}
@Override
+ public long getCacheQuotaBytes(String volumeUuid, int uid, String callingPackage) {
+ enforcePermission(Binder.getCallingUid(), callingPackage);
+
+ if (mCacheQuotas.containsKey(volumeUuid)) {
+ // TODO: Change to SparseLongArray.
+ Map<Integer, Long> uidMap = mCacheQuotas.get(volumeUuid);
+ if (uidMap.containsKey(uid)) {
+ return uidMap.get(uid);
+ }
+ }
+
+ return 64 * TrafficStats.MB_IN_BYTES;
+ }
+
+ @Override
public StorageStats queryStatsForPackage(String volumeUuid, String packageName, int userId,
String callingPackage) {
enforcePermission(Binder.getCallingUid(), callingPackage);
@@ -188,7 +208,8 @@
final ApplicationInfo appInfo;
try {
- appInfo = mPackage.getApplicationInfoAsUser(packageName, 0, userId);
+ appInfo = mPackage.getApplicationInfoAsUser(packageName,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
} catch (NameNotFoundException e) {
throw new IllegalStateException(e);
}
@@ -231,8 +252,8 @@
for (int i = 0; i < packageNames.length; i++) {
try {
- codePaths[i] = mPackage.getApplicationInfoAsUser(packageNames[i], 0,
- userId).getCodePath();
+ codePaths[i] = mPackage.getApplicationInfoAsUser(packageNames[i],
+ PackageManager.MATCH_UNINSTALLED_PACKAGES, userId).getCodePath();
} catch (NameNotFoundException e) {
throw new IllegalStateException(e);
}
@@ -264,7 +285,8 @@
}
int[] appIds = null;
- for (ApplicationInfo app : mPackage.getInstalledApplicationsAsUser(0, userId)) {
+ for (ApplicationInfo app : mPackage.getInstalledApplicationsAsUser(
+ PackageManager.MATCH_UNINSTALLED_PACKAGES, userId)) {
final int appId = UserHandle.getAppId(app.uid);
if (!ArrayUtils.contains(appIds, appId)) {
appIds = ArrayUtils.appendInt(appIds, appId);
@@ -433,7 +455,7 @@
private CacheQuotaStrategy getInitializedStrategy() {
UsageStatsManagerInternal usageStatsManager =
LocalServices.getService(UsageStatsManagerInternal.class);
- return new CacheQuotaStrategy(mContext, usageStatsManager, mInstaller);
+ return new CacheQuotaStrategy(mContext, usageStatsManager, mInstaller, mCacheQuotas);
}
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 632a1d6..748e32a 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1312,6 +1312,68 @@
public static final String KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY =
"call_forwarding_blocks_while_roaming_string_array";
+ /**
+ * The day of the month (1-31) on which the data cycle rolls over.
+ * <p>
+ * If the current month does not have this day, the cycle will roll over at the start of the
+ * next month.
+ * <p>
+ * This setting may be still overridden by explicit user choice. By default, the platform value
+ * will be used.
+ */
+ public static final String KEY_MONTHLY_DATA_CYCLE_DAY_INT =
+ "monthly_data_cycle_day_int";
+
+ /**
+ * When {@link #KEY_MONTHLY_DATA_CYCLE_DAY_INT}, {@link #KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG},
+ * or {@link #KEY_DATA_WARNING_THRESHOLD_BYTES_LONG} are set to this value, the platform default
+ * value will be used for that key.
+ *
+ * @hide
+ */
+ public static final int DATA_CYCLE_USE_PLATFORM_DEFAULT = -1;
+
+ /**
+ * Flag indicating that a data cycle threshold should be disabled.
+ * <p>
+ * If {@link #KEY_DATA_WARNING_THRESHOLD_BYTES_LONG} is set to this value, the platform's
+ * default data warning, if one exists, will be disabled. A user selected data warning will not
+ * be overridden.
+ * <p>
+ * If {@link #KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG} is set to this value, the platform's
+ * default data limit, if one exists, will be disabled. A user selected data limit will not be
+ * overridden.
+ */
+ public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2;
+
+ /**
+ * Controls the data usage warning.
+ * <p>
+ * If the user uses more than this amount of data in their billing cycle, as defined by
+ * {@link #KEY_MONTHLY_DATA_CYCLE_DAY_INT}, the user will be alerted about the usage.
+ * If the value is set to {@link #DATA_CYCLE_THRESHOLD_DISABLED}, the data usage warning will
+ * be disabled.
+ * <p>
+ * This setting may be overridden by explicit user choice. By default, the platform value
+ * will be used.
+ */
+ public static final String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG =
+ "data_warning_threshold_bytes_long";
+
+ /**
+ * Controls the cellular data limit.
+ * <p>
+ * If the user uses more than this amount of data in their billing cycle, as defined by
+ * {@link #KEY_MONTHLY_DATA_CYCLE_DAY_INT}, cellular data will be turned off by the user's
+ * phone. If the value is set to {@link #DATA_CYCLE_THRESHOLD_DISABLED}, the data limit will be
+ * disabled.
+ * <p>
+ * This setting may be overridden by explicit user choice. By default, the platform value
+ * will be used.
+ */
+ public static final String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG =
+ "data_limit_threshold_bytes_long";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -1515,6 +1577,10 @@
});
sDefaults.putStringArray(KEY_CARRIER_DEFAULT_REDIRECTION_URL_STRING_ARRAY, null);
+ sDefaults.putInt(KEY_MONTHLY_DATA_CYCLE_DAY_INT, DATA_CYCLE_USE_PLATFORM_DEFAULT);
+ sDefaults.putLong(KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, DATA_CYCLE_USE_PLATFORM_DEFAULT);
+ sDefaults.putLong(KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG, DATA_CYCLE_USE_PLATFORM_DEFAULT);
+
// Rat families: {GPRS, EDGE}, {EVDO, EVDO_A, EVDO_B}, {UMTS, HSPA, HSDPA, HSUPA, HSPAP},
// {LTE, LTE_CA}
// Order is important - lowest precidence first
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 3f5ca84..da1d998 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -492,10 +492,22 @@
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+ boolean visibleToInstantApps) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler) {
throw new UnsupportedOperationException();
}
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+ String broadcastPermission, Handler scheduler, boolean visibleToInstantApps) {
+ throw new UnsupportedOperationException();
+ }
+
/** @hide */
@Override
public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
diff --git a/tests/net/java/android/net/ip/IpManagerTest.java b/tests/net/java/android/net/ip/IpManagerTest.java
new file mode 100644
index 0000000..025b017
--- /dev/null
+++ b/tests/net/java/android/net/ip/IpManagerTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.ip;
+
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.INetworkManagementService;
+import android.provider.Settings;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.mock.MockContentResolver;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.R;
+
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for IpManager.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IpManagerTest {
+ private static final int DEFAULT_AVOIDBADWIFI_CONFIG_VALUE = 1;
+
+ @Mock private Context mContext;
+ @Mock private INetworkManagementService mNMService;
+ @Mock private Resources mResources;
+ private MockContentResolver mContentResolver;
+
+ @Before public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mResources.getInteger(R.integer.config_networkAvoidBadWifi))
+ .thenReturn(DEFAULT_AVOIDBADWIFI_CONFIG_VALUE);
+
+ mContentResolver = new MockContentResolver();
+ mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ }
+
+ @Test
+ public void testNullCallbackDoesNotThrow() throws Exception {
+ final IpManager ipm = new IpManager(mContext, "lo", null, mNMService);
+ }
+
+ @Test
+ public void testInvalidInterfaceDoesNotThrow() throws Exception {
+ final IpManager.Callback cb = new IpManager.Callback();
+ final IpManager ipm = new IpManager(mContext, "test_wlan0", cb, mNMService);
+ }
+}
diff --git a/tests/testables/Android.mk b/tests/testables/Android.mk
new file mode 100644
index 0000000..58399fd
--- /dev/null
+++ b/tests/testables/Android.mk
@@ -0,0 +1,33 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := testables
+LOCAL_MODULE_TAG := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ mockito-updated-target-minus-junit4 \
+ legacy-android-test
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysUIRunner.java b/tests/testables/src/android/testing/AndroidTestingRunner.java
similarity index 89%
rename from packages/SystemUI/tests/src/com/android/systemui/SysUIRunner.java
rename to tests/testables/src/android/testing/AndroidTestingRunner.java
index fd99d1d..816ed03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysUIRunner.java
+++ b/tests/testables/src/android/testing/AndroidTestingRunner.java
@@ -12,14 +12,14 @@
* permissions and limitations under the License.
*/
-package com.android.systemui;
+package android.testing;
import android.support.test.internal.runner.junit4.statement.RunAfters;
import android.support.test.internal.runner.junit4.statement.RunBefores;
import android.support.test.internal.runner.junit4.statement.UiThreadStatement;
-import com.android.systemui.utils.TestableLooper.LooperStatement;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
+import android.testing.TestableLooper.LooperStatement;
+import android.testing.TestableLooper.RunWithLooper;
import org.junit.After;
import org.junit.Before;
@@ -32,12 +32,15 @@
import java.util.List;
-public class SysUIRunner extends BlockJUnit4ClassRunner {
+/**
+ * A runner with support for extra annotations provided by the Testables library.
+ */
+public class AndroidTestingRunner extends BlockJUnit4ClassRunner {
private final long mTimeout;
private final Class<?> mKlass;
- public SysUIRunner(Class<?> klass) throws InitializationError {
+ public AndroidTestingRunner(Class<?> klass) throws InitializationError {
super(klass);
mKlass = klass;
// Can't seem to get reference to timeout parameter from here, so set default to 10 mins.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/FragmentTestCase.java b/tests/testables/src/android/testing/BaseFragmentTest.java
similarity index 86%
rename from packages/SystemUI/tests/src/com/android/systemui/FragmentTestCase.java
rename to tests/testables/src/android/testing/BaseFragmentTest.java
index 1678d92..53841d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/FragmentTestCase.java
+++ b/tests/testables/src/android/testing/BaseFragmentTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 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
@@ -12,7 +12,9 @@
* permissions and limitations under the License.
*/
-package com.android.systemui;
+package android.testing;
+
+import static org.junit.Assert.assertNotNull;
import android.annotation.Nullable;
import android.app.Fragment;
@@ -21,20 +23,17 @@
import android.app.FragmentManagerNonConfig;
import android.graphics.PixelFormat;
import android.os.Handler;
-import android.os.Looper;
import android.os.Parcelable;
+import android.support.test.InstrumentationRegistry;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.widget.FrameLayout;
-import com.android.systemui.utils.TestableLooper;
-import com.android.systemui.utils.ViewUtils;
-import com.android.systemui.utils.leaks.LeakCheckedTest;
-
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import java.io.FileDescriptor;
@@ -46,7 +45,7 @@
* the host for subclasses, so they can push it into desired states and do any unit testing
* required.
*/
-public abstract class FragmentTestCase extends LeakCheckedTest {
+public abstract class BaseFragmentTest {
private static final int VIEW_ID = 42;
private final Class<? extends Fragment> mCls;
@@ -55,7 +54,10 @@
protected FragmentController mFragments;
protected Fragment mFragment;
- public FragmentTestCase(Class<? extends Fragment> cls) {
+ @Rule
+ public final TestableContext mContext = getContext();
+
+ public BaseFragmentTest(Class<? extends Fragment> cls) {
mCls = cls;
}
@@ -64,6 +66,8 @@
mView = new FrameLayout(mContext);
mView.setId(VIEW_ID);
+ assertNotNull("BaseFragmentTest must be tagged with @RunWithLooper",
+ TestableLooper.get(this));
TestableLooper.get(this).runWithLooper(() -> {
mHandler = new Handler();
@@ -76,8 +80,8 @@
});
}
- private String hex(Looper looper) {
- return Integer.toHexString(System.identityHashCode(looper));
+ protected TestableContext getContext() {
+ return new TestableContext(InstrumentationRegistry.getContext());
}
@After
@@ -174,14 +178,14 @@
return mView.findViewById(id);
}
- private class HostCallbacks extends FragmentHostCallback<FragmentTestCase> {
+ private class HostCallbacks extends FragmentHostCallback<BaseFragmentTest> {
public HostCallbacks() {
- super(mContext, FragmentTestCase.this.mHandler, 0);
+ super(mContext, BaseFragmentTest.this.mHandler, 0);
}
@Override
- public FragmentTestCase onGetHost() {
- return FragmentTestCase.this;
+ public BaseFragmentTest onGetHost() {
+ return BaseFragmentTest.this;
}
@Override
@@ -220,7 +224,7 @@
@Nullable
@Override
public View onFindViewById(int id) {
- return FragmentTestCase.this.findViewById(id);
+ return BaseFragmentTest.this.findViewById(id);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/LayoutInflaterBuilder.java b/tests/testables/src/android/testing/LayoutInflaterBuilder.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/util/LayoutInflaterBuilder.java
rename to tests/testables/src/android/testing/LayoutInflaterBuilder.java
index 5cfe677..098302e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/LayoutInflaterBuilder.java
+++ b/tests/testables/src/android/testing/LayoutInflaterBuilder.java
@@ -1,20 +1,18 @@
/*
- * Copyright (C) 2016 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.util;
+package android.testing;
import android.annotation.NonNull;
import android.content.Context;
diff --git a/tests/testables/src/android/testing/LeakCheck.java b/tests/testables/src/android/testing/LeakCheck.java
new file mode 100644
index 0000000..8daaa8f
--- /dev/null
+++ b/tests/testables/src/android/testing/LeakCheck.java
@@ -0,0 +1,101 @@
+/*
+ * 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.testing;
+
+import android.util.ArrayMap;
+import android.util.Log;
+
+import org.junit.Assert;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class LeakCheck extends TestWatcher {
+
+ private final Map<String, Tracker> mTrackers = new HashMap<>();
+
+ public LeakCheck() {
+ }
+
+ @Override
+ protected void succeeded(Description description) {
+ verify();
+ }
+
+ public Tracker getTracker(String tag) {
+ Tracker t = mTrackers.get(tag);
+ if (t == null) {
+ t = new Tracker();
+ mTrackers.put(tag, t);
+ }
+ return t;
+ }
+
+ public void verify() {
+ mTrackers.values().forEach(Tracker::verify);
+ }
+
+ public static class LeakInfo {
+ private static final String TAG = "LeakInfo";
+ private List<Throwable> mThrowables = new ArrayList<>();
+
+ LeakInfo() {
+ }
+
+ public void addAllocation(Throwable t) {
+ // TODO: Drop off the first element in the stack trace here to have a cleaner stack.
+ mThrowables.add(t);
+ }
+
+ public void clearAllocations() {
+ mThrowables.clear();
+ }
+
+ void verify() {
+ if (mThrowables.size() == 0) return;
+ Log.e(TAG, "Listener or binding not properly released");
+ for (Throwable t : mThrowables) {
+ Log.e(TAG, "Allocation found", t);
+ }
+ StringWriter writer = new StringWriter();
+ mThrowables.get(0).printStackTrace(new PrintWriter(writer));
+ Assert.fail("Listener or binding not properly released\n"
+ + writer.toString());
+ }
+ }
+
+ public static class Tracker {
+ private Map<Object, LeakInfo> mObjects = new ArrayMap<>();
+
+ public LeakInfo getLeakInfo(Object object) {
+ LeakInfo leakInfo = mObjects.get(object);
+ if (leakInfo == null) {
+ leakInfo = new LeakInfo();
+ mObjects.put(object, leakInfo);
+ }
+ return leakInfo;
+ }
+
+ void verify() {
+ mObjects.values().forEach(LeakInfo::verify);
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeContentResolver.java b/tests/testables/src/android/testing/TestableContentResolver.java
similarity index 94%
rename from packages/SystemUI/tests/src/com/android/systemui/utils/FakeContentResolver.java
rename to tests/testables/src/android/testing/TestableContentResolver.java
index 34f2e01..bfafbe0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeContentResolver.java
+++ b/tests/testables/src/android/testing/TestableContentResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 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
@@ -12,7 +12,7 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.utils;
+package android.testing;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -29,14 +29,14 @@
/**
* Alternative to a MockContentResolver that falls back to real providers.
*/
-public class FakeContentResolver extends ContentResolver {
+public class TestableContentResolver extends ContentResolver {
private final Map<String, ContentProvider> mProviders = Maps.newHashMap();
private final ContentResolver mParent;
private final ArraySet<ContentProvider> mInUse = new ArraySet<>();
private boolean mFallbackToExisting;
- public FakeContentResolver(Context context) {
+ public TestableContentResolver(Context context) {
super(context);
mParent = context.getContentResolver();
mFallbackToExisting = true;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java b/tests/testables/src/android/testing/TestableContext.java
similarity index 70%
rename from packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java
rename to tests/testables/src/android/testing/TestableContext.java
index 1429390..cb5d4cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java
+++ b/tests/testables/src/android/testing/TestableContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 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
@@ -12,7 +12,7 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.utils;
+package android.testing;
import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks;
@@ -32,36 +32,59 @@
import android.util.ArrayMap;
import android.view.LayoutInflater;
-import com.android.systemui.SysUiServiceProvider;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.utils.leaks.Tracker;
+import org.junit.rules.TestRule;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
-public class TestableContext extends ContextWrapper implements SysUiServiceProvider {
+/**
+ * A ContextWrapper with utilities specifically designed to make Testing easier.
+ *
+ * <ul>
+ * <li>System services can be mocked out with {@link #addMockSystemService}</li>
+ * <li>Service binding can be mocked out with {@link #addMockService}</li>
+ * <li>Settings support {@link TestableSettings}</li>
+ * <li>Has support for {@link LeakCheck} for services and receivers</li>
+ * </ul>
+ *
+ * <p>TestableContext should be defined as a rule on your test so it can clean up after itself.
+ * Like the following:</p>
+ * <pre class="prettyprint">
+ * {@literal
+ * @Rule
+ * private final TestableContext mContext = new TestableContext(InstrumentationRegister.getContext());
+ * }
+ * </pre>
+ */
+public class TestableContext extends ContextWrapper implements TestRule {
- private final FakeContentResolver mFakeContentResolver;
- private final FakeSettingsProvider mSettingsProvider;
+ private final TestableContentResolver mTestableContentResolver;
+ private final TestableSettings mSettingsProvider;
private ArrayMap<String, Object> mMockSystemServices;
private ArrayMap<ComponentName, IBinder> mMockServices;
private ArrayMap<ServiceConnection, ComponentName> mActiveServices;
- private ArrayMap<Class<?>, Object> mComponents;
private PackageManager mMockPackageManager;
- private Tracker mReceiver;
- private Tracker mService;
- private Tracker mComponent;
+ private LeakCheck.Tracker mReceiver;
+ private LeakCheck.Tracker mService;
+ private LeakCheck.Tracker mComponent;
- public TestableContext(Context base, SysuiTestCase test) {
+ public TestableContext(Context base) {
+ this(base, null);
+ }
+
+ public TestableContext(Context base, LeakCheck check) {
super(base);
- mFakeContentResolver = new FakeContentResolver(base);
+ mTestableContentResolver = new TestableContentResolver(base);
ContentProviderClient settings = base.getContentResolver()
.acquireContentProviderClient(Settings.AUTHORITY);
- mSettingsProvider = FakeSettingsProvider.getFakeSettingsProvider(settings,
- mFakeContentResolver);
- mFakeContentResolver.addProvider(Settings.AUTHORITY, mSettingsProvider);
- mReceiver = test.getTracker("receiver");
- mService = test.getTracker("service");
- mComponent = test.getTracker("component");
+ mSettingsProvider = TestableSettings.getFakeSettingsProvider(settings,
+ mTestableContentResolver);
+ mTestableContentResolver.addProvider(Settings.AUTHORITY, mSettingsProvider.getProvider());
+ mReceiver = check != null ? check.getTracker("receiver") : null;
+ mService = check != null ? check.getTracker("service") : null;
+ mComponent = check != null ? check.getTracker("component") : null;
}
public void setMockPackageManager(PackageManager mock) {
@@ -86,19 +109,15 @@
}
public void addMockSystemService(String name, Object service) {
- mMockSystemServices = lazyInit(mMockSystemServices);
+ if (mMockSystemServices == null) mMockSystemServices = new ArrayMap<>();
mMockSystemServices.put(name, service);
}
public void addMockService(ComponentName component, IBinder service) {
- mMockServices = lazyInit(mMockServices);
+ if (mMockServices == null) mMockServices = new ArrayMap<>();
mMockServices.put(component, service);
}
- private <T, V> ArrayMap<T, V> lazyInit(ArrayMap<T, V> services) {
- return services != null ? services : new ArrayMap<T, V>();
- }
-
@Override
public Object getSystemService(String name) {
if (mMockSystemServices != null && mMockSystemServices.containsKey(name)) {
@@ -110,13 +129,13 @@
return super.getSystemService(name);
}
- public FakeSettingsProvider getSettingsProvider() {
+ public TestableSettings getSettingsProvider() {
return mSettingsProvider;
}
@Override
- public FakeContentResolver getContentResolver() {
- return mFakeContentResolver;
+ public TestableContentResolver getContentResolver() {
+ return mTestableContentResolver;
}
@Override
@@ -177,7 +196,7 @@
private boolean checkMocks(ComponentName component, ServiceConnection conn) {
if (mMockServices != null && component != null && mMockServices.containsKey(component)) {
- mActiveServices = lazyInit(mActiveServices);
+ if (mActiveServices == null) mActiveServices = new ArrayMap<>();
mActiveServices.put(conn, component);
conn.onServiceConnected(component, mMockServices.get(component));
return true;
@@ -212,13 +231,18 @@
super.unregisterComponentCallbacks(callback);
}
- @SuppressWarnings("unchecked")
- public <T> T getComponent(Class<T> interfaceType) {
- return (T) (mComponents != null ? mComponents.get(interfaceType) : null);
- }
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new TestWatcher() {
+ @Override
+ protected void succeeded(Description description) {
+ mSettingsProvider.clearOverrides();
+ }
- public <T, C extends T> void putComponent(Class<T> interfaceType, C component) {
- mComponents = lazyInit(mComponents);
- mComponents.put(interfaceType, component);
+ @Override
+ protected void failed(Throwable e, Description description) {
+ mSettingsProvider.clearOverrides();
+ }
+ }.apply(base, description);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableImageView.java b/tests/testables/src/android/testing/TestableImageView.java
similarity index 92%
rename from packages/SystemUI/tests/src/com/android/systemui/utils/TestableImageView.java
rename to tests/testables/src/android/testing/TestableImageView.java
index b131460..901e25b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableImageView.java
+++ b/tests/testables/src/android/testing/TestableImageView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 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
@@ -12,7 +12,7 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.utils;
+package android.testing;
import android.annotation.DrawableRes;
import android.annotation.Nullable;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/utils/TestableLooper.java
rename to tests/testables/src/android/testing/TestableLooper.java
index 8902e0c..8a33cf9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -12,7 +12,7 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.utils;
+package android.testing;
import android.os.Handler;
import android.os.Looper;
diff --git a/tests/testables/src/android/testing/TestableSettings.java b/tests/testables/src/android/testing/TestableSettings.java
new file mode 100644
index 0000000..d19f1ef
--- /dev/null
+++ b/tests/testables/src/android/testing/TestableSettings.java
@@ -0,0 +1,318 @@
+/*
+ * 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.testing;
+
+import android.content.ContentProvider;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
+import android.test.mock.MockContentProvider;
+import android.testing.TestableSettings.SettingOverrider.Builder;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Allows calls to android.provider.Settings to be tested easier. A SettingOverride
+ * can be acquired and a set of specific settings can be set to a value (and not changed
+ * in the system when set), so that they can be tested without breaking the test device.
+ * <p>
+ * To use, in the before method acquire the override add all settings that will affect if
+ * your test passes or not.
+ *
+ * <pre class="prettyprint">
+ * {@literal
+ * mSettingOverride = mTestableContext.getSettingsProvider().acquireOverridesBuilder()
+ * .addSetting("secure", Secure.USER_SETUP_COMPLETE, "0")
+ * .build();
+ * }
+ * </pre>
+ *
+ * Then in the after free up the settings.
+ *
+ * <pre class="prettyprint">
+ * {@literal
+ * mSettingOverride.release();
+ * }
+ * </pre>
+ */
+public class TestableSettings {
+
+ private static final String TAG = "TestableSettings";
+ private static final boolean DEBUG = false;
+
+ // Number of times to try to acquire a setting if in use.
+ private static final int MAX_TRIES = 10;
+ // Time to wait for each setting. WAIT_TIMEOUT * MAX_TRIES will be the maximum wait time
+ // for a setting.
+ private static final long WAIT_TIMEOUT = 1000;
+
+ private static TestableSettingsProvider sInstance;
+
+ private final TestableSettingsProvider mProvider;
+
+ private TestableSettings(TestableSettingsProvider provider) {
+ mProvider = provider;
+ }
+
+ public Builder acquireOverridesBuilder() {
+ return new Builder(this);
+ }
+
+ public void clearOverrides() {
+ List<SettingOverrider> overrides = mProvider.mOwners.remove(this);
+ if (overrides != null) {
+ overrides.forEach(override -> override.ensureReleased());
+ }
+ }
+
+ private void acquireSettings(SettingOverrider overridder, Set<String> keys)
+ throws AcquireTimeoutException {
+ mProvider.acquireSettings(overridder, keys, this);
+ }
+
+ ContentProvider getProvider() {
+ return mProvider;
+ }
+
+ @VisibleForTesting
+ Object getLock() {
+ return mProvider.mOverrideMap;
+ }
+
+ public static class SettingOverrider {
+ private final Set<String> mValidKeys;
+ private final Map<String, String> mValueMap = new ArrayMap<>();
+ private final TestableSettings mSettings;
+ private boolean mReleased;
+ public Throwable mObtain;
+
+ private SettingOverrider(Set<String> keys, TestableSettings provider) {
+ mValidKeys = new ArraySet<>(keys);
+ mSettings = provider;
+ }
+
+ private void ensureReleased() {
+ if (!mReleased) {
+ release();
+ }
+ }
+
+ public void release() {
+ mSettings.mProvider.releaseSettings(mValidKeys);
+ mReleased = true;
+ }
+
+ private void putDirect(String key, String value) {
+ mValueMap.put(key, value);
+ }
+
+ public void put(String table, String key, String value) {
+ if (!mValidKeys.contains(key(table, key))) {
+ throw new IllegalArgumentException("Key " + table + " " + key
+ + " not acquired for this overrider");
+ }
+ mValueMap.put(key(table, key), value);
+ }
+
+ public void remove(String table, String key) {
+ if (!mValidKeys.contains(key(table, key))) {
+ throw new IllegalArgumentException("Key " + table + " " + key
+ + " not acquired for this overrider");
+ }
+ mValueMap.remove(key(table, key));
+ }
+
+ public String get(String table, String key) {
+ if (!mValidKeys.contains(key(table, key))) {
+ throw new IllegalArgumentException("Key " + table + " " + key
+ + " not acquired for this overrider");
+ }
+ Log.d(TAG, "Get " + table + " " + key + " " + mValueMap.get(key(table, key)));
+ return mValueMap.get(key(table, key));
+ }
+
+ public static class Builder {
+ private final TestableSettings mProvider;
+ private Set<String> mKeys = new ArraySet<>();
+ private Map<String, String> mValues = new ArrayMap<>();
+
+ private Builder(TestableSettings provider) {
+ mProvider = provider;
+ }
+
+ public Builder addSetting(String table, String key) {
+ mKeys.add(key(table, key));
+ return this;
+ }
+
+ public Builder addSetting(String table, String key, String value) {
+ addSetting(table, key);
+ mValues.put(key(table, key), value);
+ return this;
+ }
+
+ public SettingOverrider build() throws AcquireTimeoutException {
+ SettingOverrider overrider = new SettingOverrider(mKeys, mProvider);
+ mProvider.acquireSettings(overrider, mKeys);
+ mValues.forEach((key, value) -> overrider.putDirect(key, value));
+ return overrider;
+ }
+ }
+ }
+
+ private static class TestableSettingsProvider extends MockContentProvider {
+
+ private final Map<String, SettingOverrider> mOverrideMap = new ArrayMap<>();
+ private final Map<Object, List<SettingOverrider>> mOwners = new ArrayMap<>();
+
+ private final ContentProviderClient mSettings;
+ private final ContentResolver mResolver;
+
+ public TestableSettingsProvider(ContentProviderClient settings, ContentResolver resolver) {
+ mSettings = settings;
+ mResolver = resolver;
+ }
+
+ private void releaseSettings(Set<String> keys) {
+ synchronized (mOverrideMap) {
+ for (String key : keys) {
+ if (DEBUG) Log.d(TAG, "Releasing " + key);
+ mOverrideMap.remove(key);
+ }
+ if (DEBUG) Log.d(TAG, "Notifying");
+ mOverrideMap.notify();
+ }
+ }
+
+ private boolean checkKeysLocked(Set<String> keys, boolean shouldThrow)
+ throws AcquireTimeoutException {
+ for (String key : keys) {
+ if (mOverrideMap.containsKey(key)) {
+ if (shouldThrow) {
+ if (DEBUG) Log.e(TAG, "Lock obtained at",
+ mOverrideMap.get(key).mObtain);
+ throw new AcquireTimeoutException("Could not acquire " + key,
+ mOverrideMap.get(key).mObtain);
+ }
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void acquireSettings(SettingOverrider overridder, Set<String> keys,
+ Object owner) throws AcquireTimeoutException {
+ synchronized (mOwners) {
+ List<SettingOverrider> list = mOwners.get(owner);
+ if (list == null) {
+ list = new ArrayList<>();
+ mOwners.put(owner, list);
+ }
+ list.add(overridder);
+ }
+ synchronized (mOverrideMap) {
+ for (int i = 0; i < MAX_TRIES; i++) {
+ if (checkKeysLocked(keys, false)) break;
+ try {
+ if (DEBUG) Log.d(TAG, "Waiting for contention to finish");
+ mOverrideMap.wait(WAIT_TIMEOUT);
+ } catch (InterruptedException e) {
+ }
+ }
+ overridder.mObtain = new Throwable();
+ checkKeysLocked(keys, true);
+ for (String key : keys) {
+ if (DEBUG) Log.d(TAG, "Acquiring " + key);
+ mOverrideMap.put(key, overridder);
+ }
+ }
+ }
+
+ public Bundle call(String method, String arg, Bundle extras) {
+ // Methods are "GET_system", "GET_global", "PUT_secure", etc.
+ final String[] commands = method.split("_", 2);
+ final String op = commands[0];
+ final String table = commands[1];
+
+ synchronized (mOverrideMap) {
+ SettingOverrider overrider = mOverrideMap.get(key(table, arg));
+ if (overrider == null) {
+ // Fall through to real settings.
+ try {
+ if (DEBUG) Log.d(TAG, "Falling through to real settings " + method);
+ // TODO: Add our own version of caching to handle this.
+ Bundle call = mSettings.call(method, arg, extras);
+ call.remove(Settings.CALL_METHOD_TRACK_GENERATION_KEY);
+ return call;
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ String value;
+ Bundle out = new Bundle();
+ switch (op) {
+ case "GET":
+ value = overrider.get(table, arg);
+ if (value != null) {
+ out.putString(Settings.NameValueTable.VALUE, value);
+ }
+ break;
+ case "PUT":
+ value = extras.getString(Settings.NameValueTable.VALUE, null);
+ if (value != null) {
+ overrider.put(table, arg, value);
+ } else {
+ overrider.remove(table, arg);
+ }
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown command " + method);
+ }
+ return out;
+ }
+ }
+ }
+
+ public static class AcquireTimeoutException extends Exception {
+ public AcquireTimeoutException(String str, Throwable cause) {
+ super(str, cause);
+ }
+ }
+
+ private static String key(String table, String key) {
+ return table + "_" + key;
+ }
+
+ /**
+ * Since the settings provider is cached inside android.provider.Settings, this must
+ * be gotten statically to ensure there is only one instance referenced.
+ */
+ public static TestableSettings getFakeSettingsProvider(ContentProviderClient settings,
+ ContentResolver resolver) {
+ if (sInstance == null) {
+ sInstance = new TestableSettingsProvider(settings, resolver);
+ }
+ return new TestableSettings(sInstance);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/UiThreadTest.java b/tests/testables/src/android/testing/UiThreadTest.java
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/UiThreadTest.java
rename to tests/testables/src/android/testing/UiThreadTest.java
index 58369b1..e40e1d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/UiThreadTest.java
+++ b/tests/testables/src/android/testing/UiThreadTest.java
@@ -12,7 +12,7 @@
* permissions and limitations under the License.
*/
-package com.android.systemui;
+package android.testing;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/ViewUtils.java b/tests/testables/src/android/testing/ViewUtils.java
similarity index 90%
rename from packages/SystemUI/tests/src/com/android/systemui/utils/ViewUtils.java
rename to tests/testables/src/android/testing/ViewUtils.java
index 678b9f4..5a651aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/ViewUtils.java
+++ b/tests/testables/src/android/testing/ViewUtils.java
@@ -12,19 +12,14 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.utils;
+package android.testing;
import android.content.pm.ApplicationInfo;
import android.graphics.PixelFormat;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.Log;
+import android.support.test.InstrumentationRegistry;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
-import android.support.test.InstrumentationRegistry;
-
-import com.android.systemui.SysuiTestCase;
public class ViewUtils {
diff --git a/tests/testables/tests/Android.mk b/tests/testables/tests/Android.mk
new file mode 100644
index 0000000..752d536
--- /dev/null
+++ b/tests/testables/tests/Android.mk
@@ -0,0 +1,39 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_USE_AAPT2 := true
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_PACKAGE_NAME := TestablesTest
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+ $(call all-Iaidl-files-under, src)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ mockito-updated-target-minus-junit4 \
+ legacy-android-test \
+ testables
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
diff --git a/tests/testables/tests/AndroidManifest.xml b/tests/testables/tests/AndroidManifest.xml
new file mode 100644
index 0000000..f6006b0
--- /dev/null
+++ b/tests/testables/tests/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.testables">
+
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.testables"
+ android:label="Tests for Testables">
+ </instrumentation>
+</manifest>
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableLooperTest.java b/tests/testables/tests/src/android/testing/TestableLooperTest.java
similarity index 93%
rename from packages/SystemUI/tests/src/com/android/systemui/utils/TestableLooperTest.java
rename to tests/testables/tests/src/android/testing/TestableLooperTest.java
index 2416e1d..18e5fff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableLooperTest.java
+++ b/tests/testables/tests/src/android/testing/TestableLooperTest.java
@@ -12,7 +12,7 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.utils;
+package android.testing;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
@@ -27,20 +27,17 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-
-import com.android.systemui.SysUIRunner;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.utils.TestableLooper.MessageHandler;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
+import android.testing.TestableLooper.MessageHandler;
+import android.testing.TestableLooper.RunWithLooper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
@RunWithLooper
-public class TestableLooperTest extends SysuiTestCase {
+public class TestableLooperTest {
private TestableLooper mTestableLooper;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProviderTest.java b/tests/testables/tests/src/android/testing/TestableSettingsTest.java
similarity index 88%
rename from packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProviderTest.java
rename to tests/testables/tests/src/android/testing/TestableSettingsTest.java
index 63bb5e7..1b01542 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProviderTest.java
+++ b/tests/testables/tests/src/android/testing/TestableSettingsTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 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
@@ -12,7 +12,7 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.utils;
+package android.testing;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -25,29 +25,32 @@
import android.provider.Settings;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
+import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
+import android.testing.TestableSettings.AcquireTimeoutException;
+import android.testing.TestableSettings.SettingOverrider;
import android.util.Log;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.utils.FakeSettingsProvider.AcquireTimeoutException;
-import com.android.systemui.utils.FakeSettingsProvider.SettingOverrider;
-
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
-public class FakeSettingsProviderTest extends SysuiTestCase {
+public class TestableSettingsTest {
public static final String NONEXISTENT_SETTING = "nonexistent_setting";
- private static final String TAG = "FakeSettingsProviderTest";
+ private static final String TAG = "TestableSettingsTest";
private SettingOverrider mOverrider;
private ContentResolver mContentResolver;
+ @Rule
+ public final TestableContext mContext =
+ new TestableContext(InstrumentationRegistry.getContext());
@Before
public void setup() throws AcquireTimeoutException {
- mOverrider = mContext.getSettingsProvider().acquireOverridesBuilder(this)
+ mOverrider = mContext.getSettingsProvider().acquireOverridesBuilder()
.addSetting("secure", NONEXISTENT_SETTING)
.addSetting("global", NONEXISTENT_SETTING, "initial value")
.addSetting("global", Global.DEVICE_PROVISIONED)
@@ -55,13 +58,6 @@
mContentResolver = mContext.getContentResolver();
}
- @After
- public void teardown() {
- if (mOverrider != null) {
- mOverrider.release();
- }
- }
-
@Test
public void testInitialValueSecure() {
String value = Secure.getString(mContentResolver, NONEXISTENT_SETTING);
@@ -109,8 +105,9 @@
@Test
public void testAutoRelease() throws Exception {
- super.cleanup();
- mContext.getSettingsProvider().acquireOverridesBuilder(this)
+ mOverrider.release();
+ mOverrider = null;
+ mContext.getSettingsProvider().acquireOverridesBuilder()
.addSetting("global", Global.DEVICE_PROVISIONED)
.build();
}
@@ -122,7 +119,7 @@
String secure = "secure";
String key = "something shared";
String[] result = new String[1];
- overriders[0] = mContext.getSettingsProvider().acquireOverridesBuilder(this)
+ overriders[0] = mContext.getSettingsProvider().acquireOverridesBuilder()
.addSetting(secure, key, "Some craziness")
.build();
synchronized (lock) {
@@ -137,7 +134,7 @@
lock.notify();
}
overriders[1] = mContext.getSettingsProvider()
- .acquireOverridesBuilder(FakeSettingsProviderTest.this)
+ .acquireOverridesBuilder()
.addSetting(secure, key, "default value")
.build();
// Ensure that the default is the one we set, and not left over from
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 253ea6f..1282349 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -62,7 +62,7 @@
// ---- implementation of IWindowManager that we care about ----
@Override
- public int getRotation() throws RemoteException {
+ public int getDefaultDisplayRotation() throws RemoteException {
return mRotation;
}
@@ -399,7 +399,7 @@
}
@Override
- public int watchRotation(IRotationWatcher arg0) throws RemoteException {
+ public int watchRotation(IRotationWatcher arg0, int arg1) throws RemoteException {
// TODO Auto-generated method stub
return 0;
}
diff --git a/tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java b/tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java
index 3ce7cab..672ff6d 100644
--- a/tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java
+++ b/tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java
@@ -95,6 +95,9 @@
public void notifyServicesStateChanged() {
}
+
+ public void setRelevantEventTypes(int eventTypes) {
+ }
};
/**
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 3276628..a385847 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -1607,6 +1607,12 @@
}
@Override
+ public Intent registerReceiver(BroadcastReceiver arg0, IntentFilter arg1, boolean arg2) {
+ // pass
+ return null;
+ }
+
+ @Override
public Intent registerReceiver(BroadcastReceiver arg0, IntentFilter arg1,
String arg2, Handler arg3) {
// pass
@@ -1614,6 +1620,13 @@
}
@Override
+ public Intent registerReceiver(BroadcastReceiver arg0, IntentFilter arg1,
+ String arg2, Handler arg3, boolean arg4) {
+ // pass
+ return null;
+ }
+
+ @Override
public Intent registerReceiverAsUser(BroadcastReceiver arg0, UserHandle arg0p5,
IntentFilter arg1, String arg2, Handler arg3) {
// pass
diff --git a/wifi/java/android/net/wifi/IconInfo.aidl b/wifi/java/android/net/wifi/IconInfo.aidl
new file mode 100644
index 0000000..a7bb2ef
--- /dev/null
+++ b/wifi/java/android/net/wifi/IconInfo.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;
+
+parcelable IconInfo;
diff --git a/wifi/java/android/net/wifi/IconInfo.java b/wifi/java/android/net/wifi/IconInfo.java
new file mode 100644
index 0000000..0eae363
--- /dev/null
+++ b/wifi/java/android/net/wifi/IconInfo.java
@@ -0,0 +1,111 @@
+/*
+ * 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;
+
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.os.Parcel;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * A class representing icon information.
+ */
+public final class IconInfo implements Parcelable {
+ /**
+ * Name of the icon file.
+ */
+ private final String mFilename;
+
+ /**
+ * Raw binary data of the icon.
+ */
+ private final byte[] mData;
+
+ public IconInfo(String filename, byte[] data) {
+ mFilename = filename;
+ mData = data;
+ }
+
+ public IconInfo(IconInfo source) {
+ if (source == null) {
+ mFilename = null;
+ mData = null;
+ return;
+ }
+
+ mFilename = source.mFilename;
+ if (source.mData != null) {
+ mData = Arrays.copyOf(source.mData, source.mData.length);
+ } else {
+ mData = null;
+ }
+ }
+
+ public String getFilename() {
+ return mFilename;
+ }
+
+ public byte[] getData() {
+ return mData;
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof IconInfo)) {
+ return false;
+ }
+ IconInfo that = (IconInfo) thatObject;
+ return TextUtils.equals(mFilename, that.mFilename)
+ && Arrays.equals(mData, that.mData);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mFilename, mData);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mFilename);
+ dest.writeByteArray(mData);
+ }
+
+ public static final Creator<IconInfo> CREATOR =
+ new Creator<IconInfo>() {
+ @Override
+ public IconInfo createFromParcel(Parcel in) {
+ String filename = in.readString();
+ byte[] data = in.createByteArray();
+ return new IconInfo(filename, data);
+ }
+
+ @Override
+ public IconInfo[] newArray(int size) {
+ return new IconInfo[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index ed6a166..447cafb 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -114,166 +114,118 @@
public static final int WIFI_CREDENTIAL_FORGOT = 1;
/**
- * Broadcast intent action indicating that the a Passpoint release 2 icon has been received.
- * @hide
- */
- public static final String PASSPOINT_ICON_RECEIVED_ACTION =
- "android.net.wifi.PASSPOINT_ICON_RECEIVED";
- /** @hide */
- public static final String EXTRA_PASSPOINT_ICON_FILE = "file";
-
- /**
- * Broadcast intent action indicating that the a Passpoint release
- * 2 WNM frame has been received.
- * @hide
- */
- public static final String PASSPOINT_WNM_FRAME_RECEIVED_ACTION =
- "android.net.wifi.PASSPOINT_WNM_FRAME_RECEIVED";
- /**
- * Originating BSS
- * @hide */
- public static final String EXTRA_PASSPOINT_WNM_BSSID = "bssid";
- /**
- * SOAP-XML or OMA-DM
- * @hide */
- public static final String EXTRA_PASSPOINT_WNM_METHOD = "method";
- /**
- * Type of Passpoint match
- * @hide */
- public static final String EXTRA_PASSPOINT_WNM_PPOINT_MATCH = "match";
- /**
- * String
- * @hide */
- public static final String EXTRA_PASSPOINT_WNM_URL = "url";
- /**
- * Boolean true=ess, false=bss
- * @hide */
- public static final String EXTRA_PASSPOINT_WNM_ESS = "ess";
- /**
- * Delay in seconds
- * @hide */
- public static final String EXTRA_PASSPOINT_WNM_DELAY = "delay";
-
- /**
* Broadcast intent action indicating that a Passpoint provider icon has been received.
*
+ * Included extras:
+ * {@link #EXTRA_BSSID_LONG}
+ * {@link #EXTRA_ICON_INFO}
+ *
* 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
+ * <p>Note: The broadcast is only delivered to registered receivers - no manifest registered
+ * components will be launched.
*/
- public static final String EXTRA_PASSPOINT_ICON_BSSID =
- "android.net.wifi.extra.PASSPOINT_ICON_BSSID";
+ public static final String ACTION_PASSPOINT_ICON = "android.net.wifi.action.PASSPOINT_ICON";
/**
- * Filename of the icon.
+ * BSSID of an AP in long representation. The {@link #EXTRA_BSSID} contains BSSID in
+ * String representation.
*
- * Type: String
+ * Retrieve with {@link android.content.Intent#getLongExtra(String, long)}.
*/
- public static final String EXTRA_PASSPOINT_ICON_FILENAME =
- "android.net.wifi.extra.PASSPOINT_ICON_FILENAME";
+ public static final String EXTRA_BSSID_LONG = "android.net.wifi.extra.BSSID_LONG";
/**
- * Binary blob of the icon.
+ * Icon information.
*
- * Type: byte[]
+ * Retrieve with {@link android.content.Intent#getParcelableExtra(String)} and cast into
+ * {@link IconInfo}.
*/
- public static final String EXTRA_PASSPOINT_ICON_DATA =
- "android.net.wifi.extra.PASSPOINT_ICON_DATA";
+ public static final String EXTRA_ICON_INFO = "android.net.wifi.extra.ICON_INFO";
/**
* Broadcast intent action indicating a Passpoint OSU Providers List element has been received.
*
+ * Included extras:
+ * {@link #EXTRA_BSSID_LONG}
+ * {@link #EXTRA_ANQP_ELEMENT_DATA}
+ *
* Receiver Required Permission: android.Manifest.permission.ACCESS_WIFI_STATE
+ *
+ * <p>Note: The broadcast is only delivered to registered receivers - no manifest registered
+ * components will be launched.
+ *
*/
public static final String ACTION_PASSPOINT_OSU_PROVIDERS_LIST =
"android.net.wifi.action.PASSPOINT_OSU_PROVIDERS_LIST";
/**
- * BSSID of the sender.
+ * Raw binary data of an ANQP (Access Network Query Protocol) element.
*
- * Type: long
+ * Retrieve with {@link android.content.Intent#getByteArrayExtra(String)}.
*/
- 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";
+ public static final String EXTRA_ANQP_ELEMENT_DATA =
+ "android.net.wifi.extra.ANQP_ELEMENT_DATA";
/**
* Broadcast intent action indicating that a Passpoint Deauth Imminent frame has been received.
*
+ * Included extras:
+ * {@link #EXTRA_BSSID_LONG}
+ * {@link #EXTRA_ESS}
+ * {@link #EXTRA_DELAY}
+ * {@link #EXTRA_URL}
+ *
* Receiver Required Permission: android.Manifest.permission.ACCESS_WIFI_STATE
+ *
+ * <p>Note: The broadcast is only delivered to registered receivers - no manifest registered
+ * components will be launched.
+ *
*/
public static final String ACTION_PASSPOINT_DEAUTH_IMMINENT =
"android.net.wifi.action.PASSPOINT_DEAUTH_IMMINENT";
/**
- * The BSSID of the sender.
+ * Flag indicating BSS (Basic Service Set) or ESS (Extended Service Set). This will be set to
+ * {@code true} for ESS.
*
- * Type: long
+ * Retrieve with {@link android.content.Intent#getBooleanExtra(String, boolean)}.
*/
- public static final String EXTRA_PASSPOINT_DEAUTH_IMMINENT_BSSID =
- "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_BSSID";
+ public static final String EXTRA_ESS = "android.net.wifi.extra.ESS";
/**
- * Flag indicating failure at BSS (Basic Service Set) or ESS (Extended Service Set) level.
+ * Delay in seconds.
*
- * Type: boolean
+ * Retrieve with {@link android.content.Intent#getIntExtra(String, int)}.
*/
- public static final String EXTRA_PASSPOINT_DEAUTH_IMMINENT_ESS =
- "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_ESS";
+ public static final String EXTRA_DELAY = "android.net.wifi.extra.DELAY";
/**
- * 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}.
+ * String representation of an URL.
*
- * Type: int
+ * Retrieve with {@link android.content.Intent#getStringExtra(String)}.
*/
- 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";
+ public static final String EXTRA_URL = "android.net.wifi.extra.URL";
/**
* Broadcast intent action indicating a Passpoint subscription remediation frame has been
* received.
*
+ * Included extras:
+ * {@link #EXTRA_BSSID_LONG}
+ * {@link #EXTRA_SUBSCRIPTION_REMEDIATION_METHOD}
+ * {@link #EXTRA_URL}
+ *
* Receiver Required Permission: android.Manifest.permission.ACCESS_WIFI_STATE
+ *
+ ** <p>Note: The broadcast is only delivered to registered receivers - no manifest registered
+ * components will be launched.
*/
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
+ * Retrieve with {@link android.content.Intent#getIntExtra(String, 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";
+ public static final String EXTRA_SUBSCRIPTION_REMEDIATION_METHOD =
+ "android.net.wifi.extra.SUBSCRIPTION_REMEDIATION_METHOD";
/**
* Broadcast intent action indicating that Wi-Fi has been enabled, disabled,
@@ -972,12 +924,15 @@
* Name). In the case when there is an existing configuration with the same
* FQDN, the new configuration will replace the existing configuration.
*
+ * An {@link IllegalArgumentException} will be thrown on failure.
+ *
* @param config The Passpoint configuration to be added
- * @return true on success
*/
- public boolean addOrUpdatePasspointConfiguration(PasspointConfiguration config) {
+ public void addOrUpdatePasspointConfiguration(PasspointConfiguration config) {
try {
- return mService.addOrUpdatePasspointConfiguration(config);
+ if (!mService.addOrUpdatePasspointConfiguration(config)) {
+ throw new IllegalArgumentException();
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -986,12 +941,15 @@
/**
* Remove the Passpoint configuration identified by its FQDN (Fully Qualified Domain Name).
*
+ * An {@link IllegalArgumentException} will be thrown on failure.
+ *
* @param fqdn The FQDN of the passpoint configuration to be removed
- * @return true on success
*/
- public boolean removePasspointConfiguration(String fqdn) {
+ public void removePasspointConfiguration(String fqdn) {
try {
- return mService.removePasspointConfiguration(fqdn);
+ if (!mService.removePasspointConfiguration(fqdn)) {
+ throw new IllegalArgumentException();
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1013,10 +971,13 @@
}
/**
- * Query for a Hotspot 2.0 release 2 OSU icon file.
+ * Query for a Hotspot 2.0 release 2 OSU icon file. An {@link #ACTION_PASSPOINT_ICON} intent
+ * will be broadcasted once the request is completed. The return value of
+ * {@link IconInfo#getData} from the intent extra will indicate the result of the request.
+ * A value of {@code null} will indicate a failure.
*
* @param bssid The BSSID of the AP
- * @param fileName File name of the icon to query
+ * @param fileName Name of the icon file (remote file) to query from the AP
*/
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 1f661c4..7de55aa 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -333,6 +333,7 @@
* Validate the configuration data.
*
* @return true on success or false on failure
+ * @hide
*/
public boolean validate() {
if (mHomeSp == null || !mHomeSp.validate()) {
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
index 2388841..d8da84f 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
@@ -286,6 +286,7 @@
* Validate the configuration data.
*
* @return true on success or false on failure
+ * @hide
*/
public boolean validate() {
if (TextUtils.isEmpty(mUsername)) {
@@ -443,6 +444,7 @@
* Validate the configuration data.
*
* @return true on success or false on failure
+ * @hide
*/
public boolean validate() {
if (!TextUtils.equals(CERT_TYPE_X509V3, mCertType)) {
@@ -569,6 +571,7 @@
* Validate the configuration data.
*
* @return true on success or false on failure
+ * @hide
*/
public boolean validate() {
// Note: this only validate the format of IMSI string itself. Additional verification
@@ -768,6 +771,7 @@
* Validate the configuration data.
*
* @return true on success or false on failure
+ * @hide
*/
public boolean validate() {
if (TextUtils.isEmpty(mRealm)) {
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
index 8ec40c0..68bdf37 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
@@ -245,6 +245,7 @@
* Validate HomeSp data.
*
* @return true on success or false on failure
+ * @hide
*/
public boolean validate() {
if (TextUtils.isEmpty(mFqdn)) {
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Policy.java b/wifi/java/android/net/wifi/hotspot2/pps/Policy.java
index 63238e8..da36a11 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/Policy.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Policy.java
@@ -253,6 +253,7 @@
* Validate RoamingParnter data.
*
* @return true on success
+ * @hide
*/
public boolean validate() {
if (TextUtils.isEmpty(mFqdn)) {
@@ -393,6 +394,7 @@
* Validate Policy data.
*
* @return true on success
+ * @hide
*/
public boolean validate() {
if (mPolicyUpdate == null) {
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.java b/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.java
index 70264b0e..ae051b0 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.java
@@ -251,6 +251,7 @@
* Validate UpdateParameter data.
*
* @return true on success
+ * @hide
*/
public boolean validate() {
if (mUpdateIntervalInMinutes == Long.MIN_VALUE) {
diff --git a/wifi/tests/src/android/net/wifi/IconInfoTest.java b/wifi/tests/src/android/net/wifi/IconInfoTest.java
new file mode 100644
index 0000000..2fdb484
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/IconInfoTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.wifi.IconInfo;
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.IconInfo}.
+ */
+@SmallTest
+public class IconInfoTest {
+ private static final String TEST_FILENAME = "testIcon";
+ private static final byte[] TEST_DATA = new byte[] {0x12, 0x23, 0x34, 0x45, 0x56, 0x67};
+
+ /**
+ * Verify parcel write and read consistency for the given {@link IconInfo}
+ *
+ * @param writeIcon the {@link IconInfo} to write and verify
+ * @throws Exception
+ */
+ private static void verifyParcel(IconInfo writeIcon) throws Exception {
+ Parcel parcel = Parcel.obtain();
+ writeIcon.writeToParcel(parcel, 0);
+
+ parcel.setDataPosition(0); // Rewind data position back to the beginning for read.
+ IconInfo readIcon = IconInfo.CREATOR.createFromParcel(parcel);
+ assertEquals(writeIcon, readIcon);
+ }
+
+ /**
+ * Verify parcel serialization for a {@link IconInfo} with null data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyParcelWithNullData() throws Exception {
+ verifyParcel(new IconInfo(TEST_FILENAME, (byte[]) null));
+ }
+
+ /**
+ * Verify parcel serialization for a {@link IconInfo} with zero length data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyParcelWithZeroLengthData() throws Exception {
+ verifyParcel(new IconInfo(TEST_FILENAME, new byte[0]));
+ }
+
+ /**
+ * Verify parcel serialization for a {@link IconInfo} with non-zero length data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyParcelWithNonZeroLengthData() throws Exception {
+ verifyParcel(new IconInfo(TEST_FILENAME, TEST_DATA));
+ }
+
+ /**
+ * Verify parcel serialization for a {@link IconInfo} with a null filename.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyParcelWithNullFilename() throws Exception {
+ verifyParcel(new IconInfo(null, TEST_DATA));
+ }
+
+ /**
+ * Verify the copy constructor with non-null filename and data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyCopyConstructor() throws Exception {
+ IconInfo source = new IconInfo(TEST_FILENAME, TEST_DATA);
+ assertEquals(source, new IconInfo(source));
+ }
+
+ /**
+ * Verify the copy constructor with null data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyCopyConstructorWithNullData() throws Exception {
+ IconInfo source = new IconInfo(TEST_FILENAME, (byte[]) null);
+ assertEquals(source, new IconInfo(source));
+ }
+
+ /**
+ * Verify the copy constructor with null file name.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyCopyConstructorWithNullFilename() throws Exception {
+ IconInfo source = new IconInfo(null, TEST_DATA);
+ assertEquals(source, new IconInfo(source));
+ }
+}