diff options
110 files changed, 4547 insertions, 2313 deletions
diff --git a/api/current.txt b/api/current.txt index 457a9e8..2058ac9 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4016,79 +4016,6 @@ package android.app { field public java.lang.String serviceDetails; } - public deprecated class AssistContent { - ctor public AssistContent(); - method public android.content.ClipData getClipData(); - method public android.os.Bundle getExtras(); - method public java.lang.String getStructuredData(); - method public android.net.Uri getWebUri(); - method public boolean isAppProvidedIntent(); - method public void setClipData(android.content.ClipData); - method public void setIntent(android.content.Intent); - method public void setStructuredData(java.lang.String); - method public void setWebUri(android.net.Uri); - } - - public deprecated class AssistStructure { - ctor public AssistStructure(); - method public android.content.ComponentName getActivityComponent(); - method public int getWindowNodeCount(); - } - - public static class AssistStructure.ViewNode { - method public android.app.AssistStructure.ViewNode getChildAt(int); - method public int getChildCount(); - method public java.lang.String getClassName(); - method public java.lang.CharSequence getContentDescription(); - method public android.os.Bundle getExtras(); - method public int getHeight(); - method public java.lang.String getHint(); - method public int getId(); - method public java.lang.String getIdEntry(); - method public java.lang.String getIdPackage(); - method public java.lang.String getIdType(); - method public int getLeft(); - method public int getScrollX(); - method public int getScrollY(); - method public java.lang.CharSequence getText(); - method public int getTextBackgroundColor(); - method public int getTextColor(); - method public int getTextSelectionEnd(); - method public int getTextSelectionStart(); - method public float getTextSize(); - method public int getTextStyle(); - method public int getTop(); - method public int getVisibility(); - method public int getWidth(); - method public boolean isAccessibilityFocused(); - method public boolean isActivated(); - method public boolean isAssistBlocked(); - method public boolean isCheckable(); - method public boolean isChecked(); - method public boolean isClickable(); - method public boolean isContextClickable(); - method public boolean isEnabled(); - method public boolean isFocusable(); - method public boolean isFocused(); - method public boolean isLongClickable(); - method public boolean isSelected(); - field public static final int TEXT_COLOR_UNDEFINED = 1; // 0x1 - field public static final int TEXT_STYLE_BOLD = 1; // 0x1 - field public static final int TEXT_STYLE_ITALIC = 2; // 0x2 - field public static final int TEXT_STYLE_STRIKE_THRU = 8; // 0x8 - field public static final int TEXT_STYLE_UNDERLINE = 4; // 0x4 - } - - public static class AssistStructure.WindowNode { - method public int getDisplayId(); - method public int getHeight(); - method public int getLeft(); - method public android.app.AssistStructure.ViewNode getRootViewNode(); - method public java.lang.CharSequence getTitle(); - method public int getTop(); - method public int getWidth(); - } - public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener { ctor public DatePickerDialog(android.content.Context, android.app.DatePickerDialog.OnDateSetListener, int, int, int); ctor public DatePickerDialog(android.content.Context, int, android.app.DatePickerDialog.OnDateSetListener, int, int, int); @@ -5900,22 +5827,87 @@ package android.app.admin { package android.app.assist { - public final class AssistContent extends android.app.AssistContent implements android.os.Parcelable { - ctor public AssistContent(android.os.Parcel); + public deprecated class AssistContent implements android.os.Parcelable { + ctor public AssistContent(); method public int describeContents(); + method public android.content.ClipData getClipData(); + method public android.os.Bundle getExtras(); method public android.content.Intent getIntent(); + method public java.lang.String getStructuredData(); + method public android.net.Uri getWebUri(); + method public boolean isAppProvidedIntent(); + method public void setClipData(android.content.ClipData); + method public void setIntent(android.content.Intent); + method public void setStructuredData(java.lang.String); + method public void setWebUri(android.net.Uri); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.app.assist.AssistContent> CREATOR; } - public final class AssistStructure extends android.app.AssistStructure implements android.os.Parcelable { + public class AssistStructure implements android.os.Parcelable { ctor public AssistStructure(); method public int describeContents(); - method public android.app.AssistStructure.WindowNode getWindowNodeAt(int); + method public android.content.ComponentName getActivityComponent(); + method public android.app.assist.AssistStructure.WindowNode getWindowNodeAt(int); + method public int getWindowNodeCount(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.app.assist.AssistStructure> CREATOR; } + public static class AssistStructure.ViewNode { + method public android.app.assist.AssistStructure.ViewNode getChildAt(int); + method public int getChildCount(); + method public java.lang.String getClassName(); + method public java.lang.CharSequence getContentDescription(); + method public android.os.Bundle getExtras(); + method public int getHeight(); + method public java.lang.String getHint(); + method public int getId(); + method public java.lang.String getIdEntry(); + method public java.lang.String getIdPackage(); + method public java.lang.String getIdType(); + method public int getLeft(); + method public int getScrollX(); + method public int getScrollY(); + method public java.lang.CharSequence getText(); + method public int getTextBackgroundColor(); + method public int getTextColor(); + method public int getTextSelectionEnd(); + method public int getTextSelectionStart(); + method public float getTextSize(); + method public int getTextStyle(); + method public int getTop(); + method public int getVisibility(); + method public int getWidth(); + method public boolean isAccessibilityFocused(); + method public boolean isActivated(); + method public boolean isAssistBlocked(); + method public boolean isCheckable(); + method public boolean isChecked(); + method public boolean isClickable(); + method public boolean isContextClickable(); + method public boolean isEnabled(); + method public boolean isFocusable(); + method public boolean isFocused(); + method public boolean isLongClickable(); + method public boolean isSelected(); + field public static final int TEXT_COLOR_UNDEFINED = 1; // 0x1 + field public static final int TEXT_STYLE_BOLD = 1; // 0x1 + field public static final int TEXT_STYLE_ITALIC = 2; // 0x2 + field public static final int TEXT_STYLE_STRIKE_THRU = 8; // 0x8 + field public static final int TEXT_STYLE_UNDERLINE = 4; // 0x4 + } + + public static class AssistStructure.WindowNode { + method public int getDisplayId(); + method public int getHeight(); + method public int getLeft(); + method public android.app.assist.AssistStructure.ViewNode getRootViewNode(); + method public java.lang.CharSequence getTitle(); + method public int getTop(); + method public int getWidth(); + } + } package android.app.backup { @@ -7211,6 +7203,7 @@ package android.content { method public android.content.Context getContext(); method public final android.os.IBinder getSyncAdapterBinder(); method public abstract void onPerformSync(android.accounts.Account, android.os.Bundle, java.lang.String, android.content.ContentProviderClient, android.content.SyncResult); + method public void onSecurityException(android.accounts.Account, android.os.Bundle, java.lang.String, android.content.SyncResult); method public void onSyncCanceled(); method public void onSyncCanceled(java.lang.Thread); field public static final deprecated int LOG_SYNC_DETAILS = 2743; // 0xab7 @@ -13505,7 +13498,6 @@ package android.hardware.camera2 { field public static final int HOT_PIXEL_MODE_HIGH_QUALITY = 2; // 0x2 field public static final int HOT_PIXEL_MODE_OFF = 0; // 0x0 field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; // 0x1 - field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_HIGH_RESOLUTION = 3; // 0x3 field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; // 0x2 field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; // 0x0 field public static final int LENS_FACING_BACK = 1; // 0x1 @@ -13858,6 +13850,7 @@ package android.hardware.camera2.params { } public final class StreamConfigurationMap { + method public android.util.Size[] getHighResolutionOutputSizes(int); method public android.util.Range<java.lang.Integer>[] getHighSpeedVideoFpsRanges(); method public android.util.Range<java.lang.Integer>[] getHighSpeedVideoFpsRangesFor(android.util.Size); method public android.util.Size[] getHighSpeedVideoSizes(); @@ -18207,7 +18200,6 @@ package android.net { method public void onLinkPropertiesChanged(android.net.Network, android.net.LinkProperties); method public void onLosing(android.net.Network, int); method public void onLost(android.net.Network); - method public void onPreCheck(android.net.Network); } public static abstract interface ConnectivityManager.OnNetworkActiveListener { @@ -18358,6 +18350,7 @@ package android.net { method public boolean hasTransport(int); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.net.NetworkCapabilities> CREATOR; + field public static final int NET_CAPABILITY_CAPTIVE_PORTAL = 17; // 0x11 field public static final int NET_CAPABILITY_CBS = 5; // 0x5 field public static final int NET_CAPABILITY_DUN = 2; // 0x2 field public static final int NET_CAPABILITY_EIMS = 10; // 0xa @@ -18372,6 +18365,7 @@ package android.net { field public static final int NET_CAPABILITY_RCS = 8; // 0x8 field public static final int NET_CAPABILITY_SUPL = 1; // 0x1 field public static final int NET_CAPABILITY_TRUSTED = 14; // 0xe + field public static final int NET_CAPABILITY_VALIDATED = 16; // 0x10 field public static final int NET_CAPABILITY_WIFI_P2P = 6; // 0x6 field public static final int NET_CAPABILITY_XCAP = 9; // 0x9 field public static final int TRANSPORT_BLUETOOTH = 2; // 0x2 @@ -19085,6 +19079,8 @@ package android.net.wifi { public class ScanResult implements android.os.Parcelable { method public int describeContents(); + method public boolean is80211mcResponder(); + method public boolean isPasspointNetwork(); method public void writeToParcel(android.os.Parcel, int); field public java.lang.String BSSID; field public static final int CHANNEL_WIDTH_160MHZ = 3; // 0x3 @@ -19092,18 +19088,19 @@ package android.net.wifi { field public static final int CHANNEL_WIDTH_40MHZ = 1; // 0x1 field public static final int CHANNEL_WIDTH_80MHZ = 2; // 0x2 field public static final int CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4; // 0x4 + field public static final long FLAG_80211mc_RESPONDER = 2L; // 0x2L + field public static final long FLAG_PASSPOINT_NETWORK = 1L; // 0x1L field public java.lang.String SSID; field public java.lang.String capabilities; field public int centerFreq0; field public int centerFreq1; field public int channelWidth; + field public long flags; field public int frequency; - field public boolean is80211McRTTResponder; field public int level; - field public java.lang.String operatorFriendlyName; - field public boolean passpointNetwork; + field public java.lang.CharSequence operatorFriendlyName; field public long timestamp; - field public java.lang.String venueName; + field public java.lang.CharSequence venueName; } public final class SupplicantState extends java.lang.Enum implements android.os.Parcelable { @@ -19146,7 +19143,7 @@ package android.net.wifi { field public java.lang.String preSharedKey; field public int priority; field public java.lang.String providerFriendlyName; - field public java.util.HashSet<java.lang.Long> roamingConsortiumIds; + field public java.lang.Long[] roamingConsortiumIds; field public int status; field public java.lang.String[] wepKeys; field public int wepTxKeyIndex; @@ -19208,7 +19205,7 @@ package android.net.wifi { method public java.lang.String getAnonymousIdentity(); method public java.security.cert.X509Certificate getCaCertificate(); method public java.security.cert.X509Certificate getClientCertificate(); - method public java.lang.String getDomainSubjectMatch(); + method public java.lang.String getDomainSuffixMatch(); method public int getEapMethod(); method public java.lang.String getIdentity(); method public java.lang.String getPassword(); @@ -26582,6 +26579,7 @@ package android.provider { field public static final java.lang.String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled"; field public static final java.lang.String USE_GOOGLE_MAIL = "use_google_mail"; field public static final java.lang.String WAIT_FOR_DEBUGGER = "wait_for_debugger"; + field public static final java.lang.String WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN = "wifi_device_owner_configs_lockdown"; field public static final java.lang.String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count"; field public static final java.lang.String WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS = "wifi_mobile_data_transition_wakelock_timeout_ms"; field public static final java.lang.String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = "wifi_networks_available_notification_on"; diff --git a/api/system-current.txt b/api/system-current.txt index 08715cd..df18ce4 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4112,79 +4112,6 @@ package android.app { field public java.lang.String serviceDetails; } - public deprecated class AssistContent { - ctor public AssistContent(); - method public android.content.ClipData getClipData(); - method public android.os.Bundle getExtras(); - method public java.lang.String getStructuredData(); - method public android.net.Uri getWebUri(); - method public boolean isAppProvidedIntent(); - method public void setClipData(android.content.ClipData); - method public void setIntent(android.content.Intent); - method public void setStructuredData(java.lang.String); - method public void setWebUri(android.net.Uri); - } - - public deprecated class AssistStructure { - ctor public AssistStructure(); - method public android.content.ComponentName getActivityComponent(); - method public int getWindowNodeCount(); - } - - public static class AssistStructure.ViewNode { - method public android.app.AssistStructure.ViewNode getChildAt(int); - method public int getChildCount(); - method public java.lang.String getClassName(); - method public java.lang.CharSequence getContentDescription(); - method public android.os.Bundle getExtras(); - method public int getHeight(); - method public java.lang.String getHint(); - method public int getId(); - method public java.lang.String getIdEntry(); - method public java.lang.String getIdPackage(); - method public java.lang.String getIdType(); - method public int getLeft(); - method public int getScrollX(); - method public int getScrollY(); - method public java.lang.CharSequence getText(); - method public int getTextBackgroundColor(); - method public int getTextColor(); - method public int getTextSelectionEnd(); - method public int getTextSelectionStart(); - method public float getTextSize(); - method public int getTextStyle(); - method public int getTop(); - method public int getVisibility(); - method public int getWidth(); - method public boolean isAccessibilityFocused(); - method public boolean isActivated(); - method public boolean isAssistBlocked(); - method public boolean isCheckable(); - method public boolean isChecked(); - method public boolean isClickable(); - method public boolean isContextClickable(); - method public boolean isEnabled(); - method public boolean isFocusable(); - method public boolean isFocused(); - method public boolean isLongClickable(); - method public boolean isSelected(); - field public static final int TEXT_COLOR_UNDEFINED = 1; // 0x1 - field public static final int TEXT_STYLE_BOLD = 1; // 0x1 - field public static final int TEXT_STYLE_ITALIC = 2; // 0x2 - field public static final int TEXT_STYLE_STRIKE_THRU = 8; // 0x8 - field public static final int TEXT_STYLE_UNDERLINE = 4; // 0x4 - } - - public static class AssistStructure.WindowNode { - method public int getDisplayId(); - method public int getHeight(); - method public int getLeft(); - method public android.app.AssistStructure.ViewNode getRootViewNode(); - method public java.lang.CharSequence getTitle(); - method public int getTop(); - method public int getWidth(); - } - public class BroadcastOptions { method public static android.app.BroadcastOptions makeBasic(); method public void setTemporaryAppWhitelistDuration(long); @@ -6018,22 +5945,87 @@ package android.app.admin { package android.app.assist { - public final class AssistContent extends android.app.AssistContent implements android.os.Parcelable { - ctor public AssistContent(android.os.Parcel); + public deprecated class AssistContent implements android.os.Parcelable { + ctor public AssistContent(); method public int describeContents(); + method public android.content.ClipData getClipData(); + method public android.os.Bundle getExtras(); method public android.content.Intent getIntent(); + method public java.lang.String getStructuredData(); + method public android.net.Uri getWebUri(); + method public boolean isAppProvidedIntent(); + method public void setClipData(android.content.ClipData); + method public void setIntent(android.content.Intent); + method public void setStructuredData(java.lang.String); + method public void setWebUri(android.net.Uri); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.app.assist.AssistContent> CREATOR; } - public final class AssistStructure extends android.app.AssistStructure implements android.os.Parcelable { + public class AssistStructure implements android.os.Parcelable { ctor public AssistStructure(); method public int describeContents(); - method public android.app.AssistStructure.WindowNode getWindowNodeAt(int); + method public android.content.ComponentName getActivityComponent(); + method public android.app.assist.AssistStructure.WindowNode getWindowNodeAt(int); + method public int getWindowNodeCount(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.app.assist.AssistStructure> CREATOR; } + public static class AssistStructure.ViewNode { + method public android.app.assist.AssistStructure.ViewNode getChildAt(int); + method public int getChildCount(); + method public java.lang.String getClassName(); + method public java.lang.CharSequence getContentDescription(); + method public android.os.Bundle getExtras(); + method public int getHeight(); + method public java.lang.String getHint(); + method public int getId(); + method public java.lang.String getIdEntry(); + method public java.lang.String getIdPackage(); + method public java.lang.String getIdType(); + method public int getLeft(); + method public int getScrollX(); + method public int getScrollY(); + method public java.lang.CharSequence getText(); + method public int getTextBackgroundColor(); + method public int getTextColor(); + method public int getTextSelectionEnd(); + method public int getTextSelectionStart(); + method public float getTextSize(); + method public int getTextStyle(); + method public int getTop(); + method public int getVisibility(); + method public int getWidth(); + method public boolean isAccessibilityFocused(); + method public boolean isActivated(); + method public boolean isAssistBlocked(); + method public boolean isCheckable(); + method public boolean isChecked(); + method public boolean isClickable(); + method public boolean isContextClickable(); + method public boolean isEnabled(); + method public boolean isFocusable(); + method public boolean isFocused(); + method public boolean isLongClickable(); + method public boolean isSelected(); + field public static final int TEXT_COLOR_UNDEFINED = 1; // 0x1 + field public static final int TEXT_STYLE_BOLD = 1; // 0x1 + field public static final int TEXT_STYLE_ITALIC = 2; // 0x2 + field public static final int TEXT_STYLE_STRIKE_THRU = 8; // 0x8 + field public static final int TEXT_STYLE_UNDERLINE = 4; // 0x4 + } + + public static class AssistStructure.WindowNode { + method public int getDisplayId(); + method public int getHeight(); + method public int getLeft(); + method public android.app.assist.AssistStructure.ViewNode getRootViewNode(); + method public java.lang.CharSequence getTitle(); + method public int getTop(); + method public int getWidth(); + } + } package android.app.backup { @@ -7436,6 +7428,7 @@ package android.content { method public android.content.Context getContext(); method public final android.os.IBinder getSyncAdapterBinder(); method public abstract void onPerformSync(android.accounts.Account, android.os.Bundle, java.lang.String, android.content.ContentProviderClient, android.content.SyncResult); + method public void onSecurityException(android.accounts.Account, android.os.Bundle, java.lang.String, android.content.SyncResult); method public void onSyncCanceled(); method public void onSyncCanceled(java.lang.Thread); field public static final deprecated int LOG_SYNC_DETAILS = 2743; // 0xab7 @@ -13836,7 +13829,6 @@ package android.hardware.camera2 { field public static final int HOT_PIXEL_MODE_HIGH_QUALITY = 2; // 0x2 field public static final int HOT_PIXEL_MODE_OFF = 0; // 0x0 field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; // 0x1 - field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_HIGH_RESOLUTION = 3; // 0x3 field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; // 0x2 field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; // 0x0 field public static final int LENS_FACING_BACK = 1; // 0x1 @@ -14189,6 +14181,7 @@ package android.hardware.camera2.params { } public final class StreamConfigurationMap { + method public android.util.Size[] getHighResolutionOutputSizes(int); method public android.util.Range<java.lang.Integer>[] getHighSpeedVideoFpsRanges(); method public android.util.Range<java.lang.Integer>[] getHighSpeedVideoFpsRangesFor(android.util.Size); method public android.util.Size[] getHighSpeedVideoSizes(); @@ -19708,7 +19701,6 @@ package android.net { method public void onLinkPropertiesChanged(android.net.Network, android.net.LinkProperties); method public void onLosing(android.net.Network, int); method public void onLost(android.net.Network); - method public void onPreCheck(android.net.Network); } public static abstract interface ConnectivityManager.OnNetworkActiveListener { @@ -19859,6 +19851,7 @@ package android.net { method public boolean hasTransport(int); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.net.NetworkCapabilities> CREATOR; + field public static final int NET_CAPABILITY_CAPTIVE_PORTAL = 17; // 0x11 field public static final int NET_CAPABILITY_CBS = 5; // 0x5 field public static final int NET_CAPABILITY_DUN = 2; // 0x2 field public static final int NET_CAPABILITY_EIMS = 10; // 0xa @@ -19873,6 +19866,7 @@ package android.net { field public static final int NET_CAPABILITY_RCS = 8; // 0x8 field public static final int NET_CAPABILITY_SUPL = 1; // 0x1 field public static final int NET_CAPABILITY_TRUSTED = 14; // 0xe + field public static final int NET_CAPABILITY_VALIDATED = 16; // 0x10 field public static final int NET_CAPABILITY_WIFI_P2P = 6; // 0x6 field public static final int NET_CAPABILITY_XCAP = 9; // 0x9 field public static final int TRANSPORT_BLUETOOTH = 2; // 0x2 @@ -20827,6 +20821,8 @@ package android.net.wifi { public class ScanResult implements android.os.Parcelable { method public int describeContents(); + method public boolean is80211mcResponder(); + method public boolean isPasspointNetwork(); method public void writeToParcel(android.os.Parcel, int); field public java.lang.String BSSID; field public static final int CHANNEL_WIDTH_160MHZ = 3; // 0x3 @@ -20834,18 +20830,19 @@ package android.net.wifi { field public static final int CHANNEL_WIDTH_40MHZ = 1; // 0x1 field public static final int CHANNEL_WIDTH_80MHZ = 2; // 0x2 field public static final int CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4; // 0x4 + field public static final long FLAG_80211mc_RESPONDER = 2L; // 0x2L + field public static final long FLAG_PASSPOINT_NETWORK = 1L; // 0x1L field public java.lang.String SSID; field public java.lang.String capabilities; field public int centerFreq0; field public int centerFreq1; field public int channelWidth; + field public long flags; field public int frequency; - field public boolean is80211McRTTResponder; field public int level; - field public java.lang.String operatorFriendlyName; - field public boolean passpointNetwork; + field public java.lang.CharSequence operatorFriendlyName; field public long timestamp; - field public java.lang.String venueName; + field public java.lang.CharSequence venueName; } public final class SupplicantState extends java.lang.Enum implements android.os.Parcelable { @@ -20895,7 +20892,7 @@ package android.net.wifi { field public java.lang.String preSharedKey; field public int priority; field public java.lang.String providerFriendlyName; - field public java.util.HashSet<java.lang.Long> roamingConsortiumIds; + field public java.lang.Long[] roamingConsortiumIds; field public int status; field public java.lang.String[] wepKeys; field public int wepTxKeyIndex; @@ -20972,7 +20969,7 @@ package android.net.wifi { method public java.lang.String getAnonymousIdentity(); method public java.security.cert.X509Certificate getCaCertificate(); method public java.security.cert.X509Certificate getClientCertificate(); - method public java.lang.String getDomainSubjectMatch(); + method public java.lang.String getDomainSuffixMatch(); method public int getEapMethod(); method public java.lang.String getIdentity(); method public java.lang.String getPassword(); @@ -28630,6 +28627,7 @@ package android.provider { field public static final java.lang.String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled"; field public static final java.lang.String USE_GOOGLE_MAIL = "use_google_mail"; field public static final java.lang.String WAIT_FOR_DEBUGGER = "wait_for_debugger"; + field public static final java.lang.String WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN = "wifi_device_owner_configs_lockdown"; field public static final java.lang.String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count"; field public static final java.lang.String WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS = "wifi_mobile_data_transition_wakelock_timeout_ms"; field public static final java.lang.String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = "wifi_networks_available_notification_on"; diff --git a/cmds/hid/Android.mk b/cmds/hid/Android.mk new file mode 100644 index 0000000..ff3691d --- /dev/null +++ b/cmds/hid/Android.mk @@ -0,0 +1,18 @@ +# Copyright 2015 The Android Open Source Project +# +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_MODULE := hid +LOCAL_JNI_SHARED_LIBRARIES := libhidcommand_jni +include $(BUILD_JAVA_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := hid +LOCAL_SRC_FILES := hid +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := EXECUTABLES +include $(BUILD_PREBUILT) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/cmds/hid/MODULE_LICENSE_APACHE2 b/cmds/hid/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/cmds/hid/MODULE_LICENSE_APACHE2 diff --git a/cmds/hid/NOTICE b/cmds/hid/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/cmds/hid/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, 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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/cmds/hid/hid b/cmds/hid/hid new file mode 100755 index 0000000..2359fcd --- /dev/null +++ b/cmds/hid/hid @@ -0,0 +1,8 @@ +#!/system/bin/sh +# +# Script to start "hid" on the device, which has a very rudimentary +# shell. +# +base=/system +export CLASSPATH=$base/framework/hid.jar +exec app_process $base/bin com.android.commands.hid.Hid "$@" diff --git a/cmds/hid/jni/Android.mk b/cmds/hid/jni/Android.mk new file mode 100644 index 0000000..8163a9d --- /dev/null +++ b/cmds/hid/jni/Android.mk @@ -0,0 +1,23 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + com_android_commands_hid_Device.cpp + +LOCAL_C_INCLUDES := \ + $(JNI_H_INCLUDE) \ + frameworks/base/core/jni + +LOCAL_SHARED_LIBRARIES := \ + libandroid_runtime \ + liblog \ + libnativehelper \ + libutils + +LOCAL_MODULE := libhidcommand_jni +LOCAL_MODULE_TAGS := optional + +LOCAL_CFLAGS += -Wall + +include $(BUILD_SHARED_LIBRARY) diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp new file mode 100644 index 0000000..4278e7d --- /dev/null +++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp @@ -0,0 +1,253 @@ +/* + * 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. + */ + +#define LOG_TAG "HidCommandDevice" + +#include "com_android_commands_hid_Device.h" + +#include <linux/uhid.h> + +#include <fcntl.h> +#include <cstdio> +#include <cstring> +#include <memory> +#include <unistd.h> + +#include <android_runtime/AndroidRuntime.h> +#include <android_runtime/Log.h> +#include <android_os_MessageQueue.h> +#include <core_jni_helpers.h> +#include <jni.h> +#include <JNIHelp.h> +#include <ScopedPrimitiveArray.h> +#include <ScopedUtfChars.h> +#include <utils/Log.h> +#include <utils/Looper.h> +#include <utils/StrongPointer.h> + +namespace android { +namespace uhid { + +static const char* UHID_PATH = "/dev/uhid"; +static const size_t UHID_MAX_NAME_LENGTH = 128; + +static struct { + jmethodID onDeviceOpen; + jmethodID onDeviceError; +} gDeviceCallbackClassInfo; + +static int handleLooperEvents(int fd, int events, void* data) { + Device* d = reinterpret_cast<Device*>(data); + return d->handleEvents(events); +} + +static void checkAndClearException(JNIEnv* env, const char* methodName) { + if (env->ExceptionCheck()) { + ALOGE("An exception was thrown by callback '%s'.", methodName); + LOGE_EX(env); + env->ExceptionClear(); + } +} + +DeviceCallback::DeviceCallback(JNIEnv* env, jobject callback) : + mCallbackObject(env->NewGlobalRef(callback)) { } + +DeviceCallback::~DeviceCallback() { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->DeleteGlobalRef(mCallbackObject); +} + +void DeviceCallback::onDeviceError() { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceError); + checkAndClearException(env, "onDeviceError"); +} + +void DeviceCallback::onDeviceOpen() { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOpen); + checkAndClearException(env, "onDeviceOpen"); +} + +Device* Device::open(int32_t id, const char* name, int32_t vid, int32_t pid, + std::unique_ptr<uint8_t[]> descriptor, size_t descriptorSize, + std::unique_ptr<DeviceCallback> callback, sp<Looper> looper) { + + int fd = ::open(UHID_PATH, O_RDWR | O_CLOEXEC); + if (fd < 0) { + ALOGE("Failed to open uhid: %s", strerror(errno)); + return nullptr; + } + + struct uhid_event ev; + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_CREATE; + strncpy((char*)ev.u.create.name, name, UHID_MAX_NAME_LENGTH); + ev.u.create.rd_data = descriptor.get(); + ev.u.create.rd_size = descriptorSize; + ev.u.create.bus = BUS_BLUETOOTH; + ev.u.create.vendor = vid; + ev.u.create.product = pid; + ev.u.create.version = 0; + ev.u.create.country = 0; + + errno = 0; + ssize_t ret = TEMP_FAILURE_RETRY(::write(fd, &ev, sizeof(ev))); + if (ret < 0 || ret != sizeof(ev)) { + ::close(fd); + ALOGE("Failed to create uhid node: %s", strerror(errno)); + return nullptr; + } + + // Wait for the device to actually be created. + ret = TEMP_FAILURE_RETRY(::read(fd, &ev, sizeof(ev))); + if (ret < 0 || ev.type != UHID_START) { + ::close(fd); + ALOGE("uhid node failed to start: %s", strerror(errno)); + return nullptr; + } + + return new Device(id, fd, std::move(callback), looper); +} + +Device::Device(int32_t id, int fd, std::unique_ptr<DeviceCallback> callback, sp<Looper> looper) : + mId(id), mFd(fd), mDeviceCallback(std::move(callback)), mLooper(looper) { + looper->addFd(fd, 0, Looper::EVENT_INPUT, handleLooperEvents, reinterpret_cast<void*>(this)); +} + +Device::~Device() { + mLooper->removeFd(mFd); + struct uhid_event ev; + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_DESTROY; + TEMP_FAILURE_RETRY(::write(mFd, &ev, sizeof(ev))); + ::close(mFd); + mFd = -1; +} + +void Device::sendReport(uint8_t* report, size_t reportSize) { + struct uhid_event ev; + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_INPUT; + ev.u.input.size = reportSize; + memcpy(&ev.u.input.data, report, reportSize); + ssize_t ret = TEMP_FAILURE_RETRY(::write(mFd, &ev, sizeof(ev))); + if (ret < 0 || ret != sizeof(ev)) { + ALOGE("Failed to send hid event: %s", strerror(errno)); + } +} + +int Device::handleEvents(int events) { + if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { + ALOGE("uhid node was closed or an error occurred. events=0x%x", events); + mDeviceCallback->onDeviceError(); + return 0; + } + struct uhid_event ev; + ssize_t ret = TEMP_FAILURE_RETRY(::read(mFd, &ev, sizeof(ev))); + if (ret < 0) { + ALOGE("Failed to read from uhid node: %s", strerror(errno)); + mDeviceCallback->onDeviceError(); + return 0; + } + + if (ev.type == UHID_OPEN) { + mDeviceCallback->onDeviceOpen(); + } + + return 1; +} + +} // namespace uhid + +std::unique_ptr<uint8_t[]> getData(JNIEnv* env, jbyteArray javaArray, size_t& outSize) { + ScopedByteArrayRO scopedArray(env, javaArray); + outSize = scopedArray.size(); + std::unique_ptr<uint8_t[]> data(new uint8_t[outSize]); + for (size_t i = 0; i < outSize; i++) { + data[i] = static_cast<uint8_t>(scopedArray[i]); + } + return data; +} + +static jlong openDevice(JNIEnv* env, jclass clazz, jstring rawName, jint id, jint vid, jint pid, + jbyteArray rawDescriptor, jobject queue, jobject callback) { + ScopedUtfChars name(env, rawName); + if (name.c_str() == nullptr) { + return 0; + } + + size_t size; + std::unique_ptr<uint8_t[]> desc = getData(env, rawDescriptor, size); + + std::unique_ptr<uhid::DeviceCallback> cb(new uhid::DeviceCallback(env, callback)); + sp<Looper> looper = android_os_MessageQueue_getMessageQueue(env, queue)->getLooper(); + + uhid::Device* d = uhid::Device::open( + id, reinterpret_cast<const char*>(name.c_str()), vid, pid, + std::move(desc), size, std::move(cb), std::move(looper)); + return reinterpret_cast<jlong>(d); +} + +static void sendReport(JNIEnv* env, jclass clazz, jlong ptr,jbyteArray rawReport) { + size_t size; + std::unique_ptr<uint8_t[]> report = getData(env, rawReport, size); + uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr); + if (d) { + d->sendReport(report.get(), size); + } +} + +static void closeDevice(JNIEnv* env, jclass clazz, jlong ptr) { + uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr); + if (d) { + delete d; + } +} + +static JNINativeMethod sMethods[] = { + { "nativeOpenDevice", + "(Ljava/lang/String;III[BLandroid/os/MessageQueue;" + "Lcom/android/commands/hid/Device$DeviceCallback;)J", + reinterpret_cast<void*>(openDevice) }, + { "nativeSendReport", "(J[B)V", reinterpret_cast<void*>(sendReport) }, + { "nativeCloseDevice", "(J)V", reinterpret_cast<void*>(closeDevice) }, +}; + +int register_com_android_commands_hid_Device(JNIEnv* env) { + jclass clazz = FindClassOrDie(env, "com/android/commands/hid/Device$DeviceCallback"); + uhid::gDeviceCallbackClassInfo.onDeviceOpen = + GetMethodIDOrDie(env, clazz, "onDeviceOpen", "()V"); + uhid::gDeviceCallbackClassInfo.onDeviceError= + GetMethodIDOrDie(env, clazz, "onDeviceError", "()V"); + return jniRegisterNativeMethods(env, "com/android/commands/hid/Device", + sMethods, NELEM(sMethods)); +} + +} // namespace android + +jint JNI_OnLoad(JavaVM* jvm, void*) { + JNIEnv *env = NULL; + if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6)) { + return JNI_ERR; + } + + if (android::register_com_android_commands_hid_Device(env) < 0 ){ + return JNI_ERR; + } + + return JNI_VERSION_1_6; +} diff --git a/cmds/hid/jni/com_android_commands_hid_Device.h b/cmds/hid/jni/com_android_commands_hid_Device.h new file mode 100644 index 0000000..6c5899e --- /dev/null +++ b/cmds/hid/jni/com_android_commands_hid_Device.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#include <memory> + +#include <jni.h> +#include <utils/Looper.h> +#include <utils/StrongPointer.h> + +namespace android { +namespace uhid { + +class DeviceCallback { +public: + DeviceCallback(JNIEnv* env, jobject callback); + ~DeviceCallback(); + + void onDeviceOpen(); + void onDeviceError(); + +private: + jobject mCallbackObject; +}; + +class Device { +public: + static Device* open(int32_t id, const char* name, int32_t vid, int32_t pid, + std::unique_ptr<uint8_t[]> descriptor, size_t descriptorSize, + std::unique_ptr<DeviceCallback> callback, sp<Looper> looper); + + Device(int32_t id, int fd, std::unique_ptr<DeviceCallback> callback, sp<Looper> looper); + ~Device(); + + void sendReport(uint8_t* report, size_t reportSize); + void close(); + + int handleEvents(int events); + +private: + int32_t mId; + int mFd; + std::unique_ptr<DeviceCallback> mDeviceCallback; + sp<Looper> mLooper; +}; + + +} // namespace uhid +} // namespace android diff --git a/cmds/hid/src/com/android/commands/hid/Device.java b/cmds/hid/src/com/android/commands/hid/Device.java new file mode 100644 index 0000000..dbe883b --- /dev/null +++ b/cmds/hid/src/com/android/commands/hid/Device.java @@ -0,0 +1,163 @@ +/* + * 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.commands.hid; + +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.os.MessageQueue; +import android.os.SystemClock; +import android.util.Log; + +import com.android.internal.os.SomeArgs; + +public class Device { + private static final String TAG = "HidDevice"; + + // Minimum amount of time to wait before sending input events to a device. Even though we're + // guaranteed that the device has been created and opened by the input system, there's still a + // window in which the system hasn't started reading events out of it. If a stream of events + // begins in during this window (like a button down event) and *then* we start reading, we're + // liable to ignore the whole stream. + private static final int MIN_WAIT_FOR_FIRST_EVENT = 150; + + private static final int MSG_OPEN_DEVICE = 1; + private static final int MSG_SEND_REPORT = 2; + private static final int MSG_CLOSE_DEVICE = 3; + + + private final int mId; + private final HandlerThread mThread; + private final DeviceHandler mHandler; + private long mEventTime; + + private final Object mCond = new Object(); + + static { + System.loadLibrary("hidcommand_jni"); + } + + private static native long nativeOpenDevice(String name, int id, int vid, int pid, + byte[] descriptor, MessageQueue queue, DeviceCallback callback); + private static native void nativeSendReport(long ptr, byte[] data); + private static native void nativeCloseDevice(long ptr); + + public Device(int id, String name, int vid, int pid, byte[] descriptor, byte[] report) { + mId = id; + mThread = new HandlerThread("HidDeviceHandler"); + mThread.start(); + mHandler = new DeviceHandler(mThread.getLooper()); + SomeArgs args = SomeArgs.obtain(); + args.argi1 = id; + args.argi2 = vid; + args.argi3 = pid; + if (name != null) { + args.arg1 = name; + } else { + args.arg1 = id + ":" + vid + ":" + pid; + } + args.arg2 = descriptor; + args.arg3 = report; + mHandler.obtainMessage(MSG_OPEN_DEVICE, args).sendToTarget(); + mEventTime = SystemClock.uptimeMillis() + MIN_WAIT_FOR_FIRST_EVENT; + } + + public void sendReport(byte[] report) { + Message msg = mHandler.obtainMessage(MSG_SEND_REPORT, report); + mHandler.sendMessageAtTime(msg, mEventTime); + } + + public void addDelay(int delay) { + mEventTime += delay; + } + + public void close() { + Message msg = mHandler.obtainMessage(MSG_CLOSE_DEVICE); + msg.setAsynchronous(true); + mHandler.sendMessageAtTime(msg, mEventTime + 1); + try { + synchronized (mCond) { + mCond.wait(); + } + } catch (InterruptedException ignore) {} + } + + private class DeviceHandler extends Handler { + private long mPtr; + private int mBarrierToken; + + public DeviceHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_OPEN_DEVICE: + SomeArgs args = (SomeArgs) msg.obj; + mPtr = nativeOpenDevice((String) args.arg1, args.argi1, args.argi2, args.argi3, + (byte[]) args.arg2, getLooper().myQueue(), new DeviceCallback()); + nativeSendReport(mPtr, (byte[]) args.arg3); + pauseEvents(); + break; + case MSG_SEND_REPORT: + if (mPtr != 0) { + nativeSendReport(mPtr, (byte[]) msg.obj); + } else { + Log.e(TAG, "Tried to send report to closed device."); + } + break; + case MSG_CLOSE_DEVICE: + if (mPtr != 0) { + nativeCloseDevice(mPtr); + getLooper().quitSafely(); + mPtr = 0; + } else { + Log.e(TAG, "Tried to close already closed device."); + } + synchronized (mCond) { + mCond.notify(); + } + break; + default: + throw new IllegalArgumentException("Unknown device message"); + } + } + + public void pauseEvents() { + mBarrierToken = getLooper().myQueue().postSyncBarrier(); + } + + public void resumeEvents() { + getLooper().myQueue().removeSyncBarrier(mBarrierToken); + mBarrierToken = 0; + } + } + + private class DeviceCallback { + public void onDeviceOpen() { + mHandler.resumeEvents(); + } + + public void onDeviceError() { + Message msg = mHandler.obtainMessage(MSG_CLOSE_DEVICE); + msg.setAsynchronous(true); + msg.sendToTarget(); + } + } +} diff --git a/cmds/hid/src/com/android/commands/hid/Event.java b/cmds/hid/src/com/android/commands/hid/Event.java new file mode 100644 index 0000000..c6a37bd --- /dev/null +++ b/cmds/hid/src/com/android/commands/hid/Event.java @@ -0,0 +1,255 @@ +/* + * 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.commands.hid; + +import android.util.JsonReader; +import android.util.JsonToken; +import android.util.Log; + +import java.io.InputStreamReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; + +public class Event { + private static final String TAG = "HidEvent"; + + public static final String COMMAND_REGISTER = "register"; + public static final String COMMAND_DELAY = "delay"; + public static final String COMMAND_REPORT = "report"; + + private int mId; + private String mCommand; + private String mName; + private byte[] mDescriptor; + private int mVid; + private int mPid; + private byte[] mReport; + private int mDuration; + + public int getId() { + return mId; + } + + public String getCommand() { + return mCommand; + } + + public String getName() { + return mName; + } + + public byte[] getDescriptor() { + return mDescriptor; + } + + public int getVendorId() { + return mVid; + } + + public int getProductId() { + return mPid; + } + + public byte[] getReport() { + return mReport; + } + + public int getDuration() { + return mDuration; + } + + public String toString() { + return "Event{id=" + mId + + ", command=" + String.valueOf(mCommand) + + ", name=" + String.valueOf(mName) + + ", descriptor=" + Arrays.toString(mDescriptor) + + ", vid=" + mVid + + ", pid=" + mPid + + ", report=" + Arrays.toString(mReport) + + ", duration=" + mDuration + + "}"; + } + + private static class Builder { + private Event mEvent; + + public Builder() { + mEvent = new Event(); + } + + public void setId(int id) { + mEvent.mId = id; + } + + private void setCommand(String command) { + mEvent.mCommand = command; + } + + public void setName(String name) { + mEvent.mName = name; + } + + public void setDescriptor(byte[] descriptor) { + mEvent.mDescriptor = descriptor; + } + + public void setReport(byte[] report) { + mEvent.mReport = report; + } + + public void setVid(int vid) { + mEvent.mVid = vid; + } + + public void setPid(int pid) { + mEvent.mPid = pid; + } + + public void setDuration(int duration) { + mEvent.mDuration = duration; + } + + public Event build() { + if (mEvent.mId == -1) { + throw new IllegalStateException("No event id"); + } else if (mEvent.mCommand == null) { + throw new IllegalStateException("Event does not contain a command"); + } + if (COMMAND_REGISTER.equals(mEvent.mCommand)) { + if (mEvent.mDescriptor == null) { + throw new IllegalStateException("Device registration is missing descriptor"); + } + } else if (COMMAND_DELAY.equals(mEvent.mCommand)) { + if (mEvent.mDuration <= 0) { + throw new IllegalStateException("Delay has missing or invalid duration"); + } + } else if (COMMAND_REPORT.equals(mEvent.mCommand)) { + if (mEvent.mReport == null) { + throw new IllegalStateException("Report command is missing report data"); + } + } + return mEvent; + } + } + + public static class Reader { + private JsonReader mReader; + + public Reader(InputStreamReader in) { + mReader = new JsonReader(in); + mReader.setLenient(true); + } + + public Event getNextEvent() throws IOException { + Event e = null; + while (e == null && mReader.peek() != JsonToken.END_DOCUMENT) { + Event.Builder eb = new Event.Builder(); + try { + mReader.beginObject(); + while (mReader.hasNext()) { + String name = mReader.nextName(); + switch (name) { + case "id": + eb.setId(readInt()); + break; + case "command": + eb.setCommand(mReader.nextString()); + break; + case "descriptor": + eb.setDescriptor(readData()); + break; + case "name": + eb.setName(mReader.nextString()); + break; + case "vid": + eb.setVid(readInt()); + break; + case "pid": + eb.setPid(readInt()); + break; + case "report": + eb.setReport(readData()); + break; + case "duration": + eb.setDuration(readInt()); + break; + default: + mReader.skipValue(); + } + } + mReader.endObject(); + } catch (IllegalStateException ex) { + error("Error reading in object, ignoring.", ex); + consumeRemainingElements(); + mReader.endObject(); + continue; + } + e = eb.build(); + } + + return e; + } + + private byte[] readData() throws IOException { + ArrayList<Integer> data = new ArrayList<Integer>(); + try { + mReader.beginArray(); + while (mReader.hasNext()) { + data.add(Integer.decode(mReader.nextString())); + } + mReader.endArray(); + } catch (IllegalStateException|NumberFormatException e) { + consumeRemainingElements(); + mReader.endArray(); + throw new IllegalStateException("Encountered malformed data.", e); + } + byte[] rawData = new byte[data.size()]; + for (int i = 0; i < data.size(); i++) { + int d = data.get(i); + if ((d & 0xFF) != d) { + throw new IllegalStateException("Invalid data, all values must be byte-sized"); + } + rawData[i] = (byte)d; + } + return rawData; + } + + private int readInt() throws IOException { + String val = mReader.nextString(); + return Integer.decode(val); + } + + private void consumeRemainingElements() throws IOException { + while (mReader.hasNext()) { + mReader.skipValue(); + } + } + } + + private static void error(String msg) { + error(msg, null); + } + + private static void error(String msg, Exception e) { + System.out.println(msg); + Log.e(TAG, msg); + if (e != null) { + Log.e(TAG, Log.getStackTraceString(e)); + } + } +} diff --git a/cmds/hid/src/com/android/commands/hid/Hid.java b/cmds/hid/src/com/android/commands/hid/Hid.java new file mode 100644 index 0000000..976a782 --- /dev/null +++ b/cmds/hid/src/com/android/commands/hid/Hid.java @@ -0,0 +1,133 @@ +/* + * 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.commands.hid; + +import android.os.SystemClock; +import android.util.JsonReader; +import android.util.JsonToken; +import android.util.Log; +import android.util.SparseArray; + +import libcore.io.IoUtils; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; + +public class Hid { + private static final String TAG = "HID"; + + private final Event.Reader mReader; + private final SparseArray<Device> mDevices; + + private static void usage() { + error("Usage: hid [FILE]"); + } + + public static void main(String[] args) { + if (args.length != 1) { + usage(); + System.exit(1); + } + + InputStream stream = null; + try { + if (args[0].equals("-")) { + stream = System.in; + } else { + File f = new File(args[0]); + stream = new FileInputStream(f); + } + (new Hid(stream)).run(); + } catch (Exception e) { + error("HID injection failed.", e); + System.exit(1); + } finally { + IoUtils.closeQuietly(stream); + } + } + + private Hid(InputStream in) { + mDevices = new SparseArray<Device>(); + try { + mReader = new Event.Reader(new InputStreamReader(in, "UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + private void run() { + try { + Event e = null; + while ((e = mReader.getNextEvent()) != null) { + process(e); + } + } catch (IOException ex) { + error("Error reading in events.", ex); + } + + for (int i = 0; i < mDevices.size(); i++) { + mDevices.valueAt(i).close(); + } + } + + + private void process(Event e) { + final int index = mDevices.indexOfKey(e.getId()); + if (index >= 0) { + Device d = mDevices.valueAt(index); + if (Event.COMMAND_DELAY.equals(e.getCommand())) { + d.addDelay(e.getDuration()); + } else if (Event.COMMAND_REPORT.equals(e.getCommand())) { + d.sendReport(e.getReport()); + } else { + error("Unknown command \"" + e.getCommand() + "\". Ignoring event."); + } + } else { + registerDevice(e); + } + } + + private void registerDevice(Event e) { + if (!Event.COMMAND_REGISTER.equals(e.getCommand())) { + throw new IllegalStateException( + "Tried to send command \"" + e.getCommand() + "\" to an unregistered device!"); + } + int id = e.getId(); + Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(), + e.getDescriptor(), e.getReport()); + mDevices.append(id, d); + } + + private static void error(String msg) { + error(msg, null); + } + + private static void error(String msg, Exception e) { + System.out.println(msg); + Log.e(TAG, msg); + if (e != null) { + Log.e(TAG, Log.getStackTraceString(e)); + } + } +} diff --git a/cmds/wm/src/com/android/commands/wm/Wm.java b/cmds/wm/src/com/android/commands/wm/Wm.java index 64f023f..fb050e5 100644 --- a/cmds/wm/src/com/android/commands/wm/Wm.java +++ b/cmds/wm/src/com/android/commands/wm/Wm.java @@ -54,6 +54,7 @@ public class Wm extends BaseCommand { " wm density [reset|DENSITY]\n" + " wm overscan [reset|LEFT,TOP,RIGHT,BOTTOM]\n" + " wm scaling [off|auto]\n" + + " wm screen-capture [userId] [true|false]\n" + "\n" + "wm size: return or override display size.\n" + " width and height in pixels unless suffixed with 'dp'.\n" + @@ -62,7 +63,9 @@ public class Wm extends BaseCommand { "\n" + "wm overscan: set overscan area for display.\n" + "\n" + - "wm scaling: set display scaling mode.\n" + "wm scaling: set display scaling mode.\n" + + "\n" + + "wm screen-capture: enable/disable screen capture.\n" ); } @@ -85,16 +88,39 @@ public class Wm extends BaseCommand { runDisplayOverscan(); } else if (op.equals("scaling")) { runDisplayScaling(); + } else if (op.equals("screen-capture")) { + runSetScreenCapture(); } else { showError("Error: unknown command '" + op + "'"); return; } } + private void runSetScreenCapture() throws Exception { + String userIdStr = nextArg(); + String enableStr = nextArg(); + int userId; + boolean disable; + + try { + userId = Integer.parseInt(userIdStr); + } catch (NumberFormatException e) { + System.err.println("Error: bad number " + e); + return; + } + + disable = !Boolean.parseBoolean(enableStr); + + try { + mWm.setScreenCaptureDisabled(userId, disable); + } catch (RemoteException e) { + System.err.println("Error: Can't set screen capture " + e); + } + } + private void runDisplaySize() throws Exception { String size = nextArg(); int w, h; - boolean scale = true; if (size == null) { Point initialSize = new Point(); Point baseSize = new Point(); @@ -181,7 +207,6 @@ public class Wm extends BaseCommand { private void runDisplayOverscan() throws Exception { String overscanStr = nextArgRequired(); Rect rect = new Rect(); - int density; if ("reset".equals(overscanStr)) { rect.set(0, 0, 0, 0); } else { diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 1b4ee2e..e8ab109 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -2075,6 +2075,9 @@ public class Activity extends ContextThemeWrapper "by the window decor. Do not request Window.FEATURE_ACTION_BAR and set " + "android:windowActionBar to false in your theme to use a Toolbar instead."); } + // Clear out the MenuInflater to make sure that it is valid for the new Action Bar + mMenuInflater = null; + ToolbarActionBar tbab = new ToolbarActionBar(toolbar, getTitle(), this); mActionBar = tbab; mWindow.setCallback(tbab.getWrappedWindowCallback()); diff --git a/core/java/android/app/AssistContent.java b/core/java/android/app/AssistContent.java deleted file mode 100644 index ad2ba39..0000000 --- a/core/java/android/app/AssistContent.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app; - -import android.content.ClipData; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; - -/** - * Holds information about the content an application is viewing, to hand to an - * assistant at the user's request. This is filled in by - * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}. - * @deprecated use {@link android.app.assist.AssistContent}. - */ -@Deprecated -public class AssistContent { - private boolean mIsAppProvidedIntent = false; - private Intent mIntent; - private String mStructuredData; - private ClipData mClipData; - private Uri mUri; - private final Bundle mExtras; - - /** - * @hide - * Key name this data structure is stored in the Bundle generated by - * {@link android.app.Activity#onProvideAssistData}. - */ - public static final String ASSIST_KEY = "android:assist_content"; - - /** - * @hide - * Retrieve the framework-generated AssistContent that is stored within - * the Bundle filled in by {@link android.app.Activity#onProvideAssistContent}. - */ - public static android.app.assist.AssistContent getAssistContent(Bundle assistBundle) { - return assistBundle.getParcelable(ASSIST_KEY); - } - - public AssistContent() { - mExtras = new Bundle(); - } - - /** - * @hide - * Called by {@link android.app.ActivityThread} to set the default Intent based on - * {@link android.app.Activity#getIntent Activity.getIntent}. - * - * <p>Automatically populates {@link #mUri} if that Intent is an {@link Intent#ACTION_VIEW} - * of a web (http or https scheme) URI.</p> - */ - public void setDefaultIntent(Intent intent) { - mIntent = intent; - setWebUri(null); - if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) { - Uri uri = intent.getData(); - if (uri != null) { - if ("http".equals(uri.getScheme()) || "https".equals(uri.getScheme())) { - setWebUri(uri); - } - } - } - } - - /** - * Sets the Intent associated with the content, describing the current top-level context of - * the activity. If this contains a reference to a piece of data related to the activity, - * be sure to set {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} so the accessibility - * service can access it. - */ - public void setIntent(Intent intent) { - mIsAppProvidedIntent = true; - mIntent = intent; - } - - /** - * Returns the current {@link #setIntent} if one is set, else the default Intent obtained from - * {@link android.app.Activity#getIntent Activity.getIntent}. Can be modified in-place. - * @hide - */ - public Intent getIntent() { - return mIntent; - } - - /** - * Returns whether or not the current Intent was explicitly provided in - * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}. If not, - * the Intent was automatically set based on - * {@link android.app.Activity#getIntent Activity.getIntent}. - */ - public boolean isAppProvidedIntent() { - return mIsAppProvidedIntent; - } - - /** - * Optional additional content items that are involved with - * the current UI. Access to this content will be granted to the assistant as if you - * are sending it through an Intent with {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}. - */ - public void setClipData(ClipData clip) { - mClipData = clip; - } - - /** - * Return the current {@link #setClipData}, which you can modify in-place. - */ - public ClipData getClipData() { - return mClipData; - } - - /** - * Sets optional structured data regarding the content being viewed. The provided data - * must be a string represented with <a href="http://json-ld.org/">JSON-LD</a> using the - * <a href="http://schema.org/">schema.org</a> vocabulary. - */ - public void setStructuredData(String structuredData) { - mStructuredData = structuredData; - } - - /** - * Returns the current {@link #setStructuredData}. - */ - public String getStructuredData() { - return mStructuredData; - } - - /** - * Set a web URI associated with the current data being shown to the user. - * This URI could be opened in a web browser, or in the app as an - * {@link Intent#ACTION_VIEW} Intent, to show the same data that is currently - * being displayed by it. The URI here should be something that is transportable - * off the device into other environments to acesss the same data as is currently - * being shown in the app; if the app does not have such a representation, it should - * leave the null and only report the local intent and clip data. - */ - public void setWebUri(Uri uri) { - mUri = uri; - } - - /** - * Return the content's web URI as per {@link #setWebUri(android.net.Uri)}, or null if - * there is none. - */ - public Uri getWebUri() { - return mUri; - } - - /** - * Return Bundle for extra vendor-specific data that can be modified and examined. - */ - public Bundle getExtras() { - return mExtras; - } - - /** @hide */ - public AssistContent(Parcel in) { - if (in.readInt() != 0) { - mIntent = Intent.CREATOR.createFromParcel(in); - } - if (in.readInt() != 0) { - mClipData = ClipData.CREATOR.createFromParcel(in); - } - if (in.readInt() != 0) { - mUri = Uri.CREATOR.createFromParcel(in); - } - if (in.readInt() != 0) { - mStructuredData = in.readString(); - } - mIsAppProvidedIntent = in.readInt() == 1; - mExtras = in.readBundle(); - } - - /** @hide */ - public void writeToParcelInternal(Parcel dest, int flags) { - if (mIntent != null) { - dest.writeInt(1); - mIntent.writeToParcel(dest, flags); - } else { - dest.writeInt(0); - } - if (mClipData != null) { - dest.writeInt(1); - mClipData.writeToParcel(dest, flags); - } else { - dest.writeInt(0); - } - if (mUri != null) { - dest.writeInt(1); - mUri.writeToParcel(dest, flags); - } else { - dest.writeInt(0); - } - if (mStructuredData != null) { - dest.writeInt(1); - dest.writeString(mStructuredData); - } else { - dest.writeInt(0); - } - dest.writeInt(mIsAppProvidedIntent ? 1 : 0); - dest.writeBundle(mExtras); - } -} diff --git a/core/java/android/app/AssistStructure.java b/core/java/android/app/AssistStructure.java deleted file mode 100644 index 7f6dae5..0000000 --- a/core/java/android/app/AssistStructure.java +++ /dev/null @@ -1,1075 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app; - -import android.content.ComponentName; -import android.graphics.Rect; -import android.os.Binder; -import android.os.Bundle; -import android.os.IBinder; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.PooledStringReader; -import android.os.PooledStringWriter; -import android.os.RemoteException; -import android.os.SystemClock; -import android.text.TextUtils; -import android.util.Log; -import android.view.View; -import android.view.ViewAssistStructure; -import android.view.ViewRootImpl; -import android.view.WindowManager; -import android.view.WindowManagerGlobal; - -import java.util.ArrayList; - -/** - * Assist data automatically created by the platform's implementation - * of {@link android.app.Activity#onProvideAssistData}. - * @deprecated use {@link android.app.assist.AssistStructure}. - */ -@Deprecated -public class AssistStructure { - static final String TAG = "AssistStructure"; - - /** - * @hide - * Key name this data structure is stored in the Bundle generated by - * {@link android.app.Activity#onProvideAssistData}. - */ - public static final String ASSIST_KEY = "android:assist_structure"; - - /** @hide */ - public boolean mHaveData; - - ComponentName mActivityComponent; - - final ArrayList<WindowNode> mWindowNodes = new ArrayList<>(); - - final ArrayList<ViewNodeBuilder> mPendingAsyncChildren = new ArrayList<>(); - - /** @hide */ - public SendChannel mSendChannel; - /** @hide */ - public IBinder mReceiveChannel; - - Rect mTmpRect = new Rect(); - - static final int TRANSACTION_XFER = Binder.FIRST_CALL_TRANSACTION+1; - static final String DESCRIPTOR = "android.app.AssistStructure"; - - /** @hide */ - public final class SendChannel extends Binder { - @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) - throws RemoteException { - if (code == TRANSACTION_XFER) { - data.enforceInterface(DESCRIPTOR); - writeContentToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); - return true; - } else { - return super.onTransact(code, data, reply, flags); - } - } - } - - final static class ViewNodeText { - CharSequence mText; - int mTextSelectionStart; - int mTextSelectionEnd; - int mTextColor; - int mTextBackgroundColor; - float mTextSize; - int mTextStyle; - String mHint; - - ViewNodeText() { - } - - ViewNodeText(Parcel in) { - mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); - mTextSelectionStart = in.readInt(); - mTextSelectionEnd = in.readInt(); - mTextColor = in.readInt(); - mTextBackgroundColor = in.readInt(); - mTextSize = in.readFloat(); - mTextStyle = in.readInt(); - mHint = in.readString(); - } - - void writeToParcel(Parcel out) { - TextUtils.writeToParcel(mText, out, 0); - out.writeInt(mTextSelectionStart); - out.writeInt(mTextSelectionEnd); - out.writeInt(mTextColor); - out.writeInt(mTextBackgroundColor); - out.writeFloat(mTextSize); - out.writeInt(mTextStyle); - out.writeString(mHint); - } - } - - /** - * Describes a window in the assist data. - */ - static public class WindowNode { - final int mX; - final int mY; - final int mWidth; - final int mHeight; - final CharSequence mTitle; - final int mDisplayId; - final ViewNode mRoot; - - WindowNode(AssistStructure assist, ViewRootImpl root) { - View view = root.getView(); - Rect rect = new Rect(); - view.getBoundsOnScreen(rect); - mX = rect.left - view.getLeft(); - mY = rect.top - view.getTop(); - mWidth = rect.width(); - mHeight = rect.height(); - mTitle = root.getTitle(); - mDisplayId = root.getDisplayId(); - mRoot = new ViewNode(); - ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false); - if ((root.getWindowFlags()&WindowManager.LayoutParams.FLAG_SECURE) != 0) { - // This is a secure window, so it doesn't want a screenshot, and that - // means we should also not copy out its view hierarchy. - view.onProvideStructure(builder); - builder.setAssistBlocked(true); - return; - } - view.dispatchProvideStructure(builder); - } - - WindowNode(Parcel in, PooledStringReader preader) { - mX = in.readInt(); - mY = in.readInt(); - mWidth = in.readInt(); - mHeight = in.readInt(); - mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); - mDisplayId = in.readInt(); - mRoot = new ViewNode(in, preader); - } - - void writeToParcel(Parcel out, PooledStringWriter pwriter) { - out.writeInt(mX); - out.writeInt(mY); - out.writeInt(mWidth); - out.writeInt(mHeight); - TextUtils.writeToParcel(mTitle, out, 0); - out.writeInt(mDisplayId); - mRoot.writeToParcel(out, pwriter); - } - - /** - * Returns the left edge of the window, in pixels, relative to the left - * edge of the screen. - */ - public int getLeft() { - return mX; - } - - /** - * Returns the top edge of the window, in pixels, relative to the top - * edge of the screen. - */ - public int getTop() { - return mY; - } - - /** - * Returns the total width of the window in pixels. - */ - public int getWidth() { - return mWidth; - } - - /** - * Returns the total height of the window in pixels. - */ - public int getHeight() { - return mHeight; - } - - /** - * Returns the title associated with the window, if it has one. - */ - public CharSequence getTitle() { - return mTitle; - } - - /** - * Returns the ID of the display this window is on, for use with - * {@link android.hardware.display.DisplayManager#getDisplay DisplayManager.getDisplay()}. - */ - public int getDisplayId() { - return mDisplayId; - } - - /** - * Returns the {@link ViewNode} containing the root content of the window. - */ - public ViewNode getRootViewNode() { - return mRoot; - } - } - - /** - * Describes a single view in the assist data. - */ - static public class ViewNode { - /** - * Magic value for text color that has not been defined, which is very unlikely - * to be confused with a real text color. - */ - public static final int TEXT_COLOR_UNDEFINED = 1; - - public static final int TEXT_STYLE_BOLD = 1<<0; - public static final int TEXT_STYLE_ITALIC = 1<<1; - public static final int TEXT_STYLE_UNDERLINE = 1<<2; - public static final int TEXT_STYLE_STRIKE_THRU = 1<<3; - - int mId; - String mIdPackage; - String mIdType; - String mIdEntry; - int mX; - int mY; - int mScrollX; - int mScrollY; - int mWidth; - int mHeight; - - static final int FLAGS_DISABLED = 0x00000001; - static final int FLAGS_VISIBILITY_MASK = View.VISIBLE|View.INVISIBLE|View.GONE; - static final int FLAGS_FOCUSABLE = 0x00000010; - static final int FLAGS_FOCUSED = 0x00000020; - static final int FLAGS_ACCESSIBILITY_FOCUSED = 0x04000000; - static final int FLAGS_SELECTED = 0x00000040; - static final int FLAGS_ASSIST_BLOCKED = 0x00000080; - static final int FLAGS_ACTIVATED = 0x40000000; - static final int FLAGS_CHECKABLE = 0x00000100; - static final int FLAGS_CHECKED = 0x00000200; - static final int FLAGS_CLICKABLE = 0x00004000; - static final int FLAGS_LONG_CLICKABLE = 0x00200000; - static final int FLAGS_CONTEXT_CLICKABLE = 0x00400000; - - int mFlags; - - String mClassName; - CharSequence mContentDescription; - - ViewNodeText mText; - Bundle mExtras; - - ViewNode[] mChildren; - - ViewNode() { - } - - ViewNode(Parcel in, PooledStringReader preader) { - mId = in.readInt(); - if (mId != 0) { - mIdEntry = preader.readString(); - if (mIdEntry != null) { - mIdType = preader.readString(); - mIdPackage = preader.readString(); - } else { - mIdPackage = mIdType = null; - } - } else { - mIdPackage = mIdType = mIdEntry = null; - } - mX = in.readInt(); - mY = in.readInt(); - mScrollX = in.readInt(); - mScrollY = in.readInt(); - mWidth = in.readInt(); - mHeight = in.readInt(); - mFlags = in.readInt(); - mClassName = preader.readString(); - mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); - if (in.readInt() != 0) { - mText = new ViewNodeText(in); - } else { - mText = null; - } - mExtras = in.readBundle(); - final int NCHILDREN = in.readInt(); - if (NCHILDREN > 0) { - mChildren = new ViewNode[NCHILDREN]; - for (int i=0; i<NCHILDREN; i++) { - mChildren[i] = new ViewNode(in, preader); - } - } else { - mChildren = null; - } - } - - void writeToParcel(Parcel out, PooledStringWriter pwriter) { - out.writeInt(mId); - if (mId != 0) { - pwriter.writeString(mIdEntry); - if (mIdEntry != null) { - pwriter.writeString(mIdType); - pwriter.writeString(mIdPackage); - } - } - out.writeInt(mX); - out.writeInt(mY); - out.writeInt(mScrollX); - out.writeInt(mScrollY); - out.writeInt(mWidth); - out.writeInt(mHeight); - out.writeInt(mFlags); - pwriter.writeString(mClassName); - TextUtils.writeToParcel(mContentDescription, out, 0); - if (mText != null) { - out.writeInt(1); - mText.writeToParcel(out); - } else { - out.writeInt(0); - } - out.writeBundle(mExtras); - if (mChildren != null) { - final int NCHILDREN = mChildren.length; - out.writeInt(NCHILDREN); - for (int i=0; i<NCHILDREN; i++) { - mChildren[i].writeToParcel(out, pwriter); - } - } else { - out.writeInt(0); - } - } - - /** - * Returns the ID associated with this view, as per {@link View#getId() View.getId()}. - */ - public int getId() { - return mId; - } - - /** - * If {@link #getId()} is a resource identifier, this is the package name of that - * identifier. See {@link android.view.ViewStructure#setId ViewStructure.setId} - * for more information. - */ - public String getIdPackage() { - return mIdPackage; - } - - /** - * If {@link #getId()} is a resource identifier, this is the type name of that - * identifier. See {@link android.view.ViewStructure#setId ViewStructure.setId} - * for more information. - */ - public String getIdType() { - return mIdType; - } - - /** - * If {@link #getId()} is a resource identifier, this is the entry name of that - * identifier. See {@link android.view.ViewStructure#setId ViewStructure.setId} - * for more information. - */ - public String getIdEntry() { - return mIdEntry; - } - - /** - * Returns the left edge of this view, in pixels, relative to the left edge of its parent. - */ - public int getLeft() { - return mX; - } - - /** - * Returns the top edge of this view, in pixels, relative to the top edge of its parent. - */ - public int getTop() { - return mY; - } - - /** - * Returns the current X scroll offset of this view, as per - * {@link android.view.View#getScrollX() View.getScrollX()}. - */ - public int getScrollX() { - return mScrollX; - } - - /** - * Returns the current Y scroll offset of this view, as per - * {@link android.view.View#getScrollX() View.getScrollY()}. - */ - public int getScrollY() { - return mScrollY; - } - - /** - * Returns the width of this view, in pixels. - */ - public int getWidth() { - return mWidth; - } - - /** - * Returns the height of this view, in pixels. - */ - public int getHeight() { - return mHeight; - } - - /** - * Returns the visibility mode of this view, as per - * {@link android.view.View#getVisibility() View.getVisibility()}. - */ - public int getVisibility() { - return mFlags&ViewNode.FLAGS_VISIBILITY_MASK; - } - - /** - * Returns true if assist data has been blocked starting at this node in the hierarchy. - */ - public boolean isAssistBlocked() { - return (mFlags&ViewNode.FLAGS_ASSIST_BLOCKED) == 0; - } - - /** - * Returns true if this node is in an enabled state. - */ - public boolean isEnabled() { - return (mFlags&ViewNode.FLAGS_DISABLED) == 0; - } - - /** - * Returns true if this node is clickable by the user. - */ - public boolean isClickable() { - return (mFlags&ViewNode.FLAGS_CLICKABLE) != 0; - } - - /** - * Returns true if this node can take input focus. - */ - public boolean isFocusable() { - return (mFlags&ViewNode.FLAGS_FOCUSABLE) != 0; - } - - /** - * Returns true if this node currently had input focus at the time that the - * structure was collected. - */ - public boolean isFocused() { - return (mFlags&ViewNode.FLAGS_FOCUSED) != 0; - } - - /** - * Returns true if this node currently had accessibility focus at the time that the - * structure was collected. - */ - public boolean isAccessibilityFocused() { - return (mFlags&ViewNode.FLAGS_ACCESSIBILITY_FOCUSED) != 0; - } - - /** - * Returns true if this node represents something that is checkable by the user. - */ - public boolean isCheckable() { - return (mFlags&ViewNode.FLAGS_CHECKABLE) != 0; - } - - /** - * Returns true if this node is currently in a checked state. - */ - public boolean isChecked() { - return (mFlags&ViewNode.FLAGS_CHECKED) != 0; - } - - /** - * Returns true if this node has currently been selected by the user. - */ - public boolean isSelected() { - return (mFlags&ViewNode.FLAGS_SELECTED) != 0; - } - - /** - * Returns true if this node has currently been activated by the user. - */ - public boolean isActivated() { - return (mFlags&ViewNode.FLAGS_ACTIVATED) != 0; - } - - /** - * Returns true if this node is something the user can perform a long click/press on. - */ - public boolean isLongClickable() { - return (mFlags&ViewNode.FLAGS_LONG_CLICKABLE) != 0; - } - - /** - * Returns true if this node is something the user can perform a context click on. - */ - public boolean isContextClickable() { - return (mFlags&ViewNode.FLAGS_CONTEXT_CLICKABLE) != 0; - } - - /** - * Returns the class name of the node's implementation, indicating its behavior. - * For example, a button will report "android.widget.Button" meaning it behaves - * like a {@link android.widget.Button}. - */ - public String getClassName() { - return mClassName; - } - - /** - * Returns any content description associated with the node, which semantically describes - * its purpose for accessibility and other uses. - */ - public CharSequence getContentDescription() { - return mContentDescription; - } - - /** - * Returns any text associated with the node that is displayed to the user, or null - * if there is none. - */ - public CharSequence getText() { - return mText != null ? mText.mText : null; - } - - /** - * If {@link #getText()} is non-null, this is where the current selection starts. - */ - public int getTextSelectionStart() { - return mText != null ? mText.mTextSelectionStart : -1; - } - - /** - * If {@link #getText()} is non-null, this is where the current selection starts. - * If there is no selection, returns the same value as {@link #getTextSelectionStart()}, - * indicating the cursor position. - */ - public int getTextSelectionEnd() { - return mText != null ? mText.mTextSelectionEnd : -1; - } - - /** - * If {@link #getText()} is non-null, this is the main text color associated with it. - * If there is no text color, {@link #TEXT_COLOR_UNDEFINED} is returned. - * Note that the text may also contain style spans that modify the color of specific - * parts of the text. - */ - public int getTextColor() { - return mText != null ? mText.mTextColor : TEXT_COLOR_UNDEFINED; - } - - /** - * If {@link #getText()} is non-null, this is the main text background color associated - * with it. - * If there is no text background color, {@link #TEXT_COLOR_UNDEFINED} is returned. - * Note that the text may also contain style spans that modify the color of specific - * parts of the text. - */ - public int getTextBackgroundColor() { - return mText != null ? mText.mTextBackgroundColor : TEXT_COLOR_UNDEFINED; - } - - /** - * If {@link #getText()} is non-null, this is the main text size (in pixels) associated - * with it. - * Note that the text may also contain style spans that modify the size of specific - * parts of the text. - */ - public float getTextSize() { - return mText != null ? mText.mTextSize : 0; - } - - /** - * If {@link #getText()} is non-null, this is the main text style associated - * with it, containing a bit mask of {@link #TEXT_STYLE_BOLD}, - * {@link #TEXT_STYLE_BOLD}, {@link #TEXT_STYLE_STRIKE_THRU}, and/or - * {@link #TEXT_STYLE_UNDERLINE}. - * Note that the text may also contain style spans that modify the style of specific - * parts of the text. - */ - public int getTextStyle() { - return mText != null ? mText.mTextStyle : 0; - } - - /** - * Return additional hint text associated with the node; this is typically used with - * a node that takes user input, describing to the user what the input means. - */ - public String getHint() { - return mText != null ? mText.mHint : null; - } - - /** - * Return a Bundle containing optional vendor-specific extension information. - */ - public Bundle getExtras() { - return mExtras; - } - - /** - * Return the number of children this node has. - */ - public int getChildCount() { - return mChildren != null ? mChildren.length : 0; - } - - /** - * Return a child of this node, given an index value from 0 to - * {@link #getChildCount()}-1. - */ - public ViewNode getChildAt(int index) { - return mChildren[index]; - } - } - - static class ViewNodeBuilder extends ViewAssistStructure { - final AssistStructure mAssist; - final ViewNode mNode; - final boolean mAsync; - - ViewNodeBuilder(AssistStructure assist, ViewNode node, boolean async) { - mAssist = assist; - mNode = node; - mAsync = async; - } - - @Override - public void setId(int id, String packageName, String typeName, String entryName) { - mNode.mId = id; - mNode.mIdPackage = packageName; - mNode.mIdType = typeName; - mNode.mIdEntry = entryName; - } - - @Override - public void setDimens(int left, int top, int scrollX, int scrollY, int width, int height) { - mNode.mX = left; - mNode.mY = top; - mNode.mScrollX = scrollX; - mNode.mScrollY = scrollY; - mNode.mWidth = width; - mNode.mHeight = height; - } - - @Override - public void setVisibility(int visibility) { - mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_VISIBILITY_MASK) | visibility; - } - - @Override - public void setAssistBlocked(boolean state) { - mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ASSIST_BLOCKED) - | (state ? 0 : ViewNode.FLAGS_ASSIST_BLOCKED); - } - - @Override - public void setEnabled(boolean state) { - mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_DISABLED) - | (state ? 0 : ViewNode.FLAGS_DISABLED); - } - - @Override - public void setClickable(boolean state) { - mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CLICKABLE) - | (state ? ViewNode.FLAGS_CLICKABLE : 0); - } - - @Override - public void setLongClickable(boolean state) { - mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_LONG_CLICKABLE) - | (state ? ViewNode.FLAGS_LONG_CLICKABLE : 0); - } - - @Override - public void setContextClickable(boolean state) { - mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CONTEXT_CLICKABLE) - | (state ? ViewNode.FLAGS_CONTEXT_CLICKABLE : 0); - } - - @Override - public void setFocusable(boolean state) { - mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_FOCUSABLE) - | (state ? ViewNode.FLAGS_FOCUSABLE : 0); - } - - @Override - public void setFocused(boolean state) { - mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_FOCUSED) - | (state ? ViewNode.FLAGS_FOCUSED : 0); - } - - @Override - public void setAccessibilityFocused(boolean state) { - mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ACCESSIBILITY_FOCUSED) - | (state ? ViewNode.FLAGS_ACCESSIBILITY_FOCUSED : 0); - } - - @Override - public void setCheckable(boolean state) { - mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CHECKABLE) - | (state ? ViewNode.FLAGS_CHECKABLE : 0); - } - - @Override - public void setChecked(boolean state) { - mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CHECKED) - | (state ? ViewNode.FLAGS_CHECKED : 0); - } - - @Override - public void setSelected(boolean state) { - mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_SELECTED) - | (state ? ViewNode.FLAGS_SELECTED : 0); - } - - @Override - public void setActivated(boolean state) { - mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ACTIVATED) - | (state ? ViewNode.FLAGS_ACTIVATED : 0); - } - - @Override - public void setClassName(String className) { - mNode.mClassName = className; - } - - @Override - public void setContentDescription(CharSequence contentDescription) { - mNode.mContentDescription = contentDescription; - } - - private final ViewNodeText getNodeText() { - if (mNode.mText != null) { - return mNode.mText; - } - mNode.mText = new ViewNodeText(); - return mNode.mText; - } - - @Override - public void setText(CharSequence text) { - ViewNodeText t = getNodeText(); - t.mText = text; - t.mTextSelectionStart = t.mTextSelectionEnd = -1; - } - - @Override - public void setText(CharSequence text, int selectionStart, int selectionEnd) { - ViewNodeText t = getNodeText(); - t.mText = text; - t.mTextSelectionStart = selectionStart; - t.mTextSelectionEnd = selectionEnd; - } - - @Override - public void setTextStyle(float size, int fgColor, int bgColor, int style) { - ViewNodeText t = getNodeText(); - t.mTextColor = fgColor; - t.mTextBackgroundColor = bgColor; - t.mTextSize = size; - t.mTextStyle = style; - } - - @Override - public void setHint(CharSequence hint) { - getNodeText().mHint = hint != null ? hint.toString() : null; - } - - @Override - public CharSequence getText() { - return mNode.mText != null ? mNode.mText.mText : null; - } - - @Override - public int getTextSelectionStart() { - return mNode.mText != null ? mNode.mText.mTextSelectionStart : -1; - } - - @Override - public int getTextSelectionEnd() { - return mNode.mText != null ? mNode.mText.mTextSelectionEnd : -1; - } - - @Override - public CharSequence getHint() { - return mNode.mText != null ? mNode.mText.mHint : null; - } - - @Override - public Bundle getExtras() { - if (mNode.mExtras != null) { - return mNode.mExtras; - } - mNode.mExtras = new Bundle(); - return mNode.mExtras; - } - - @Override - public boolean hasExtras() { - return mNode.mExtras != null; - } - - @Override - public void setChildCount(int num) { - mNode.mChildren = new ViewNode[num]; - } - - @Override - public int addChildCount(int num) { - if (mNode.mChildren == null) { - setChildCount(num); - return 0; - } - final int start = mNode.mChildren.length; - ViewNode[] newArray = new ViewNode[start + num]; - System.arraycopy(mNode.mChildren, 0, newArray, 0, start); - mNode.mChildren = newArray; - return start; - } - - @Override - public int getChildCount() { - return mNode.mChildren != null ? mNode.mChildren.length : 0; - } - - @Override - public ViewAssistStructure newChild(int index) { - ViewNode node = new ViewNode(); - mNode.mChildren[index] = node; - return new ViewNodeBuilder(mAssist, node, false); - } - - @Override - public ViewAssistStructure asyncNewChild(int index) { - synchronized (mAssist) { - ViewNode node = new ViewNode(); - mNode.mChildren[index] = node; - ViewNodeBuilder builder = new ViewNodeBuilder(mAssist, node, true); - mAssist.mPendingAsyncChildren.add(builder); - return builder; - } - } - - @Override - public void asyncCommit() { - synchronized (mAssist) { - if (!mAsync) { - throw new IllegalStateException("Child " + this - + " was not created with ViewAssistStructure.asyncNewChild"); - } - if (!mAssist.mPendingAsyncChildren.remove(this)) { - throw new IllegalStateException("Child " + this + " already committed"); - } - mAssist.notifyAll(); - } - } - - @Override - public Rect getTempRect() { - return mAssist.mTmpRect; - } - } - - /** @hide */ - public AssistStructure(Activity activity) { - mHaveData = true; - mActivityComponent = activity.getComponentName(); - ArrayList<ViewRootImpl> views = WindowManagerGlobal.getInstance().getRootViews( - activity.getActivityToken()); - for (int i=0; i<views.size(); i++) { - ViewRootImpl root = views.get(i); - mWindowNodes.add(new WindowNode(this, root)); - } - } - - public AssistStructure() { - mHaveData = true; - mActivityComponent = null; - } - - /** @hide */ - public AssistStructure(Parcel in) { - mReceiveChannel = in.readStrongBinder(); - } - - /** @hide */ - public void dump() { - Log.i(TAG, "Activity: " + mActivityComponent.flattenToShortString()); - final int N = getWindowNodeCount(); - for (int i=0; i<N; i++) { - WindowNode node = getWindowNodeAt(i); - Log.i(TAG, "Window #" + i + " [" + node.getLeft() + "," + node.getTop() - + " " + node.getWidth() + "x" + node.getHeight() + "]" + " " + node.getTitle()); - dump(" ", node.getRootViewNode()); - } - } - - void dump(String prefix, ViewNode node) { - Log.i(TAG, prefix + "View [" + node.getLeft() + "," + node.getTop() - + " " + node.getWidth() + "x" + node.getHeight() + "]" + " " + node.getClassName()); - int id = node.getId(); - if (id != 0) { - StringBuilder sb = new StringBuilder(); - sb.append(prefix); sb.append(" ID: #"); sb.append(Integer.toHexString(id)); - String entry = node.getIdEntry(); - if (entry != null) { - String type = node.getIdType(); - String pkg = node.getIdPackage(); - sb.append(" "); sb.append(pkg); sb.append(":"); sb.append(type); - sb.append("/"); sb.append(entry); - } - Log.i(TAG, sb.toString()); - } - int scrollX = node.getScrollX(); - int scrollY = node.getScrollY(); - if (scrollX != 0 || scrollY != 0) { - Log.i(TAG, prefix + " Scroll: " + scrollX + "," + scrollY); - } - CharSequence contentDescription = node.getContentDescription(); - if (contentDescription != null) { - Log.i(TAG, prefix + " Content description: " + contentDescription); - } - CharSequence text = node.getText(); - if (text != null) { - Log.i(TAG, prefix + " Text (sel " + node.getTextSelectionStart() + "-" - + node.getTextSelectionEnd() + "): " + text); - Log.i(TAG, prefix + " Text size: " + node.getTextSize() + " , style: #" - + node.getTextStyle()); - Log.i(TAG, prefix + " Text color fg: #" + Integer.toHexString(node.getTextColor()) - + ", bg: #" + Integer.toHexString(node.getTextBackgroundColor())); - } - String hint = node.getHint(); - if (hint != null) { - Log.i(TAG, prefix + " Hint: " + hint); - } - Bundle extras = node.getExtras(); - if (extras != null) { - Log.i(TAG, prefix + " Extras: " + extras); - } - final int NCHILDREN = node.getChildCount(); - if (NCHILDREN > 0) { - Log.i(TAG, prefix + " Children:"); - String cprefix = prefix + " "; - for (int i=0; i<NCHILDREN; i++) { - ViewNode cnode = node.getChildAt(i); - dump(cprefix, cnode); - } - } - } - - /** - * @hide - * Retrieve the framework-generated AssistStructure that is stored within - * the Bundle filled in by {@link Activity#onProvideAssistData}. - */ - public static android.app.assist.AssistStructure getAssistStructure(Bundle assistBundle) { - return assistBundle.getParcelable(ASSIST_KEY); - } - - /** - * Return the activity this AssistStructure came from. - */ - public ComponentName getActivityComponent() { - ensureData(); - return mActivityComponent; - } - - /** - * Return the number of window contents that have been collected in this assist data. - */ - public int getWindowNodeCount() { - ensureData(); - return mWindowNodes.size(); - } - - /** - * Return one of the windows in the assist data. - * @param index Which window to retrieve, may be 0 to {@link #getWindowNodeCount()}-1. - * @hide - */ - public WindowNode getWindowNodeAt(int index) { - ensureData(); - return mWindowNodes.get(index); - } - - /** @hide */ - public void ensureData() { - if (mHaveData) { - return; - } - mHaveData = true; - Parcel data = Parcel.obtain(); - Parcel reply = Parcel.obtain(); - data.writeInterfaceToken(DESCRIPTOR); - try { - mReceiveChannel.transact(TRANSACTION_XFER, data, reply, 0); - } catch (RemoteException e) { - Log.w(TAG, "Failure reading AssistStructure data", e); - return; - } - readContentFromParcel(reply); - data.recycle(); - reply.recycle(); - } - - void writeContentToParcel(Parcel out, int flags) { - // First make sure all content has been created. - boolean skipStructure = false; - synchronized (this) { - long endTime = SystemClock.uptimeMillis() + 5000; - long now; - while (mPendingAsyncChildren.size() > 0 && (now=SystemClock.uptimeMillis()) < endTime) { - try { - wait(endTime-now); - } catch (InterruptedException e) { - } - } - if (mPendingAsyncChildren.size() > 0) { - // We waited too long, assume none of the assist structure is valid. - skipStructure = true; - } - } - int start = out.dataPosition(); - PooledStringWriter pwriter = new PooledStringWriter(out); - ComponentName.writeToParcel(mActivityComponent, out); - final int N = skipStructure ? 0 : mWindowNodes.size(); - out.writeInt(N); - for (int i=0; i<N; i++) { - mWindowNodes.get(i).writeToParcel(out, pwriter); - } - pwriter.finish(); - Log.i(TAG, "Flattened assist data: " + (out.dataPosition() - start) + " bytes"); - } - - void readContentFromParcel(Parcel in) { - PooledStringReader preader = new PooledStringReader(in); - mActivityComponent = ComponentName.readFromParcel(in); - final int N = in.readInt(); - for (int i=0; i<N; i++) { - mWindowNodes.add(new WindowNode(in, preader)); - } - //dump(); - } -} diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java index 9cc399d..abb8244 100644 --- a/core/java/android/app/VoiceInteractor.java +++ b/core/java/android/app/VoiceInteractor.java @@ -225,6 +225,9 @@ public final class VoiceInteractor { * Cancel this active request. */ public void cancel() { + if (mRequestInterface == null) { + throw new IllegalStateException("Request " + this + " is no longer active"); + } try { mRequestInterface.cancel(); } catch (RemoteException e) { diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 125708a..4d1cff5 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -3972,6 +3972,7 @@ public class DevicePolicyManager { * <li>{@link Settings.Global#STAY_ON_WHILE_PLUGGED_IN} * This setting is only available from {@link android.os.Build.VERSION_CODES#MNC} onwards * and can only be set if {@link #setMaximumTimeToLock} is not used to set a timeout.</li> + * <li>{@link Settings.Global#WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN}</li> * </ul> * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. @@ -4349,6 +4350,12 @@ public class DevicePolicyManager { * group that the runtime permission belongs to. This method can only be called * by a profile or device owner. * + * <p/>Setting the grant state to {@link #PERMISSION_GRANT_STATE_DEFAULT default} does not + * revoke the permission. It retains the previous grant, if any. + * + * <p/>Permissions can be granted or revoked only for applications built with a + * {@code targetSdkVersion} of {@link android.os.Build.VERSION_CODES#MNC} or later. + * * @param admin Which profile or device owner this request is associated with. * @param packageName The application to grant or revoke a permission to. * @param permission The permission to grant or revoke. diff --git a/core/java/android/app/assist/AssistContent.java b/core/java/android/app/assist/AssistContent.java index c7e7330..07b2d57 100644 --- a/core/java/android/app/assist/AssistContent.java +++ b/core/java/android/app/assist/AssistContent.java @@ -1,24 +1,184 @@ package android.app.assist; +import android.content.ClipData; import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; /** - * New home for AssistContent. + * Holds information about the content an application is viewing, to hand to an + * assistant at the user's request. This is filled in by + * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}. */ -public final class AssistContent extends android.app.AssistContent implements Parcelable { +@Deprecated +public class AssistContent implements Parcelable { + private boolean mIsAppProvidedIntent = false; + private Intent mIntent; + private String mStructuredData; + private ClipData mClipData; + private Uri mUri; + private final Bundle mExtras; - /** @hide */ public AssistContent() { + mExtras = new Bundle(); } - public AssistContent(Parcel in) { - super(in); + /** + * @hide + * Called by {@link android.app.ActivityThread} to set the default Intent based on + * {@link android.app.Activity#getIntent Activity.getIntent}. + * + * <p>Automatically populates {@link #mUri} if that Intent is an {@link Intent#ACTION_VIEW} + * of a web (http or https scheme) URI.</p> + */ + public void setDefaultIntent(Intent intent) { + mIntent = intent; + setWebUri(null); + if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) { + Uri uri = intent.getData(); + if (uri != null) { + if ("http".equals(uri.getScheme()) || "https".equals(uri.getScheme())) { + setWebUri(uri); + } + } + } + } + + /** + * Sets the Intent associated with the content, describing the current top-level context of + * the activity. If this contains a reference to a piece of data related to the activity, + * be sure to set {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} so the accessibility + * service can access it. + */ + public void setIntent(Intent intent) { + mIsAppProvidedIntent = true; + mIntent = intent; } + /** + * Returns the current {@link #setIntent} if one is set, else the default Intent obtained from + * {@link android.app.Activity#getIntent Activity.getIntent}. Can be modified in-place. + */ public Intent getIntent() { - return super.getIntent(); + return mIntent; + } + + /** + * Returns whether or not the current Intent was explicitly provided in + * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}. If not, + * the Intent was automatically set based on + * {@link android.app.Activity#getIntent Activity.getIntent}. + */ + public boolean isAppProvidedIntent() { + return mIsAppProvidedIntent; + } + + /** + * Optional additional content items that are involved with + * the current UI. Access to this content will be granted to the assistant as if you + * are sending it through an Intent with {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}. + */ + public void setClipData(ClipData clip) { + mClipData = clip; + } + + /** + * Return the current {@link #setClipData}, which you can modify in-place. + */ + public ClipData getClipData() { + return mClipData; + } + + /** + * Sets optional structured data regarding the content being viewed. The provided data + * must be a string represented with <a href="http://json-ld.org/">JSON-LD</a> using the + * <a href="http://schema.org/">schema.org</a> vocabulary. + */ + public void setStructuredData(String structuredData) { + mStructuredData = structuredData; + } + + /** + * Returns the current {@link #setStructuredData}. + */ + public String getStructuredData() { + return mStructuredData; + } + + /** + * Set a web URI associated with the current data being shown to the user. + * This URI could be opened in a web browser, or in the app as an + * {@link Intent#ACTION_VIEW} Intent, to show the same data that is currently + * being displayed by it. The URI here should be something that is transportable + * off the device into other environments to acesss the same data as is currently + * being shown in the app; if the app does not have such a representation, it should + * leave the null and only report the local intent and clip data. + */ + public void setWebUri(Uri uri) { + mUri = uri; + } + + /** + * Return the content's web URI as per {@link #setWebUri(android.net.Uri)}, or null if + * there is none. + */ + public Uri getWebUri() { + return mUri; + } + + /** + * Return Bundle for extra vendor-specific data that can be modified and examined. + */ + public Bundle getExtras() { + return mExtras; + } + + AssistContent(Parcel in) { + if (in.readInt() != 0) { + mIntent = Intent.CREATOR.createFromParcel(in); + } + if (in.readInt() != 0) { + mClipData = ClipData.CREATOR.createFromParcel(in); + } + if (in.readInt() != 0) { + mUri = Uri.CREATOR.createFromParcel(in); + } + if (in.readInt() != 0) { + mStructuredData = in.readString(); + } + mIsAppProvidedIntent = in.readInt() == 1; + mExtras = in.readBundle(); + } + + void writeToParcelInternal(Parcel dest, int flags) { + if (mIntent != null) { + dest.writeInt(1); + mIntent.writeToParcel(dest, flags); + } else { + dest.writeInt(0); + } + if (mClipData != null) { + dest.writeInt(1); + mClipData.writeToParcel(dest, flags); + } else { + dest.writeInt(0); + } + if (mUri != null) { + dest.writeInt(1); + mUri.writeToParcel(dest, flags); + } else { + dest.writeInt(0); + } + if (mStructuredData != null) { + dest.writeInt(1); + dest.writeString(mStructuredData); + } else { + dest.writeInt(0); + } + dest.writeInt(mIsAppProvidedIntent ? 1 : 0); + dest.writeBundle(mExtras); } @Override diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 1677e95..1a04895 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -1,28 +1,1038 @@ package android.app.assist; import android.app.Activity; +import android.content.ComponentName; +import android.graphics.Rect; +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; +import android.os.PooledStringReader; +import android.os.PooledStringWriter; +import android.os.RemoteException; +import android.os.SystemClock; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; +import android.view.ViewAssistStructure; +import android.view.ViewRootImpl; +import android.view.WindowManager; +import android.view.WindowManagerGlobal; + +import java.util.ArrayList; /** - * New home for AssistStructure. + * Assist data automatically created by the platform's implementation + * of {@link android.app.Activity#onProvideAssistData}. */ -public final class AssistStructure extends android.app.AssistStructure implements Parcelable { +public class AssistStructure implements Parcelable { + static final String TAG = "AssistStructure"; - public AssistStructure() { + boolean mHaveData; + + ComponentName mActivityComponent; + + final ArrayList<WindowNode> mWindowNodes = new ArrayList<>(); + + final ArrayList<ViewNodeBuilder> mPendingAsyncChildren = new ArrayList<>(); + + SendChannel mSendChannel; + IBinder mReceiveChannel; + + Rect mTmpRect = new Rect(); + + static final int TRANSACTION_XFER = Binder.FIRST_CALL_TRANSACTION+1; + static final String DESCRIPTOR = "android.app.AssistStructure"; + + final class SendChannel extends Binder { + @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + if (code == TRANSACTION_XFER) { + data.enforceInterface(DESCRIPTOR); + writeContentToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); + return true; + } else { + return super.onTransact(code, data, reply, flags); + } + } + } + + final static class ViewNodeText { + CharSequence mText; + int mTextSelectionStart; + int mTextSelectionEnd; + int mTextColor; + int mTextBackgroundColor; + float mTextSize; + int mTextStyle; + String mHint; + + ViewNodeText() { + } + + ViewNodeText(Parcel in) { + mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + mTextSelectionStart = in.readInt(); + mTextSelectionEnd = in.readInt(); + mTextColor = in.readInt(); + mTextBackgroundColor = in.readInt(); + mTextSize = in.readFloat(); + mTextStyle = in.readInt(); + mHint = in.readString(); + } + + void writeToParcel(Parcel out) { + TextUtils.writeToParcel(mText, out, 0); + out.writeInt(mTextSelectionStart); + out.writeInt(mTextSelectionEnd); + out.writeInt(mTextColor); + out.writeInt(mTextBackgroundColor); + out.writeFloat(mTextSize); + out.writeInt(mTextStyle); + out.writeString(mHint); + } + } + + /** + * Describes a window in the assist data. + */ + static public class WindowNode { + final int mX; + final int mY; + final int mWidth; + final int mHeight; + final CharSequence mTitle; + final int mDisplayId; + final ViewNode mRoot; + + WindowNode(AssistStructure assist, ViewRootImpl root) { + View view = root.getView(); + Rect rect = new Rect(); + view.getBoundsOnScreen(rect); + mX = rect.left - view.getLeft(); + mY = rect.top - view.getTop(); + mWidth = rect.width(); + mHeight = rect.height(); + mTitle = root.getTitle(); + mDisplayId = root.getDisplayId(); + mRoot = new ViewNode(); + ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false); + if ((root.getWindowFlags()& WindowManager.LayoutParams.FLAG_SECURE) != 0) { + // This is a secure window, so it doesn't want a screenshot, and that + // means we should also not copy out its view hierarchy. + view.onProvideStructure(builder); + builder.setAssistBlocked(true); + return; + } + view.dispatchProvideStructure(builder); + } + + WindowNode(Parcel in, PooledStringReader preader) { + mX = in.readInt(); + mY = in.readInt(); + mWidth = in.readInt(); + mHeight = in.readInt(); + mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + mDisplayId = in.readInt(); + mRoot = new ViewNode(in, preader); + } + + void writeToParcel(Parcel out, PooledStringWriter pwriter) { + out.writeInt(mX); + out.writeInt(mY); + out.writeInt(mWidth); + out.writeInt(mHeight); + TextUtils.writeToParcel(mTitle, out, 0); + out.writeInt(mDisplayId); + mRoot.writeToParcel(out, pwriter); + } + + /** + * Returns the left edge of the window, in pixels, relative to the left + * edge of the screen. + */ + public int getLeft() { + return mX; + } + + /** + * Returns the top edge of the window, in pixels, relative to the top + * edge of the screen. + */ + public int getTop() { + return mY; + } + + /** + * Returns the total width of the window in pixels. + */ + public int getWidth() { + return mWidth; + } + + /** + * Returns the total height of the window in pixels. + */ + public int getHeight() { + return mHeight; + } + + /** + * Returns the title associated with the window, if it has one. + */ + public CharSequence getTitle() { + return mTitle; + } + + /** + * Returns the ID of the display this window is on, for use with + * {@link android.hardware.display.DisplayManager#getDisplay DisplayManager.getDisplay()}. + */ + public int getDisplayId() { + return mDisplayId; + } + + /** + * Returns the {@link ViewNode} containing the root content of the window. + */ + public ViewNode getRootViewNode() { + return mRoot; + } + } + + /** + * Describes a single view in the assist data. + */ + static public class ViewNode { + /** + * Magic value for text color that has not been defined, which is very unlikely + * to be confused with a real text color. + */ + public static final int TEXT_COLOR_UNDEFINED = 1; + + public static final int TEXT_STYLE_BOLD = 1<<0; + public static final int TEXT_STYLE_ITALIC = 1<<1; + public static final int TEXT_STYLE_UNDERLINE = 1<<2; + public static final int TEXT_STYLE_STRIKE_THRU = 1<<3; + + int mId; + String mIdPackage; + String mIdType; + String mIdEntry; + int mX; + int mY; + int mScrollX; + int mScrollY; + int mWidth; + int mHeight; + + static final int FLAGS_DISABLED = 0x00000001; + static final int FLAGS_VISIBILITY_MASK = View.VISIBLE|View.INVISIBLE|View.GONE; + static final int FLAGS_FOCUSABLE = 0x00000010; + static final int FLAGS_FOCUSED = 0x00000020; + static final int FLAGS_ACCESSIBILITY_FOCUSED = 0x04000000; + static final int FLAGS_SELECTED = 0x00000040; + static final int FLAGS_ASSIST_BLOCKED = 0x00000080; + static final int FLAGS_ACTIVATED = 0x40000000; + static final int FLAGS_CHECKABLE = 0x00000100; + static final int FLAGS_CHECKED = 0x00000200; + static final int FLAGS_CLICKABLE = 0x00004000; + static final int FLAGS_LONG_CLICKABLE = 0x00200000; + static final int FLAGS_CONTEXT_CLICKABLE = 0x00400000; + + int mFlags; + + String mClassName; + CharSequence mContentDescription; + + ViewNodeText mText; + Bundle mExtras; + + ViewNode[] mChildren; + + ViewNode() { + } + + ViewNode(Parcel in, PooledStringReader preader) { + mId = in.readInt(); + if (mId != 0) { + mIdEntry = preader.readString(); + if (mIdEntry != null) { + mIdType = preader.readString(); + mIdPackage = preader.readString(); + } else { + mIdPackage = mIdType = null; + } + } else { + mIdPackage = mIdType = mIdEntry = null; + } + mX = in.readInt(); + mY = in.readInt(); + mScrollX = in.readInt(); + mScrollY = in.readInt(); + mWidth = in.readInt(); + mHeight = in.readInt(); + mFlags = in.readInt(); + mClassName = preader.readString(); + mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + if (in.readInt() != 0) { + mText = new ViewNodeText(in); + } else { + mText = null; + } + mExtras = in.readBundle(); + final int NCHILDREN = in.readInt(); + if (NCHILDREN > 0) { + mChildren = new ViewNode[NCHILDREN]; + for (int i=0; i<NCHILDREN; i++) { + mChildren[i] = new ViewNode(in, preader); + } + } else { + mChildren = null; + } + } + + void writeToParcel(Parcel out, PooledStringWriter pwriter) { + out.writeInt(mId); + if (mId != 0) { + pwriter.writeString(mIdEntry); + if (mIdEntry != null) { + pwriter.writeString(mIdType); + pwriter.writeString(mIdPackage); + } + } + out.writeInt(mX); + out.writeInt(mY); + out.writeInt(mScrollX); + out.writeInt(mScrollY); + out.writeInt(mWidth); + out.writeInt(mHeight); + out.writeInt(mFlags); + pwriter.writeString(mClassName); + TextUtils.writeToParcel(mContentDescription, out, 0); + if (mText != null) { + out.writeInt(1); + mText.writeToParcel(out); + } else { + out.writeInt(0); + } + out.writeBundle(mExtras); + if (mChildren != null) { + final int NCHILDREN = mChildren.length; + out.writeInt(NCHILDREN); + for (int i=0; i<NCHILDREN; i++) { + mChildren[i].writeToParcel(out, pwriter); + } + } else { + out.writeInt(0); + } + } + + /** + * Returns the ID associated with this view, as per {@link View#getId() View.getId()}. + */ + public int getId() { + return mId; + } + + /** + * If {@link #getId()} is a resource identifier, this is the package name of that + * identifier. See {@link android.view.ViewStructure#setId ViewStructure.setId} + * for more information. + */ + public String getIdPackage() { + return mIdPackage; + } + + /** + * If {@link #getId()} is a resource identifier, this is the type name of that + * identifier. See {@link android.view.ViewStructure#setId ViewStructure.setId} + * for more information. + */ + public String getIdType() { + return mIdType; + } + + /** + * If {@link #getId()} is a resource identifier, this is the entry name of that + * identifier. See {@link android.view.ViewStructure#setId ViewStructure.setId} + * for more information. + */ + public String getIdEntry() { + return mIdEntry; + } + + /** + * Returns the left edge of this view, in pixels, relative to the left edge of its parent. + */ + public int getLeft() { + return mX; + } + + /** + * Returns the top edge of this view, in pixels, relative to the top edge of its parent. + */ + public int getTop() { + return mY; + } + + /** + * Returns the current X scroll offset of this view, as per + * {@link android.view.View#getScrollX() View.getScrollX()}. + */ + public int getScrollX() { + return mScrollX; + } + + /** + * Returns the current Y scroll offset of this view, as per + * {@link android.view.View#getScrollX() View.getScrollY()}. + */ + public int getScrollY() { + return mScrollY; + } + + /** + * Returns the width of this view, in pixels. + */ + public int getWidth() { + return mWidth; + } + + /** + * Returns the height of this view, in pixels. + */ + public int getHeight() { + return mHeight; + } + + /** + * Returns the visibility mode of this view, as per + * {@link android.view.View#getVisibility() View.getVisibility()}. + */ + public int getVisibility() { + return mFlags&ViewNode.FLAGS_VISIBILITY_MASK; + } + + /** + * Returns true if assist data has been blocked starting at this node in the hierarchy. + */ + public boolean isAssistBlocked() { + return (mFlags&ViewNode.FLAGS_ASSIST_BLOCKED) == 0; + } + + /** + * Returns true if this node is in an enabled state. + */ + public boolean isEnabled() { + return (mFlags&ViewNode.FLAGS_DISABLED) == 0; + } + + /** + * Returns true if this node is clickable by the user. + */ + public boolean isClickable() { + return (mFlags&ViewNode.FLAGS_CLICKABLE) != 0; + } + + /** + * Returns true if this node can take input focus. + */ + public boolean isFocusable() { + return (mFlags&ViewNode.FLAGS_FOCUSABLE) != 0; + } + + /** + * Returns true if this node currently had input focus at the time that the + * structure was collected. + */ + public boolean isFocused() { + return (mFlags&ViewNode.FLAGS_FOCUSED) != 0; + } + + /** + * Returns true if this node currently had accessibility focus at the time that the + * structure was collected. + */ + public boolean isAccessibilityFocused() { + return (mFlags&ViewNode.FLAGS_ACCESSIBILITY_FOCUSED) != 0; + } + + /** + * Returns true if this node represents something that is checkable by the user. + */ + public boolean isCheckable() { + return (mFlags&ViewNode.FLAGS_CHECKABLE) != 0; + } + + /** + * Returns true if this node is currently in a checked state. + */ + public boolean isChecked() { + return (mFlags&ViewNode.FLAGS_CHECKED) != 0; + } + + /** + * Returns true if this node has currently been selected by the user. + */ + public boolean isSelected() { + return (mFlags&ViewNode.FLAGS_SELECTED) != 0; + } + + /** + * Returns true if this node has currently been activated by the user. + */ + public boolean isActivated() { + return (mFlags&ViewNode.FLAGS_ACTIVATED) != 0; + } + + /** + * Returns true if this node is something the user can perform a long click/press on. + */ + public boolean isLongClickable() { + return (mFlags&ViewNode.FLAGS_LONG_CLICKABLE) != 0; + } + + /** + * Returns true if this node is something the user can perform a context click on. + */ + public boolean isContextClickable() { + return (mFlags&ViewNode.FLAGS_CONTEXT_CLICKABLE) != 0; + } + + /** + * Returns the class name of the node's implementation, indicating its behavior. + * For example, a button will report "android.widget.Button" meaning it behaves + * like a {@link android.widget.Button}. + */ + public String getClassName() { + return mClassName; + } + + /** + * Returns any content description associated with the node, which semantically describes + * its purpose for accessibility and other uses. + */ + public CharSequence getContentDescription() { + return mContentDescription; + } + + /** + * Returns any text associated with the node that is displayed to the user, or null + * if there is none. + */ + public CharSequence getText() { + return mText != null ? mText.mText : null; + } + + /** + * If {@link #getText()} is non-null, this is where the current selection starts. + */ + public int getTextSelectionStart() { + return mText != null ? mText.mTextSelectionStart : -1; + } + + /** + * If {@link #getText()} is non-null, this is where the current selection starts. + * If there is no selection, returns the same value as {@link #getTextSelectionStart()}, + * indicating the cursor position. + */ + public int getTextSelectionEnd() { + return mText != null ? mText.mTextSelectionEnd : -1; + } + + /** + * If {@link #getText()} is non-null, this is the main text color associated with it. + * If there is no text color, {@link #TEXT_COLOR_UNDEFINED} is returned. + * Note that the text may also contain style spans that modify the color of specific + * parts of the text. + */ + public int getTextColor() { + return mText != null ? mText.mTextColor : TEXT_COLOR_UNDEFINED; + } + + /** + * If {@link #getText()} is non-null, this is the main text background color associated + * with it. + * If there is no text background color, {@link #TEXT_COLOR_UNDEFINED} is returned. + * Note that the text may also contain style spans that modify the color of specific + * parts of the text. + */ + public int getTextBackgroundColor() { + return mText != null ? mText.mTextBackgroundColor : TEXT_COLOR_UNDEFINED; + } + + /** + * If {@link #getText()} is non-null, this is the main text size (in pixels) associated + * with it. + * Note that the text may also contain style spans that modify the size of specific + * parts of the text. + */ + public float getTextSize() { + return mText != null ? mText.mTextSize : 0; + } + + /** + * If {@link #getText()} is non-null, this is the main text style associated + * with it, containing a bit mask of {@link #TEXT_STYLE_BOLD}, + * {@link #TEXT_STYLE_BOLD}, {@link #TEXT_STYLE_STRIKE_THRU}, and/or + * {@link #TEXT_STYLE_UNDERLINE}. + * Note that the text may also contain style spans that modify the style of specific + * parts of the text. + */ + public int getTextStyle() { + return mText != null ? mText.mTextStyle : 0; + } + + /** + * Return additional hint text associated with the node; this is typically used with + * a node that takes user input, describing to the user what the input means. + */ + public String getHint() { + return mText != null ? mText.mHint : null; + } + + /** + * Return a Bundle containing optional vendor-specific extension information. + */ + public Bundle getExtras() { + return mExtras; + } + + /** + * Return the number of children this node has. + */ + public int getChildCount() { + return mChildren != null ? mChildren.length : 0; + } + + /** + * Return a child of this node, given an index value from 0 to + * {@link #getChildCount()}-1. + */ + public ViewNode getChildAt(int index) { + return mChildren[index]; + } + } + + static class ViewNodeBuilder extends ViewAssistStructure { + final AssistStructure mAssist; + final ViewNode mNode; + final boolean mAsync; + + ViewNodeBuilder(AssistStructure assist, ViewNode node, boolean async) { + mAssist = assist; + mNode = node; + mAsync = async; + } + + @Override + public void setId(int id, String packageName, String typeName, String entryName) { + mNode.mId = id; + mNode.mIdPackage = packageName; + mNode.mIdType = typeName; + mNode.mIdEntry = entryName; + } + + @Override + public void setDimens(int left, int top, int scrollX, int scrollY, int width, int height) { + mNode.mX = left; + mNode.mY = top; + mNode.mScrollX = scrollX; + mNode.mScrollY = scrollY; + mNode.mWidth = width; + mNode.mHeight = height; + } + + @Override + public void setVisibility(int visibility) { + mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_VISIBILITY_MASK) | visibility; + } + + @Override + public void setAssistBlocked(boolean state) { + mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ASSIST_BLOCKED) + | (state ? 0 : ViewNode.FLAGS_ASSIST_BLOCKED); + } + + @Override + public void setEnabled(boolean state) { + mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_DISABLED) + | (state ? 0 : ViewNode.FLAGS_DISABLED); + } + + @Override + public void setClickable(boolean state) { + mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CLICKABLE) + | (state ? ViewNode.FLAGS_CLICKABLE : 0); + } + + @Override + public void setLongClickable(boolean state) { + mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_LONG_CLICKABLE) + | (state ? ViewNode.FLAGS_LONG_CLICKABLE : 0); + } + + @Override + public void setContextClickable(boolean state) { + mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CONTEXT_CLICKABLE) + | (state ? ViewNode.FLAGS_CONTEXT_CLICKABLE : 0); + } + + @Override + public void setFocusable(boolean state) { + mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_FOCUSABLE) + | (state ? ViewNode.FLAGS_FOCUSABLE : 0); + } + + @Override + public void setFocused(boolean state) { + mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_FOCUSED) + | (state ? ViewNode.FLAGS_FOCUSED : 0); + } + + @Override + public void setAccessibilityFocused(boolean state) { + mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ACCESSIBILITY_FOCUSED) + | (state ? ViewNode.FLAGS_ACCESSIBILITY_FOCUSED : 0); + } + + @Override + public void setCheckable(boolean state) { + mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CHECKABLE) + | (state ? ViewNode.FLAGS_CHECKABLE : 0); + } + + @Override + public void setChecked(boolean state) { + mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CHECKED) + | (state ? ViewNode.FLAGS_CHECKED : 0); + } + + @Override + public void setSelected(boolean state) { + mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_SELECTED) + | (state ? ViewNode.FLAGS_SELECTED : 0); + } + + @Override + public void setActivated(boolean state) { + mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ACTIVATED) + | (state ? ViewNode.FLAGS_ACTIVATED : 0); + } + + @Override + public void setClassName(String className) { + mNode.mClassName = className; + } + + @Override + public void setContentDescription(CharSequence contentDescription) { + mNode.mContentDescription = contentDescription; + } + + private final ViewNodeText getNodeText() { + if (mNode.mText != null) { + return mNode.mText; + } + mNode.mText = new ViewNodeText(); + return mNode.mText; + } + + @Override + public void setText(CharSequence text) { + ViewNodeText t = getNodeText(); + t.mText = text; + t.mTextSelectionStart = t.mTextSelectionEnd = -1; + } + + @Override + public void setText(CharSequence text, int selectionStart, int selectionEnd) { + ViewNodeText t = getNodeText(); + t.mText = text; + t.mTextSelectionStart = selectionStart; + t.mTextSelectionEnd = selectionEnd; + } + + @Override + public void setTextStyle(float size, int fgColor, int bgColor, int style) { + ViewNodeText t = getNodeText(); + t.mTextColor = fgColor; + t.mTextBackgroundColor = bgColor; + t.mTextSize = size; + t.mTextStyle = style; + } + + @Override + public void setHint(CharSequence hint) { + getNodeText().mHint = hint != null ? hint.toString() : null; + } + + @Override + public CharSequence getText() { + return mNode.mText != null ? mNode.mText.mText : null; + } + + @Override + public int getTextSelectionStart() { + return mNode.mText != null ? mNode.mText.mTextSelectionStart : -1; + } + + @Override + public int getTextSelectionEnd() { + return mNode.mText != null ? mNode.mText.mTextSelectionEnd : -1; + } + + @Override + public CharSequence getHint() { + return mNode.mText != null ? mNode.mText.mHint : null; + } + + @Override + public Bundle getExtras() { + if (mNode.mExtras != null) { + return mNode.mExtras; + } + mNode.mExtras = new Bundle(); + return mNode.mExtras; + } + + @Override + public boolean hasExtras() { + return mNode.mExtras != null; + } + + @Override + public void setChildCount(int num) { + mNode.mChildren = new ViewNode[num]; + } + + @Override + public int addChildCount(int num) { + if (mNode.mChildren == null) { + setChildCount(num); + return 0; + } + final int start = mNode.mChildren.length; + ViewNode[] newArray = new ViewNode[start + num]; + System.arraycopy(mNode.mChildren, 0, newArray, 0, start); + mNode.mChildren = newArray; + return start; + } + + @Override + public int getChildCount() { + return mNode.mChildren != null ? mNode.mChildren.length : 0; + } + + @Override + public ViewAssistStructure newChild(int index) { + ViewNode node = new ViewNode(); + mNode.mChildren[index] = node; + return new ViewNodeBuilder(mAssist, node, false); + } + + @Override + public ViewAssistStructure asyncNewChild(int index) { + synchronized (mAssist) { + ViewNode node = new ViewNode(); + mNode.mChildren[index] = node; + ViewNodeBuilder builder = new ViewNodeBuilder(mAssist, node, true); + mAssist.mPendingAsyncChildren.add(builder); + return builder; + } + } + + @Override + public void asyncCommit() { + synchronized (mAssist) { + if (!mAsync) { + throw new IllegalStateException("Child " + this + + " was not created with ViewAssistStructure.asyncNewChild"); + } + if (!mAssist.mPendingAsyncChildren.remove(this)) { + throw new IllegalStateException("Child " + this + " already committed"); + } + mAssist.notifyAll(); + } + } + + @Override + public Rect getTempRect() { + return mAssist.mTmpRect; + } } /** @hide */ public AssistStructure(Activity activity) { - super(activity); + mHaveData = true; + mActivityComponent = activity.getComponentName(); + ArrayList<ViewRootImpl> views = WindowManagerGlobal.getInstance().getRootViews( + activity.getActivityToken()); + for (int i=0; i<views.size(); i++) { + ViewRootImpl root = views.get(i); + mWindowNodes.add(new WindowNode(this, root)); + } + } + + public AssistStructure() { + mHaveData = true; + mActivityComponent = null; } - AssistStructure(Parcel in) { - super(in); + /** @hide */ + public AssistStructure(Parcel in) { + mReceiveChannel = in.readStrongBinder(); } + /** @hide */ + public void dump() { + Log.i(TAG, "Activity: " + mActivityComponent.flattenToShortString()); + final int N = getWindowNodeCount(); + for (int i=0; i<N; i++) { + WindowNode node = getWindowNodeAt(i); + Log.i(TAG, "Window #" + i + " [" + node.getLeft() + "," + node.getTop() + + " " + node.getWidth() + "x" + node.getHeight() + "]" + " " + node.getTitle()); + dump(" ", node.getRootViewNode()); + } + } + + void dump(String prefix, ViewNode node) { + Log.i(TAG, prefix + "View [" + node.getLeft() + "," + node.getTop() + + " " + node.getWidth() + "x" + node.getHeight() + "]" + " " + node.getClassName()); + int id = node.getId(); + if (id != 0) { + StringBuilder sb = new StringBuilder(); + sb.append(prefix); sb.append(" ID: #"); sb.append(Integer.toHexString(id)); + String entry = node.getIdEntry(); + if (entry != null) { + String type = node.getIdType(); + String pkg = node.getIdPackage(); + sb.append(" "); sb.append(pkg); sb.append(":"); sb.append(type); + sb.append("/"); sb.append(entry); + } + Log.i(TAG, sb.toString()); + } + int scrollX = node.getScrollX(); + int scrollY = node.getScrollY(); + if (scrollX != 0 || scrollY != 0) { + Log.i(TAG, prefix + " Scroll: " + scrollX + "," + scrollY); + } + CharSequence contentDescription = node.getContentDescription(); + if (contentDescription != null) { + Log.i(TAG, prefix + " Content description: " + contentDescription); + } + CharSequence text = node.getText(); + if (text != null) { + Log.i(TAG, prefix + " Text (sel " + node.getTextSelectionStart() + "-" + + node.getTextSelectionEnd() + "): " + text); + Log.i(TAG, prefix + " Text size: " + node.getTextSize() + " , style: #" + + node.getTextStyle()); + Log.i(TAG, prefix + " Text color fg: #" + Integer.toHexString(node.getTextColor()) + + ", bg: #" + Integer.toHexString(node.getTextBackgroundColor())); + } + String hint = node.getHint(); + if (hint != null) { + Log.i(TAG, prefix + " Hint: " + hint); + } + Bundle extras = node.getExtras(); + if (extras != null) { + Log.i(TAG, prefix + " Extras: " + extras); + } + final int NCHILDREN = node.getChildCount(); + if (NCHILDREN > 0) { + Log.i(TAG, prefix + " Children:"); + String cprefix = prefix + " "; + for (int i=0; i<NCHILDREN; i++) { + ViewNode cnode = node.getChildAt(i); + dump(cprefix, cnode); + } + } + } + + /** + * Return the activity this AssistStructure came from. + */ + public ComponentName getActivityComponent() { + ensureData(); + return mActivityComponent; + } + + /** + * Return the number of window contents that have been collected in this assist data. + */ + public int getWindowNodeCount() { + ensureData(); + return mWindowNodes.size(); + } + + /** + * Return one of the windows in the assist data. + * @param index Which window to retrieve, may be 0 to {@link #getWindowNodeCount()}-1. + */ public WindowNode getWindowNodeAt(int index) { - return super.getWindowNodeAt(index); + ensureData(); + return mWindowNodes.get(index); + } + + /** @hide */ + public void ensureData() { + if (mHaveData) { + return; + } + mHaveData = true; + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(DESCRIPTOR); + try { + mReceiveChannel.transact(TRANSACTION_XFER, data, reply, 0); + } catch (RemoteException e) { + Log.w(TAG, "Failure reading AssistStructure data", e); + return; + } + readContentFromParcel(reply); + data.recycle(); + reply.recycle(); + } + + void writeContentToParcel(Parcel out, int flags) { + // First make sure all content has been created. + boolean skipStructure = false; + synchronized (this) { + long endTime = SystemClock.uptimeMillis() + 5000; + long now; + while (mPendingAsyncChildren.size() > 0 && (now=SystemClock.uptimeMillis()) < endTime) { + try { + wait(endTime-now); + } catch (InterruptedException e) { + } + } + if (mPendingAsyncChildren.size() > 0) { + // We waited too long, assume none of the assist structure is valid. + skipStructure = true; + } + } + int start = out.dataPosition(); + PooledStringWriter pwriter = new PooledStringWriter(out); + ComponentName.writeToParcel(mActivityComponent, out); + final int N = skipStructure ? 0 : mWindowNodes.size(); + out.writeInt(N); + for (int i=0; i<N; i++) { + mWindowNodes.get(i).writeToParcel(out, pwriter); + } + pwriter.finish(); + Log.i(TAG, "Flattened assist data: " + (out.dataPosition() - start) + " bytes"); + } + + void readContentFromParcel(Parcel in) { + PooledStringReader preader = new PooledStringReader(in); + mActivityComponent = ComponentName.readFromParcel(in); + final int N = in.readInt(); + for (int i=0; i<N; i++) { + mWindowNodes.add(new WindowNode(in, preader)); + } + //dump(); } public int describeContents() { diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java index 8a31390..9113426 100644 --- a/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -77,6 +77,12 @@ public abstract class UsageStatsManagerInternal { public abstract boolean isAppIdle(String packageName, int userId); /** + * @return True if currently app idle parole mode is on. This means all idle apps are allow to + * run for a short period of time. + */ + public abstract boolean isAppIdleParoleOn(); + + /** * Sets up a listener for changes to packages being accessed. * @param listener A listener within the system process. */ @@ -90,8 +96,9 @@ public abstract class UsageStatsManagerInternal { public abstract void removeAppIdleStateChangeListener( AppIdleStateChangeListener listener); - public interface AppIdleStateChangeListener { - void onAppIdleStateChanged(String packageName, int userId, boolean idle); + public static abstract class AppIdleStateChangeListener { + public abstract void onAppIdleStateChanged(String packageName, int userId, boolean idle); + public abstract void onParoleStateChanged(boolean isParoleOn); } } diff --git a/core/java/android/content/AbstractThreadedSyncAdapter.java b/core/java/android/content/AbstractThreadedSyncAdapter.java index d4dee5b..58bd5cd 100644 --- a/core/java/android/content/AbstractThreadedSyncAdapter.java +++ b/core/java/android/content/AbstractThreadedSyncAdapter.java @@ -274,6 +274,10 @@ public abstract class AbstractThreadedSyncAdapter { } else { syncResult.databaseError = true; } + } catch (SecurityException e) { + AbstractThreadedSyncAdapter.this.onSecurityException(mAccount, mExtras, + mAuthority, syncResult); + syncResult.databaseError = true; } finally { Trace.traceEnd(Trace.TRACE_TAG_SYNC_MANAGER); @@ -319,6 +323,20 @@ public abstract class AbstractThreadedSyncAdapter { String authority, ContentProviderClient provider, SyncResult syncResult); /** + * Report that there was a security exception when opening the content provider + * prior to calling {@link #onPerformSync}. This will be treated as a sync + * database failure. + * + * @param account the account that attempted to sync + * @param extras SyncAdapter-specific parameters + * @param authority the authority of the failed sync request + * @param syncResult SyncAdapter-specific parameters + */ + public void onSecurityException(Account account, Bundle extras, + String authority, SyncResult syncResult) { + } + + /** * Indicates that a sync operation has been canceled. This will be invoked on a separate * thread than the sync thread and so you must consider the multi-threaded implications * of the work that you do in this method. diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 2dbcde9..34e4701 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -106,6 +106,8 @@ interface IPackageManager { void updatePermissionFlags(String permissionName, String packageName, int flagMask, int flagValues, int userId); + void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId); + boolean shouldShowRequestPermissionRationale(String permissionName, String packageName, int userId); diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 27d14b3..a88b71c 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -628,7 +628,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri /** * <p>List of available high speed video size, fps range and max batch size configurations * supported by the camera device, in the format of (width, height, fps_min, fps_max, batch_size_max).</p> - * <p>When CONSTRAINED_HIGH_SPEED_VIDEO is supported in android.control.availableCapabilities, + * <p>When CONSTRAINED_HIGH_SPEED_VIDEO is supported in {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}, * this metadata will list the supported high speed video size, fps range and max batch size * configurations. All the sizes listed in this configuration will be a subset of the sizes * reported by {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes } @@ -675,6 +675,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p> * * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL + * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES * @hide */ public static final Key<android.hardware.camera2.params.HighSpeedVideoConfiguration[]> CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS = @@ -2679,9 +2680,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p>Camera devices will come in three flavors: LEGACY, LIMITED and FULL.</p> * <p>A FULL device will support below capabilities:</p> * <ul> - * <li>30fps operation at maximum resolution (== sensor resolution) is preferred, more than - * 20fps is required, for at least uncompressed YUV - * output. ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains BURST_CAPTURE)</li> + * <li>BURST_CAPTURE capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains BURST_CAPTURE)</li> * <li>Per frame control ({@link CameraCharacteristics#SYNC_MAX_LATENCY android.sync.maxLatency} <code>==</code> PER_FRAME_CONTROL)</li> * <li>Manual sensor control ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains MANUAL_SENSOR)</li> * <li>Manual post-processing control ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains @@ -2689,7 +2688,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <li>Arbitrary cropping region ({@link CameraCharacteristics#SCALER_CROPPING_TYPE android.scaler.croppingType} <code>==</code> FREEFORM)</li> * <li>At least 3 processed (but not stalling) format output streams * ({@link CameraCharacteristics#REQUEST_MAX_NUM_OUTPUT_PROC android.request.maxNumOutputProc} <code>>=</code> 3)</li> - * <li>The required stream configuration defined in android.scaler.availableStreamConfigurations</li> + * <li>The required stream configurations defined in android.scaler.availableStreamConfigurations</li> * <li>The required exposure time range defined in {@link CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE android.sensor.info.exposureTimeRange}</li> * <li>The required maxFrameDuration defined in {@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration}</li> * </ul> @@ -2709,23 +2708,11 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * post-processing, arbitrary cropping regions, and has relaxed performance constraints.</p> * <p>Each higher level supports everything the lower level supports * in this order: FULL <code>></code> LIMITED <code>></code> LEGACY.</p> - * <p>A HIGH_RESOLUTION device is equivalent to a FULL device, except that:</p> - * <ul> - * <li>At least one output resolution of 8 megapixels or higher in uncompressed YUV is - * supported at <code>>=</code> 20 fps.</li> - * <li>Maximum-size (sensor resolution) uncompressed YUV is supported at <code>>=</code> 10 - * fps.</li> - * <li>For devices that list the RAW capability and support either RAW10 or RAW12 output, - * maximum-resolution RAW10 or RAW12 capture will operate at least at the rate of - * maximum-resolution YUV capture, and at least one supported output resolution of - * 8 megapixels or higher in RAW10 or RAW12 is supported <code>>=</code> 20 fps.</li> - * </ul> * <p><b>Possible values:</b> * <ul> * <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED}</li> * <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}</li> * <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY}</li> - * <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_HIGH_RESOLUTION HIGH_RESOLUTION}</li> * </ul></p> * <p>This key is available on all devices.</p> * @@ -2743,7 +2730,6 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * @see #INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED * @see #INFO_SUPPORTED_HARDWARE_LEVEL_FULL * @see #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY - * @see #INFO_SUPPORTED_HARDWARE_LEVEL_HIGH_RESOLUTION */ @PublicKey public static final Key<Integer> INFO_SUPPORTED_HARDWARE_LEVEL = diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index c656fb8..5a80585 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -531,37 +531,32 @@ public abstract class CameraMetadata<TKey> { public static final int REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS = 5; /** - * <p>The camera device supports capturing maximum-resolution - * images at >= 20 frames per second, in at least the - * uncompressed YUV format, when post-processing settings - * are set to FAST.</p> - * <p>More specifically, this means that a size matching the - * camera device's active array size is listed as a - * supported size for the YUV_420_888 format in - * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}, the minimum frame - * duration for that format and size is <= 1/20 s, and - * the {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges} entry - * lists at least one FPS range where the minimum FPS is</p> - * <blockquote> - * <p>= 1 / minimumFrameDuration for the maximum-size - * YUV_420_888 format.</p> - * </blockquote> - * <p>In addition, the {@link CameraCharacteristics#SYNC_MAX_LATENCY android.sync.maxLatency} field is - * guaranted to have a value between 0 and 4, inclusive. - * {@link CameraCharacteristics#CONTROL_AE_LOCK_AVAILABLE android.control.aeLockAvailable} and - * {@link CameraCharacteristics#CONTROL_AWB_LOCK_AVAILABLE android.control.awbLockAvailable} are also guaranteed - * to be <code>true</code> so burst capture with these two locks ON - * yields consistent image output.</p> - * <p>On a camera device that reports the HIGH_RESOLUTION hardware - * level, meaning the device supports very large capture sizes, - * BURST_CAPTURE means that at least 8-megapixel images can be - * captured at <code>>=</code> 20 fps, and maximum-resolution images can be - * captured at <code>>=</code> 10 fps.</p> + * <p>The camera device supports capturing high-resolution images at >= 20 frames per + * second, in at least the uncompressed YUV format, when post-processing settings are set + * to FAST. Additionally, maximum-resolution images can be captured at >= 10 frames + * per second. Here, 'high resolution' means at least 8 megapixels, or the maximum + * resolution of the device, whichever is smaller.</p> + * <p>More specifically, this means that a size matching the camera device's active array + * size is listed as a supported size for the {@link android.graphics.ImageFormat#YUV_420_888 } format in either {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes } or {@link android.hardware.camera2.params.StreamConfigurationMap#getHighResolutionOutputSizes }, + * with a minimum frame duration for that format and size of either <= 1/20 s, or + * <= 1/10 s, respectively; and the {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges} entry + * lists at least one FPS range where the minimum FPS is >= 1 / minimumFrameDuration + * for the maximum-size YUV_420_888 format. If that maximum size is listed in {@link android.hardware.camera2.params.StreamConfigurationMap#getHighResolutionOutputSizes }, + * then the list of resolutions for YUV_420_888 from {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes } contains at + * least one resolution >= 8 megapixels, with a minimum frame duration of <= 1/20 + * s.</p> + * <p>If the device supports the {@link android.graphics.ImageFormat#RAW10 }, {@link android.graphics.ImageFormat#RAW12 }, then those can also be captured at the same rate + * as the maximum-size YUV_420_888 resolution is.</p> + * <p>If the device supports the PRIVATE_REPROCESSING capability, then the same guarantees + * as for the YUV_420_888 format also apply to the {@link android.graphics.ImageFormat#PRIVATE } format.</p> + * <p>In addition, the {@link CameraCharacteristics#SYNC_MAX_LATENCY android.sync.maxLatency} field is guaranted to have a value between 0 + * and 4, inclusive. {@link CameraCharacteristics#CONTROL_AE_LOCK_AVAILABLE android.control.aeLockAvailable} and {@link CameraCharacteristics#CONTROL_AWB_LOCK_AVAILABLE android.control.awbLockAvailable} + * are also guaranteed to be <code>true</code> so burst capture with these two locks ON yields + * consistent image output.</p> * * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES * @see CameraCharacteristics#CONTROL_AE_LOCK_AVAILABLE * @see CameraCharacteristics#CONTROL_AWB_LOCK_AVAILABLE - * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP * @see CameraCharacteristics#SYNC_MAX_LATENCY * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES */ @@ -954,13 +949,6 @@ public abstract class CameraMetadata<TKey> { */ public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; - /** - * <p>This camera device is capable of supporting advanced imaging applications at full rate, - * and additional high-resolution outputs at lower rates.</p> - * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL - */ - public static final int INFO_SUPPORTED_HARDWARE_LEVEL_HIGH_RESOLUTION = 3; - // // Enumeration values for CameraCharacteristics#SYNC_MAX_LATENCY // diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index 9fa6687..75289f7 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -398,7 +398,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> @Override public int hashCode() { - return HashCodeHelpers.hashCode(mSettings, mSurfaceSet, mUserTag); + return HashCodeHelpers.hashCodeGeneric(mSettings, mSurfaceSet, mUserTag); } public static final Parcelable.Creator<CaptureRequest> CREATOR = @@ -1759,11 +1759,24 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * 16:9 aspect ratio, the primary image will be cropped vertically (letterbox) to * generate the thumbnail image. The thumbnail image will always have a smaller Field * Of View (FOV) than the primary image when aspect ratios differ.</p> + * <p>When an {@link CaptureRequest#JPEG_ORIENTATION android.jpeg.orientation} of non-zero degree is requested, + * the camera device will handle thumbnail rotation in one of the following ways:</p> + * <ul> + * <li>Set the {@link android.media.ExifInterface#TAG_ORIENTATION EXIF orientation flag} + * and keep jpeg and thumbnail image data unrotated.</li> + * <li>Rotate the jpeg and thumbnail image data and not set + * {@link android.media.ExifInterface#TAG_ORIENTATION EXIF orientation flag}. In this + * case, LIMITED or FULL hardware level devices will report rotated thumnail size in + * capture result, so the width and height will be interchanged if 90 or 270 degree + * orientation is requested. LEGACY device will always report unrotated thumbnail + * size.</li> + * </ul> * <p><b>Range of valid values:</b><br> * {@link CameraCharacteristics#JPEG_AVAILABLE_THUMBNAIL_SIZES android.jpeg.availableThumbnailSizes}</p> * <p>This key is available on all devices.</p> * * @see CameraCharacteristics#JPEG_AVAILABLE_THUMBNAIL_SIZES + * @see CaptureRequest#JPEG_ORIENTATION */ @PublicKey public static final Key<android.util.Size> JPEG_THUMBNAIL_SIZE = diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index b1fb615..1d31109 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -2314,11 +2314,24 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * 16:9 aspect ratio, the primary image will be cropped vertically (letterbox) to * generate the thumbnail image. The thumbnail image will always have a smaller Field * Of View (FOV) than the primary image when aspect ratios differ.</p> + * <p>When an {@link CaptureRequest#JPEG_ORIENTATION android.jpeg.orientation} of non-zero degree is requested, + * the camera device will handle thumbnail rotation in one of the following ways:</p> + * <ul> + * <li>Set the {@link android.media.ExifInterface#TAG_ORIENTATION EXIF orientation flag} + * and keep jpeg and thumbnail image data unrotated.</li> + * <li>Rotate the jpeg and thumbnail image data and not set + * {@link android.media.ExifInterface#TAG_ORIENTATION EXIF orientation flag}. In this + * case, LIMITED or FULL hardware level devices will report rotated thumnail size in + * capture result, so the width and height will be interchanged if 90 or 270 degree + * orientation is requested. LEGACY device will always report unrotated thumbnail + * size.</li> + * </ul> * <p><b>Range of valid values:</b><br> * {@link CameraCharacteristics#JPEG_AVAILABLE_THUMBNAIL_SIZES android.jpeg.availableThumbnailSizes}</p> * <p>This key is available on all devices.</p> * * @see CameraCharacteristics#JPEG_AVAILABLE_THUMBNAIL_SIZES + * @see CaptureRequest#JPEG_ORIENTATION */ @PublicKey public static final Key<android.util.Size> JPEG_THUMBNAIL_SIZE = diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index 10dd8ae..7e50fd9 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -842,11 +842,19 @@ public class CameraMetadataNative implements Parcelable { CameraCharacteristics.CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS); ReprocessFormatsMap inputOutputFormatsMap = getBase( CameraCharacteristics.SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP); - + int[] capabilities = getBase(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES); + boolean listHighResolution = false; + for (int capability : capabilities) { + if (capability == CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE) { + listHighResolution = true; + break; + } + } return new StreamConfigurationMap( configurations, minFrameDurations, stallDurations, depthConfigurations, depthMinFrameDurations, depthStallDurations, - highSpeedVideoConfigurations, inputOutputFormatsMap); + highSpeedVideoConfigurations, inputOutputFormatsMap, + listHighResolution); } private <T> Integer getMaxRegions(Key<T> key) { diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java index 2fb3203..e786707 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java +++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java @@ -605,6 +605,14 @@ public class LegacyCameraDevice implements AutoCloseable { return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceType(surface)); } + /** + * Query the surface for its currently configured dataspace + */ + public static int detectSurfaceDataspace(Surface surface) throws BufferQueueAbandonedException { + checkNotNull(surface); + return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDataspace(surface)); + } + static void configureSurface(Surface surface, int width, int height, int pixelFormat) throws BufferQueueAbandonedException { checkNotNull(surface); @@ -702,6 +710,8 @@ public class LegacyCameraDevice implements AutoCloseable { private static native int nativeDetectSurfaceType(Surface surface); + private static native int nativeDetectSurfaceDataspace(Surface surface); + private static native int nativeDetectSurfaceDimens(Surface surface, /*out*/int[/*2*/] dimens); diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java index c6ea488..639ad60 100644 --- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java +++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java @@ -20,14 +20,16 @@ import android.graphics.ImageFormat; import android.graphics.PixelFormat; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.utils.HashCodeHelpers; +import android.hardware.camera2.utils.SurfaceUtils; import android.hardware.camera2.legacy.LegacyCameraDevice; import android.hardware.camera2.legacy.LegacyMetadataMapper; -import android.hardware.camera2.legacy.LegacyExceptionUtils.BufferQueueAbandonedException; import android.view.Surface; import android.util.Range; import android.util.Size; +import android.util.SparseIntArray; import java.util.Arrays; import java.util.HashMap; @@ -79,7 +81,8 @@ public final class StreamConfigurationMap { * @param stallDurations a non-{@code null} array of {@link StreamConfigurationDuration} * @param highSpeedVideoConfigurations an array of {@link HighSpeedVideoConfiguration}, null if * camera device does not support high speed video recording - * + * @param listHighResolution a flag indicating whether the device supports BURST_CAPTURE + * and thus needs a separate list of slow high-resolution output sizes * @throws NullPointerException if any of the arguments except highSpeedVideoConfigurations * were {@code null} or any subelements were {@code null} * @@ -93,10 +96,12 @@ public final class StreamConfigurationMap { StreamConfigurationDuration[] depthMinFrameDurations, StreamConfigurationDuration[] depthStallDurations, HighSpeedVideoConfiguration[] highSpeedVideoConfigurations, - ReprocessFormatsMap inputOutputFormatsMap) { + ReprocessFormatsMap inputOutputFormatsMap, + boolean listHighResolution) { mConfigurations = checkArrayElementsNotNull(configurations, "configurations"); mMinFrameDurations = checkArrayElementsNotNull(minFrameDurations, "minFrameDurations"); mStallDurations = checkArrayElementsNotNull(stallDurations, "stallDurations"); + mListHighResolution = listHighResolution; if (depthConfigurations == null) { mDepthConfigurations = new StreamConfiguration[0]; @@ -120,15 +125,27 @@ public final class StreamConfigurationMap { // For each format, track how many sizes there are available to configure for (StreamConfiguration config : configurations) { - HashMap<Integer, Integer> map = config.isOutput() ? mOutputFormats : mInputFormats; - - Integer count = map.get(config.getFormat()); - - if (count == null) { - count = 0; + int fmt = config.getFormat(); + SparseIntArray map = null; + if (config.isOutput()) { + mAllOutputFormats.put(fmt, mAllOutputFormats.get(fmt) + 1); + long duration = 0; + if (mListHighResolution) { + for (StreamConfigurationDuration configurationDuration : mMinFrameDurations) { + if (configurationDuration.getFormat() == fmt && + configurationDuration.getWidth() == config.getSize().getWidth() && + configurationDuration.getHeight() == config.getSize().getHeight()) { + duration = configurationDuration.getDuration(); + break; + } + } + } + map = duration <= DURATION_20FPS_NS ? + mOutputFormats : mHighResOutputFormats; + } else { + map = mInputFormats; } - - map.put(config.getFormat(), count + 1); + map.put(fmt, map.get(fmt) + 1); } // For each depth format, track how many sizes there are available to configure @@ -138,16 +155,11 @@ public final class StreamConfigurationMap { continue; } - Integer count = mDepthOutputFormats.get(config.getFormat()); - - if (count == null) { - count = 0; - } - - mDepthOutputFormats.put(config.getFormat(), count + 1); + mDepthOutputFormats.put(config.getFormat(), + mDepthOutputFormats.get(config.getFormat()) + 1); } - if (!mOutputFormats.containsKey(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED)) { + if (mOutputFormats.indexOfKey(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) < 0) { throw new AssertionError( "At least one stream configuration for IMPLEMENTATION_DEFINED must exist"); } @@ -241,7 +253,7 @@ public final class StreamConfigurationMap { * @return a non-empty array of sizes, or {@code null} if the format was not available. */ public Size[] getInputSizes(final int format) { - return getPublicFormatSizes(format, /*output*/false); + return getPublicFormatSizes(format, /*output*/false, /*highRes*/false); } /** @@ -274,9 +286,9 @@ public final class StreamConfigurationMap { int internalFormat = imageFormatToInternal(format); int dataspace = imageFormatToDataspace(format); if (dataspace == HAL_DATASPACE_DEPTH) { - return mDepthOutputFormats.containsKey(internalFormat); + return mDepthOutputFormats.indexOfKey(internalFormat) >= 0; } else { - return getFormatsMap(/*output*/true).containsKey(internalFormat); + return getFormatsMap(/*output*/true).indexOfKey(internalFormat) >= 0; } } @@ -378,27 +390,24 @@ public final class StreamConfigurationMap { public boolean isOutputSupportedFor(Surface surface) { checkNotNull(surface, "surface must not be null"); - Size surfaceSize; - int surfaceFormat = -1; - try { - surfaceSize = LegacyCameraDevice.getSurfaceSize(surface); - surfaceFormat = LegacyCameraDevice.detectSurfaceType(surface); - } catch(BufferQueueAbandonedException e) { - throw new IllegalArgumentException("Abandoned surface", e); - } + Size surfaceSize = SurfaceUtils.getSurfaceSize(surface); + int surfaceFormat = SurfaceUtils.getSurfaceFormat(surface); + int surfaceDataspace = SurfaceUtils.getSurfaceDataspace(surface); // See if consumer is flexible. - boolean isFlexible = LegacyCameraDevice.isFlexibleConsumer(surface); + boolean isFlexible = SurfaceUtils.isFlexibleConsumer(surface); // Override RGB formats to IMPLEMENTATION_DEFINED, b/9487482 if ((surfaceFormat >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 && surfaceFormat <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) { - surfaceFormat = LegacyMetadataMapper.HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; + surfaceFormat = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; } - for (StreamConfiguration config : mConfigurations) { + StreamConfiguration[] configs = + surfaceDataspace != HAL_DATASPACE_DEPTH ? mConfigurations : mDepthConfigurations; + for (StreamConfiguration config : configs) { if (config.getFormat() == surfaceFormat && config.isOutput()) { - // Mathing format, either need exact size match, or a flexible consumer + // Matching format, either need exact size match, or a flexible consumer // and a size no bigger than MAX_DIMEN_FOR_ROUNDING if (config.getSize().equals(surfaceSize)) { return true; @@ -414,12 +423,12 @@ public final class StreamConfigurationMap { /** * Get a list of sizes compatible with {@code klass} to use as an output. * - * <p>Since some of the supported classes may support additional formats beyond + * <p>Some of the supported classes may support additional formats beyond * {@link ImageFormat#PRIVATE}; this function only returns * sizes for {@link ImageFormat#PRIVATE}. For example, {@link android.media.ImageReader} * supports {@link ImageFormat#YUV_420_888} and {@link ImageFormat#PRIVATE}, this method will * only return the sizes for {@link ImageFormat#PRIVATE} for {@link android.media.ImageReader} - * class .</p> + * class.</p> * * <p>If a well-defined format such as {@code NV21} is required, use * {@link #getOutputSizes(int)} instead.</p> @@ -444,7 +453,7 @@ public final class StreamConfigurationMap { } return getInternalFormatSizes(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, - HAL_DATASPACE_UNKNOWN,/*output*/true); + HAL_DATASPACE_UNKNOWN,/*output*/true, /*highRes*/false); } /** @@ -453,6 +462,14 @@ public final class StreamConfigurationMap { * <p>The {@code format} should be a supported format (one of the formats returned by * {@link #getOutputFormats}).</p> * + * As of API level 23, the {@link #getHighResolutionOutputSizes} method can be used on devices + * that support the + * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE BURST_CAPTURE} + * capability to get a list of high-resolution output sizes that cannot operate at the preferred + * 20fps rate. This means that for some supported formats, this method will return an empty + * list, if all the supported resolutions operate at below 20fps. For devices that do not + * support the BURST_CAPTURE capability, all output resolutions are listed through this method. + * * @param format an image format from {@link ImageFormat} or {@link PixelFormat} * @return * an array of supported sizes, @@ -463,36 +480,40 @@ public final class StreamConfigurationMap { * @see #getOutputFormats */ public Size[] getOutputSizes(int format) { - return getPublicFormatSizes(format, /*output*/true); + return getPublicFormatSizes(format, /*output*/true, /*highRes*/ false); } /** * Get a list of supported high speed video recording sizes. - * - * <p> When HIGH_SPEED_VIDEO is supported in - * {@link CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES available scene modes}, this - * method will list the supported high speed video size configurations. All the sizes listed - * will be a subset of the sizes reported by {@link #getOutputSizes} for processed non-stalling - * formats (typically ImageFormat#YUV_420_888, ImageFormat#NV21, ImageFormat#YV12)</p> - * - * <p> To enable high speed video recording, application must set - * {@link CaptureRequest#CONTROL_SCENE_MODE} to - * {@link CaptureRequest#CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO HIGH_SPEED_VIDEO} in capture - * requests and select the video size from this method and + * <p> + * When {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO} is + * supported in {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES}, this method will + * list the supported high speed video size configurations. All the sizes listed will be a + * subset of the sizes reported by {@link #getOutputSizes} for processed non-stalling formats + * (typically {@link ImageFormat#PRIVATE} {@link ImageFormat#YUV_420_888}, etc.) + * </p> + * <p> + * To enable high speed video recording, application must create a constrained create high speed + * capture session via {@link CameraDevice#createConstrainedHighSpeedCaptureSession}, and submit + * a CaptureRequest list created by {@link CameraDevice#createConstrainedHighSpeedRequestList} + * to this session. The application must select the video size from this method and * {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE FPS range} from - * {@link #getHighSpeedVideoFpsRangesFor} to configure the recording and preview streams and - * setup the recording requests. For example, if the application intends to do high speed - * recording, it can select the maximum size reported by this method to configure output - * streams. Note that for the use case of multiple output streams, application must select one - * unique size from this method to use. Otherwise a request error might occur. Once the size is + * {@link #getHighSpeedVideoFpsRangesFor} to configure the constrained high speed session and + * generate the high speed request list. For example, if the application intends to do high + * speed recording, it can select the maximum size reported by this method to create high speed + * capture session. Note that for the use case of multiple output streams, application must + * select one unique size from this method to use (e.g., preview and recording streams must have + * the same size). Otherwise, the high speed session creation will fail. Once the size is * selected, application can get the supported FPS ranges by * {@link #getHighSpeedVideoFpsRangesFor}, and use these FPS ranges to setup the recording - * requests.</p> - * - * @return - * an array of supported high speed video recording sizes + * request lists via {@link CameraDevice#createConstrainedHighSpeedRequestList}. + * </p> * + * @return an array of supported high speed video recording sizes * @see #getHighSpeedVideoFpsRangesFor(Size) + * @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO + * @see CameraDevice#createConstrainedHighSpeedCaptureSession + * @see CameraDevice#createConstrainedHighSpeedRequestList */ public Size[] getHighSpeedVideoSizes() { Set<Size> keySet = mHighSpeedVideoSizeMap.keySet(); @@ -501,26 +522,25 @@ public final class StreamConfigurationMap { /** * Get the frame per second ranges (fpsMin, fpsMax) for input high speed video size. - * - * <p> See {@link #getHighSpeedVideoSizes} for how to enable high speed recording.</p> - * - * <p> For normal video recording use case, where some application will NOT set - * {@link CaptureRequest#CONTROL_SCENE_MODE} to - * {@link CaptureRequest#CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO HIGH_SPEED_VIDEO} in capture - * requests, the {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE FPS ranges} reported in - * this method must not be used to setup capture requests, or it will cause request error.</p> + * <p> + * See {@link #getHighSpeedVideoFpsRanges} for how to enable high speed recording. + * </p> + * <p> + * The {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE FPS ranges} reported in this method + * must not be used to setup capture requests that are submitted to unconstrained capture + * sessions, or it will result in {@link IllegalArgumentException IllegalArgumentExceptions}. + * </p> + * <p> + * See {@link #getHighSpeedVideoFpsRanges} for the characteristics of the returned FPS ranges. + * </p> * * @param size one of the sizes returned by {@link #getHighSpeedVideoSizes()} - * @return - * An array of FPS range to use with - * {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE TARGET_FPS_RANGE} when using - * {@link CaptureRequest#CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO HIGH_SPEED_VIDEO} scene - * mode. - * The upper bound of returned ranges is guaranteed to be larger or equal to 60. - * + * @return an array of supported high speed video recording FPS ranges The upper bound of + * returned ranges is guaranteed to be greater than or equal to 120. * @throws IllegalArgumentException if input size does not exist in the return value of - * getHighSpeedVideoSizes + * getHighSpeedVideoSizes * @see #getHighSpeedVideoSizes() + * @see #getHighSpeedVideoFpsRanges() */ public Range<Integer>[] getHighSpeedVideoFpsRangesFor(Size size) { Integer fpsRangeCount = mHighSpeedVideoSizeMap.get(size); @@ -542,34 +562,46 @@ public final class StreamConfigurationMap { /** * Get a list of supported high speed video recording FPS ranges. - * - * <p> When HIGH_SPEED_VIDEO is supported in - * {@link CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES available scene modes}, this - * method will list the supported high speed video FPS range configurations. Application can - * then use {@link #getHighSpeedVideoSizesFor} to query available sizes for one of returned - * FPS range.</p> - * - * <p> To enable high speed video recording, application must set - * {@link CaptureRequest#CONTROL_SCENE_MODE} to - * {@link CaptureRequest#CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO HIGH_SPEED_VIDEO} in capture - * requests and select the video size from {@link #getHighSpeedVideoSizesFor} and + * <p> + * When {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO} is + * supported in {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES}, this method will + * list the supported high speed video FPS range configurations. Application can then use + * {@link #getHighSpeedVideoSizesFor} to query available sizes for one of returned FPS range. + * </p> + * <p> + * To enable high speed video recording, application must create a constrained create high speed + * capture session via {@link CameraDevice#createConstrainedHighSpeedCaptureSession}, and submit + * a CaptureRequest list created by {@link CameraDevice#createConstrainedHighSpeedRequestList} + * to this session. The application must select the video size from this method and * {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE FPS range} from - * this method to configure the recording and preview streams and setup the recording requests. - * For example, if the application intends to do high speed recording, it can select one FPS - * range reported by this method, query the video sizes corresponding to this FPS range by - * {@link #getHighSpeedVideoSizesFor} and select one of reported sizes to configure output - * streams. Note that for the use case of multiple output streams, application must select one - * unique size from {@link #getHighSpeedVideoSizesFor}, and use it for all output streams. - * Otherwise a request error might occur when attempting to enable - * {@link CaptureRequest#CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO HIGH_SPEED_VIDEO}. - * Once the stream is configured, application can set the FPS range in the recording requests. + * {@link #getHighSpeedVideoFpsRangesFor} to configure the constrained high speed session and + * generate the high speed request list. For example, if the application intends to do high + * speed recording, it can select one FPS range reported by this method, query the video sizes + * corresponding to this FPS range by {@link #getHighSpeedVideoSizesFor} and use one of reported + * sizes to create a high speed capture session. Note that for the use case of multiple output + * streams, application must select one unique size from this method to use (e.g., preview and + * recording streams must have the same size). Otherwise, the high speed session creation will + * fail. Once the high speed capture session is created, the application can set the FPS range + * in the recording request lists via + * {@link CameraDevice#createConstrainedHighSpeedRequestList}. + * </p> + * <p> + * The FPS ranges reported by this method will have below characteristics: + * <li>The fpsMin and fpsMax will be a multiple 30fps.</li> + * <li>The fpsMin will be no less than 30fps, the fpsMax will be no less than 120fps.</li> + * <li>At least one range will be a fixed FPS range where fpsMin == fpsMax.</li> + * <li>For each fixed FPS range, there will be one corresponding variable FPS range [30, + * fps_max]. These kinds of FPS ranges are suitable for preview-only use cases where the + * application doesn't want the camera device always produce higher frame rate than the display + * refresh rate.</li> * </p> * - * @return - * an array of supported high speed video recording FPS ranges - * The upper bound of returned ranges is guaranteed to be larger or equal to 60. - * + * @return an array of supported high speed video recording FPS ranges The upper bound of + * returned ranges is guaranteed to be larger or equal to 120. * @see #getHighSpeedVideoSizesFor + * @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO + * @see CameraDevice#createConstrainedHighSpeedCaptureSession + * @see CameraDevice#createConstrainedHighSpeedRequestList */ @SuppressWarnings("unchecked") public Range<Integer>[] getHighSpeedVideoFpsRanges() { @@ -578,21 +610,13 @@ public final class StreamConfigurationMap { } /** - * Get the supported video sizes for input FPS range. + * Get the supported video sizes for an input high speed FPS range. * - * <p> See {@link #getHighSpeedVideoFpsRanges} for how to enable high speed recording.</p> - * - * <p> For normal video recording use case, where the application will NOT set - * {@link CaptureRequest#CONTROL_SCENE_MODE} to - * {@link CaptureRequest#CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO HIGH_SPEED_VIDEO} in capture - * requests, the {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE FPS ranges} reported in - * this method must not be used to setup capture requests, or it will cause request error.</p> + * <p> See {@link #getHighSpeedVideoSizes} for how to enable high speed recording.</p> * * @param fpsRange one of the FPS range returned by {@link #getHighSpeedVideoFpsRanges()} - * @return - * An array of video sizes to configure output stream when using - * {@link CaptureRequest#CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO HIGH_SPEED_VIDEO} scene - * mode. + * @return An array of video sizes to create high speed capture sessions for high speed streaming + * use cases. * * @throws IllegalArgumentException if input FPS range does not exist in the return value of * getHighSpeedVideoFpsRanges @@ -616,6 +640,32 @@ public final class StreamConfigurationMap { } /** + * Get a list of supported high resolution sizes, which cannot operate at full BURST_CAPTURE + * rate. + * + * <p>This includes all output sizes that cannot meet the 20 fps frame rate requirements for the + * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE BURST_CAPTURE} + * capability. This does not include the stall duration, so for example, a JPEG or RAW16 output + * resolution with a large stall duration but a minimum frame duration that's above 20 fps will + * still be listed in the regular {@link #getOutputSizes} list. All the sizes on this list are + * still guaranteed to operate at a rate of at least 10 fps, not including stall duration.</p> + * + * <p>For a device that does not support the BURST_CAPTURE capability, this list will be + * {@code null}, since resolutions in the {@link #getOutputSizes} list are already not + * guaranteed to meet >= 20 fps rate requirements. For a device that does support the + * BURST_CAPTURE capability, this list may be empty, if all supported resolutions meet the 20 + * fps requirement.</p> + * + * @return an array of supported slower high-resolution sizes, or {@code null} if the + * BURST_CAPTURE capability is not supported + */ + public Size[] getHighResolutionOutputSizes(int format) { + if (!mListHighResolution) return null; + + return getPublicFormatSizes(format, /*output*/true, /*highRes*/ true); + } + + /** * Get the minimum {@link CaptureRequest#SENSOR_FRAME_DURATION frame duration} * for the format/size combination (in nanoseconds). * @@ -867,6 +917,7 @@ public final class StreamConfigurationMap { return Arrays.equals(mConfigurations, other.mConfigurations) && Arrays.equals(mMinFrameDurations, other.mMinFrameDurations) && Arrays.equals(mStallDurations, other.mStallDurations) && + Arrays.equals(mDepthConfigurations, other.mDepthConfigurations) && Arrays.equals(mHighSpeedVideoConfigurations, other.mHighSpeedVideoConfigurations); } @@ -879,18 +930,31 @@ public final class StreamConfigurationMap { @Override public int hashCode() { // XX: do we care about order? - return HashCodeHelpers.hashCode( + return HashCodeHelpers.hashCodeGeneric( mConfigurations, mMinFrameDurations, - mStallDurations, mHighSpeedVideoConfigurations); + mStallDurations, + mDepthConfigurations, mHighSpeedVideoConfigurations); } // Check that the argument is supported by #getOutputFormats or #getInputFormats private int checkArgumentFormatSupported(int format, boolean output) { checkArgumentFormat(format); - int[] formats = output ? getOutputFormats() : getInputFormats(); - for (int i = 0; i < formats.length; ++i) { - if (format == formats[i]) { + int internalFormat = imageFormatToInternal(format); + int internalDataspace = imageFormatToDataspace(format); + + if (output) { + if (internalDataspace == HAL_DATASPACE_DEPTH) { + if (mDepthOutputFormats.indexOfKey(internalFormat) >= 0) { + return format; + } + } else { + if (mAllOutputFormats.indexOfKey(internalFormat) >= 0) { + return format; + } + } + } else { + if (mInputFormats.indexOfKey(internalFormat) >= 0) { return format; } } @@ -1175,7 +1239,7 @@ public final class StreamConfigurationMap { return formats; } - private Size[] getPublicFormatSizes(int format, boolean output) { + private Size[] getPublicFormatSizes(int format, boolean output, boolean highRes) { try { checkArgumentFormatSupported(format, output); } catch (IllegalArgumentException e) { @@ -1185,36 +1249,57 @@ public final class StreamConfigurationMap { int internalFormat = imageFormatToInternal(format); int dataspace = imageFormatToDataspace(format); - return getInternalFormatSizes(internalFormat, dataspace, output); + return getInternalFormatSizes(internalFormat, dataspace, output, highRes); } - private Size[] getInternalFormatSizes(int format, int dataspace, boolean output) { - - HashMap<Integer, Integer> formatsMap = - (dataspace == HAL_DATASPACE_DEPTH) ? mDepthOutputFormats : getFormatsMap(output); - - Integer sizesCount = formatsMap.get(format); - if (sizesCount == null) { + private Size[] getInternalFormatSizes(int format, int dataspace, + boolean output, boolean highRes) { + SparseIntArray formatsMap = + !output ? mInputFormats : + dataspace == HAL_DATASPACE_DEPTH ? mDepthOutputFormats : + highRes ? mHighResOutputFormats : + mOutputFormats; + + int sizesCount = formatsMap.get(format); + if ( ((!output || dataspace == HAL_DATASPACE_DEPTH) && sizesCount == 0) || + (output && dataspace != HAL_DATASPACE_DEPTH && mAllOutputFormats.get(format) == 0)) { + // Only throw if this is really not supported at all throw new IllegalArgumentException("format not available"); } - int len = sizesCount; - Size[] sizes = new Size[len]; + Size[] sizes = new Size[sizesCount]; int sizeIndex = 0; StreamConfiguration[] configurations = (dataspace == HAL_DATASPACE_DEPTH) ? mDepthConfigurations : mConfigurations; - for (StreamConfiguration config : configurations) { - if (config.getFormat() == format && config.isOutput() == output) { + int fmt = config.getFormat(); + if (fmt == format && config.isOutput() == output) { + if (output) { + // Filter slow high-res output formats; include for + // highRes, remove for !highRes + long duration = 0; + for (int i = 0; i < mMinFrameDurations.length; i++) { + StreamConfigurationDuration d = mMinFrameDurations[i]; + if (d.getFormat() == fmt && + d.getWidth() == config.getSize().getWidth() && + d.getHeight() == config.getSize().getHeight()) { + duration = d.getDuration(); + break; + } + } + if (highRes != (duration > DURATION_20FPS_NS)) { + continue; + } + } sizes[sizeIndex++] = config.getSize(); } } - if (sizeIndex != len) { + if (sizeIndex != sizesCount) { throw new AssertionError( - "Too few sizes (expected " + len + ", actual " + sizeIndex + ")"); + "Too few sizes (expected " + sizesCount + ", actual " + sizeIndex + ")"); } return sizes; @@ -1226,14 +1311,16 @@ public final class StreamConfigurationMap { int i = 0; - for (int format : getFormatsMap(output).keySet()) { + SparseIntArray map = getFormatsMap(output); + for (int j = 0; j < map.size(); j++) { + int format = map.keyAt(j); if (format != HAL_PIXEL_FORMAT_RAW_OPAQUE) { formats[i++] = imageFormatToPublic(format); } } if (output) { - for (int format : mDepthOutputFormats.keySet()) { - formats[i++] = depthFormatToPublic(format); + for (int j = 0; j < mDepthOutputFormats.size(); j++) { + formats[i++] = depthFormatToPublic(mDepthOutputFormats.keyAt(j)); } } if (formats.length != i) { @@ -1244,14 +1331,14 @@ public final class StreamConfigurationMap { } /** Get the format -> size count map for either output or input formats */ - private HashMap<Integer, Integer> getFormatsMap(boolean output) { - return output ? mOutputFormats : mInputFormats; + private SparseIntArray getFormatsMap(boolean output) { + return output ? mAllOutputFormats : mInputFormats; } private long getInternalFormatDuration(int format, int dataspace, Size size, int duration) { // assume format is already checked, since its internal - if (!arrayContains(getInternalFormatSizes(format, dataspace, /*output*/true), size)) { + if (!isSupportedInternalConfiguration(format, dataspace, size)) { throw new IllegalArgumentException("size was not supported"); } @@ -1289,10 +1376,9 @@ public final class StreamConfigurationMap { /** Count the number of publicly-visible output formats */ private int getPublicFormatCount(boolean output) { - HashMap<Integer, Integer> formatsMap = getFormatsMap(output); - + SparseIntArray formatsMap = getFormatsMap(output); int size = formatsMap.size(); - if (formatsMap.containsKey(HAL_PIXEL_FORMAT_RAW_OPAQUE)) { + if (formatsMap.indexOfKey(HAL_PIXEL_FORMAT_RAW_OPAQUE) >= 0) { size -= 1; } if (output) { @@ -1316,6 +1402,21 @@ public final class StreamConfigurationMap { return false; } + private boolean isSupportedInternalConfiguration(int format, int dataspace, + Size size) { + StreamConfiguration[] configurations = + (dataspace == HAL_DATASPACE_DEPTH) ? mDepthConfigurations : mConfigurations; + + for (int i = 0; i < configurations.length; i++) { + if (configurations[i].getFormat() == format && + configurations[i].getSize().equals(size)) { + return true; + } + } + + return false; + } + /** * Return this {@link StreamConfigurationMap} as a string representation. * @@ -1351,6 +1452,8 @@ public final class StreamConfigurationMap { StringBuilder sb = new StringBuilder("StreamConfiguration("); appendOutputsString(sb); sb.append(", "); + appendHighResOutputsString(sb); + sb.append(", "); appendInputsString(sb); sb.append(", "); appendValidOutputFormatsForInputString(sb); @@ -1381,6 +1484,27 @@ public final class StreamConfigurationMap { sb.append(")"); } + private void appendHighResOutputsString(StringBuilder sb) { + sb.append("HighResolutionOutputs("); + int[] formats = getOutputFormats(); + for (int format : formats) { + Size[] sizes = getHighResolutionOutputSizes(format); + if (sizes == null) continue; + for (Size size : sizes) { + long minFrameDuration = getOutputMinFrameDuration(format, size); + long stallDuration = getOutputStallDuration(format, size); + sb.append(String.format("[w:%d, h:%d, format:%s(%d), min_duration:%d, " + + "stall:%d], ", size.getWidth(), size.getHeight(), formatToString(format), + format, minFrameDuration, stallDuration)); + } + } + // Remove the pending ", " + if (sb.charAt(sb.length() - 1) == ' ') { + sb.delete(sb.length() - 2, sb.length()); + } + sb.append(")"); + } + private void appendInputsString(StringBuilder sb) { sb.append("Inputs("); int[] formats = getInputFormats(); @@ -1479,15 +1603,21 @@ public final class StreamConfigurationMap { } // from system/core/include/system/graphics.h + private static final int HAL_PIXEL_FORMAT_RAW16 = 0x20; private static final int HAL_PIXEL_FORMAT_BLOB = 0x21; private static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22; + private static final int HAL_PIXEL_FORMAT_YCbCr_420_888 = 0x23; private static final int HAL_PIXEL_FORMAT_RAW_OPAQUE = 0x24; + private static final int HAL_PIXEL_FORMAT_RAW10 = 0x25; + private static final int HAL_PIXEL_FORMAT_RAW12 = 0x26; private static final int HAL_PIXEL_FORMAT_Y16 = 0x20363159; + private static final int HAL_DATASPACE_UNKNOWN = 0x0; private static final int HAL_DATASPACE_JFIF = 0x101; private static final int HAL_DATASPACE_DEPTH = 0x1000; + private static final long DURATION_20FPS_NS = 50000000L; /** * @see #getDurations(int, int) */ @@ -1505,15 +1635,20 @@ public final class StreamConfigurationMap { private final HighSpeedVideoConfiguration[] mHighSpeedVideoConfigurations; private final ReprocessFormatsMap mInputOutputFormatsMap; - /** ImageFormat -> num output sizes mapping */ - private final HashMap</*ImageFormat*/Integer, /*Count*/Integer> mOutputFormats = - new HashMap<Integer, Integer>(); - /** ImageFormat -> num input sizes mapping */ - private final HashMap</*ImageFormat*/Integer, /*Count*/Integer> mInputFormats = - new HashMap<Integer, Integer>(); - /** ImageFormat -> num depth output sizes mapping */ - private final HashMap</*ImageFormat*/Integer, /*Count*/Integer> mDepthOutputFormats = - new HashMap<Integer, Integer>(); + private final boolean mListHighResolution; + + /** internal format -> num output sizes mapping, not including slow high-res sizes, for + * non-depth dataspaces */ + private final SparseIntArray mOutputFormats = new SparseIntArray(); + /** internal format -> num output sizes mapping for slow high-res sizes, for non-depth + * dataspaces */ + private final SparseIntArray mHighResOutputFormats = new SparseIntArray(); + /** internal format -> num output sizes mapping for all non-depth dataspaces */ + private final SparseIntArray mAllOutputFormats = new SparseIntArray(); + /** internal format -> num input sizes mapping, for input reprocessing formats */ + private final SparseIntArray mInputFormats = new SparseIntArray(); + /** internal format -> num depth output sizes mapping, for HAL_DATASPACE_DEPTH */ + private final SparseIntArray mDepthOutputFormats = new SparseIntArray(); /** High speed video Size -> FPS range count mapping*/ private final HashMap</*HighSpeedVideoSize*/Size, /*Count*/Integer> mHighSpeedVideoSizeMap = new HashMap<Size, Integer>(); @@ -1522,4 +1657,3 @@ public final class StreamConfigurationMap { mHighSpeedVideoFpsRangeMap = new HashMap<Range<Integer>, Integer>(); } - diff --git a/core/java/android/hardware/camera2/params/TonemapCurve.java b/core/java/android/hardware/camera2/params/TonemapCurve.java index 398a7e9..2d7bbaa 100644 --- a/core/java/android/hardware/camera2/params/TonemapCurve.java +++ b/core/java/android/hardware/camera2/params/TonemapCurve.java @@ -277,7 +277,7 @@ public final class TonemapCurve { return mHashCode; } - mHashCode = HashCodeHelpers.hashCode(mRed, mGreen, mBlue); + mHashCode = HashCodeHelpers.hashCodeGeneric(mRed, mGreen, mBlue); mHashCalculated = true; return mHashCode; diff --git a/core/java/android/hardware/camera2/utils/HashCodeHelpers.java b/core/java/android/hardware/camera2/utils/HashCodeHelpers.java index 7b4aa09..731da8b 100644 --- a/core/java/android/hardware/camera2/utils/HashCodeHelpers.java +++ b/core/java/android/hardware/camera2/utils/HashCodeHelpers.java @@ -30,7 +30,7 @@ public final class HashCodeHelpers { * * @return the numeric hash code */ - public static int hashCode(int[] array) { + public static int hashCode(int... array) { if (array == null) { return 0; } @@ -60,7 +60,7 @@ public final class HashCodeHelpers { * * @return the numeric hash code */ - public static int hashCode(float[] array) { + public static int hashCode(float... array) { if (array == null) { return 0; } @@ -83,7 +83,7 @@ public final class HashCodeHelpers { * * @return the numeric hash code */ - public static <T> int hashCode(T[] array) { + public static <T> int hashCodeGeneric(T... array) { if (array == null) { return 0; } @@ -97,56 +97,4 @@ public final class HashCodeHelpers { return h; } - public static <T> int hashCode(T a) { - return (a == null) ? 0 : a.hashCode(); - } - - public static <T> int hashCode(T a, T b) { - int h = hashCode(a); - - int x = (b == null) ? 0 : b.hashCode(); - h = ((h << 5) - h) ^ x; // (h * 31) XOR x - - return h; - } - - public static <T> int hashCode(T a, T b, T c) { - int h = hashCode(a, b); - - int x = (c == null) ? 0 : c.hashCode(); - h = ((h << 5) - h) ^ x; // (h * 31) XOR x - - return h; - } - - public static <T> int hashCode(T a, T b, T c, T d) { - int h = hashCode(a, b, c); - - int x = (d == null) ? 0 : d.hashCode(); - h = ((h << 5) - h) ^ x; // (h * 31) XOR x - - return h; - } - - public static int hashCode(int x) { - return hashCode(new int[] { x } ); - } - - public static int hashCode(int x, int y) { - return hashCode(new int[] { x, y } ); - } - - public static int hashCode(int x, int y, int z) { - return hashCode(new int[] { x, y, z } ); - } - - public static int hashCode(int x, int y, int z, int w) { - return hashCode(new int[] { x, y, z, w } ); - } - - public static int hashCode(int x, int y, int z, int w, int t) { - return hashCode(new int[] { x, y, z, w, t } ); - } - - } diff --git a/core/java/android/hardware/camera2/utils/SurfaceUtils.java b/core/java/android/hardware/camera2/utils/SurfaceUtils.java index 40005a5..064b21a 100644 --- a/core/java/android/hardware/camera2/utils/SurfaceUtils.java +++ b/core/java/android/hardware/camera2/utils/SurfaceUtils.java @@ -79,4 +79,30 @@ public class SurfaceUtils { throw new IllegalArgumentException("Surface was abandoned", e); } } + + /** + * Get the Surface dataspace. + * + * @param surface The surface to be queried for dataspace. + * @return dataspace of the surface. + * + * @throws IllegalArgumentException if the surface is already abandoned. + */ + public static int getSurfaceDataspace(Surface surface) { + try { + return LegacyCameraDevice.detectSurfaceDataspace(surface); + } catch (BufferQueueAbandonedException e) { + throw new IllegalArgumentException("Surface was abandoned", e); + } + } + + /** + * Return true is the consumer is one of the consumers that can accept + * producer overrides of the default dimensions and format. + * + */ + public static boolean isFlexibleConsumer(Surface output) { + return LegacyCameraDevice.isFlexibleConsumer(output); + } + } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index b9f7365..80476ea 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -2110,6 +2110,8 @@ public class ConnectivityManager { * can automatically log in to a captive portal without user intervention. * * @param network The {@link Network} of the network that is being evaluated. + * + * @hide */ public void onPreCheck(Network network) {} @@ -2379,6 +2381,14 @@ public class ConnectivityManager { * Status of the request can be followed by listening to the various * callbacks described in {@link NetworkCallback}. The {@link Network} * can be used to direct traffic to the network. + * <p>It is presently unsupported to request a network with mutable + * {@link NetworkCapabilities} such as + * {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or + * {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL} + * as these {@code NetworkCapabilities} represent states that a particular + * network may never attain, and whether a network will attain these states + * is unknown prior to bringing up the network so the framework does not + * know how to go about satisfing a request with these capabilities. * <p>This method requires the caller to hold the permission * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. * @@ -2386,6 +2396,8 @@ public class ConnectivityManager { * @param networkCallback The {@link NetworkCallback} to be utilized for this * request. Note the callback must not be shared - they * uniquely specify this request. + * @throws IllegalArgumentException if {@code request} specifies any mutable + * {@code NetworkCapabilities}. */ public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback) { sendRequestForNetwork(request.networkCapabilities, networkCallback, 0, @@ -2467,12 +2479,22 @@ public class ConnectivityManager { * <p> * The request may be released normally by calling * {@link #releaseNetworkRequest(android.app.PendingIntent)}. + * <p>It is presently unsupported to request a network with either + * {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or + * {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL} + * as these {@code NetworkCapabilities} represent states that a particular + * network may never attain, and whether a network will attain these states + * is unknown prior to bringing up the network so the framework does not + * know how to go about satisfing a request with these capabilities. * <p>This method requires the caller to hold the permission * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. * @param request {@link NetworkRequest} describing this request. * @param operation Action to perform when the network is available (corresponds * to the {@link NetworkCallback#onAvailable} call. Typically * comes from {@link PendingIntent#getBroadcast}. Cannot be null. + * @throws IllegalArgumentException if {@code request} contains either + * {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or + * {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL}. */ public void requestNetwork(NetworkRequest request, PendingIntent operation) { checkPendingIntent(operation); diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index cf747cf..658051c 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -173,12 +173,17 @@ public final class NetworkCapabilities implements Parcelable { * Indicates that connectivity on this network was successfully validated. For example, for a * network with NET_CAPABILITY_INTERNET, it means that Internet connectivity was successfully * detected. - * @hide */ public static final int NET_CAPABILITY_VALIDATED = 16; + /** + * Indicates that this network was found to have a captive portal in place last time it was + * probed. + */ + public static final int NET_CAPABILITY_CAPTIVE_PORTAL = 17; + private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS; - private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_VALIDATED; + private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_CAPTIVE_PORTAL; /** * Adds the given capability to this {@code NetworkCapability} instance. diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 87e8c5e..97b85e2 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -1469,13 +1469,30 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo * </tr> * <tr> * <td>art.gc.gc-count-rate-histogram</td> - * <td>The histogram of the number of garbage collection runs per 10 seconds.</td> + * <td>Every 10 seconds, the gc-count-rate is computed as the number of garbage + * collection runs that have occurred over the last 10 + * seconds. art.gc.gc-count-rate-histogram is a histogram of the gc-count-rate + * samples taken since the process began. The histogram can be used to identify + * instances of high rates of garbage collection runs. For example, a histogram + * of "0:34503,1:45350,2:11281,3:8088,4:43,5:8" shows that most of the time + * there are between 0 and 2 garbage collection runs every 10 seconds, but there + * were 8 distinct 10-second intervals in which 5 garbage collection runs + * occurred.</td> * <td>{@code 0:34503,1:45350,2:11281,3:8088,4:43,5:8}</td> * <td>23</td> * </tr> * <tr> * <td>art.gc.blocking-gc-count-rate-histogram</td> - * <td>The histogram of the number of garbage collection runs per 10 seconds.</td> + * <td>Every 10 seconds, the blocking-gc-count-rate is computed as the number of + * blocking garbage collection runs that have occurred over the last 10 + * seconds. art.gc.blocking-gc-count-rate-histogram is a histogram of the + * blocking-gc-count-rate samples taken since the process began. The histogram + * can be used to identify instances of high rates of blocking garbage + * collection runs. For example, a histogram of "0:99269,1:1,2:1" shows that + * most of the time there are zero blocking garbage collection runs every 10 + * seconds, but there was one 10-second interval in which one blocking garbage + * collection run occurred, and there was one interval in which two blocking + * garbage collection runs occurred.</td> * <td>{@code 0:99269,1:1,2:1}</td> * <td>23</td> * </tr> diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index 1cc2d33..f10b982 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -1919,9 +1919,9 @@ public final class StrictMode { for (int i = 0; i < numViolations; ++i) { if (LOG_V) Log.d(TAG, "strict mode violation stacks read from binder call. i=" + i); ViolationInfo info = new ViolationInfo(p, !currentlyGathering); - if (info.crashInfo.stackTrace != null && info.crashInfo.stackTrace.length() > 10000) { + if (info.crashInfo.stackTrace != null && info.crashInfo.stackTrace.length() > 30000) { String front = info.crashInfo.stackTrace.substring(256); - // 10000 characters is way too large for this to be any sane kind of + // 30000 characters is way too large for this to be any sane kind of // strict mode collection of stacks. We've had a problem where we leave // strict mode violations associated with the thread, and it keeps tacking // more and more stacks on to the violations. Looks like we're in this casse, diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 56cd1a7..9b5fbfa 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6685,6 +6685,17 @@ public final class Settings { "wifi_mobile_data_transition_wakelock_timeout_ms"; /** + * This setting controls whether WiFi configurations created by a Device Owner app + * should be locked down (that is, be editable or removable only by the Device Owner App, + * not even by Settings app). + * This setting takes integer values. Non-zero values mean DO created configurations + * are locked down. Value of zero means they are not. Default value in the absence of + * actual value to this setting is 0. + */ + public static final String WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN = + "wifi_device_owner_configs_lockdown"; + + /** * The operational wifi frequency band * Set to one of {@link WifiManager#WIFI_FREQUENCY_BAND_AUTO}, * {@link WifiManager#WIFI_FREQUENCY_BAND_5GHZ} or diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index 39dd29b..f9e216a 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -1146,20 +1146,7 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall mContentFrame.requestApplyInsets(); } - /** @hide */ - public void onHandleAssist(Bundle assistBundle) { - } - public void onHandleAssist(Bundle data, AssistStructure structure, AssistContent content) { - if (data != null) { - Bundle assistContext = data.getBundle(Intent.EXTRA_ASSIST_CONTEXT); - if (assistContext != null) { - assistContext.putParcelable(AssistStructure.ASSIST_KEY, structure); - assistContext.putParcelable(AssistContent.ASSIST_KEY, content); - data.putBundle(Intent.EXTRA_ASSIST_CONTEXT, assistContext); - } - } - onHandleAssist(data); } public void onHandleScreenshot(Bitmap screenshot) { diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java index 13a959e..47d5c79 100644 --- a/core/java/android/text/format/Formatter.java +++ b/core/java/android/text/format/Formatter.java @@ -16,6 +16,7 @@ package android.text.format; +import android.annotation.Nullable; import android.content.Context; import android.content.res.Resources; import android.net.NetworkUtils; @@ -52,7 +53,10 @@ public final class Formatter { * @param sizeBytes size value to be formatted, in bytes * @return formatted string with the number */ - public static String formatFileSize(Context context, long sizeBytes) { + public static String formatFileSize(@Nullable Context context, long sizeBytes) { + if (context == null) { + return ""; + } final BytesResult res = formatBytes(context.getResources(), sizeBytes, 0); return context.getString(com.android.internal.R.string.fileSizeSuffix, res.value, res.units); @@ -62,7 +66,10 @@ public final class Formatter { * Like {@link #formatFileSize}, but trying to generate shorter numbers * (showing fewer digits of precision). */ - public static String formatShortFileSize(Context context, long sizeBytes) { + public static String formatShortFileSize(@Nullable Context context, long sizeBytes) { + if (context == null) { + return ""; + } final BytesResult res = formatBytes(context.getResources(), sizeBytes, FLAG_SHORTER); return context.getString(com.android.internal.R.string.fileSizeSuffix, res.value, res.units); diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java index 8b74a1e..585fc4e 100644 --- a/core/java/android/transition/Visibility.java +++ b/core/java/android/transition/Visibility.java @@ -504,13 +504,20 @@ public abstract class Visibility extends Transition { private final boolean mIsForcedVisibility; private final View mView; private final int mFinalVisibility; + private final ViewGroup mParent; + private boolean mEnded; boolean mCanceled = false; public DisappearListener(View view, int finalVisibility, boolean isForcedVisibility) { this.mView = view; this.mIsForcedVisibility = isForcedVisibility; this.mFinalVisibility = finalVisibility; + this.mParent = (ViewGroup) view.getParent(); + if (!isForcedVisibility && mParent != null) { + // Prevent a layout from including mView in its calculation. + mParent.suppressLayout(true); + } } @Override @@ -552,13 +559,39 @@ public abstract class Visibility extends Transition { hideViewWhenNotCanceled(); } + @Override + public void onTransitionPause(Transition transition) { + if (mParent != null && !mIsForcedVisibility) { + mParent.suppressLayout(false); + } + } + + @Override + public void onTransitionResume(Transition transition) { + if (mParent != null && !mIsForcedVisibility) { + mParent.suppressLayout(true); + } + } + private void hideViewWhenNotCanceled() { - if (!mCanceled) { - if (mIsForcedVisibility) { - mView.setTransitionAlpha(0); - } else { - mView.setVisibility(mFinalVisibility); + if (!mEnded) { + if (!mCanceled) { + if (mIsForcedVisibility) { + mView.setTransitionAlpha(0); + } else { + // Recreate the parent's display list in case it includes mView. + mView.setTransitionVisibility(mFinalVisibility); + if (mParent != null) { + mParent.invalidate(); + } + } + } + if (!mIsForcedVisibility && mParent != null) { + // Layout is allowed now that the View is in its final state + mParent.suppressLayout(false); } + // Do this only once + mEnded = true; } } } diff --git a/core/java/android/util/Range.java b/core/java/android/util/Range.java index 211d01a..5524506 100644 --- a/core/java/android/util/Range.java +++ b/core/java/android/util/Range.java @@ -350,7 +350,7 @@ public final class Range<T extends Comparable<? super T>> { */ @Override public int hashCode() { - return HashCodeHelpers.hashCode(mLower, mUpper); + return HashCodeHelpers.hashCodeGeneric(mLower, mUpper); } private final T mLower; diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 4394cd8..6026d04 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -1399,6 +1399,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { private static native int nativeGetButtonState(long nativePtr); private static native void nativeSetButtonState(long nativePtr, int buttonState); private static native int nativeGetActionButton(long nativePtr); + private static native void nativeSetActionButton(long nativePtr, int actionButton); private static native void nativeOffsetLocation(long nativePtr, float deltaX, float deltaY); private static native float nativeGetXOffset(long nativePtr); private static native float nativeGetYOffset(long nativePtr); @@ -2284,6 +2285,16 @@ public final class MotionEvent extends InputEvent implements Parcelable { } /** + * Sets the action button for the event. + * + * @see #getActionButton() + * @hide + */ + public final void setActionButton(int button) { + nativeSetActionButton(mNativePtr, button); + } + + /** * Returns the original raw X coordinate of this event. For touch * events on the screen, this is the original location of the event * on the screen, before it had been adjusted for the containing window diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 4074529..5970c3f 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -453,6 +453,19 @@ public class SurfaceControl { } } + /** + * Sets the security of the surface. Setting the flag is equivalent to creating the + * Surface with the {@link #SECURE} flag. + */ + public void setSecure(boolean isSecure) { + checkNotReleased(); + if (isSecure) { + nativeSetFlags(mNativeObject, SECURE, SECURE); + } else { + nativeSetFlags(mNativeObject, 0, SECURE); + } + } + /* * set display parameters. * needs to be inside open/closeTransaction block diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index fd3ee4f..92dae2e 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -6032,7 +6032,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public AccessibilityNodeInfo createAccessibilityNodeInfoInternal() { AccessibilityNodeProvider provider = getAccessibilityNodeProvider(); if (provider != null) { - return provider.createAccessibilityNodeInfo(View.NO_ID); + return provider.createAccessibilityNodeInfo(AccessibilityNodeProvider.HOST_VIEW_ID); } else { AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(this); onInitializeAccessibilityNodeInfo(info); @@ -6326,6 +6326,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @hide */ public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { + if (mAttachInfo == null) { + return; + } + Rect bounds = mAttachInfo.mTmpInvalRect; getDrawingRect(bounds); @@ -8774,7 +8778,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @hide */ public void notifyViewAccessibilityStateChangedIfNeeded(int changeType) { - if (!AccessibilityManager.getInstance(mContext).isEnabled()) { + if (!AccessibilityManager.getInstance(mContext).isEnabled() || mAttachInfo == null) { return; } if (mSendViewStateChangedAccessibilityEvent == null) { @@ -8796,7 +8800,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @hide */ public void notifySubtreeAccessibilityStateChangedIfNeeded() { - if (!AccessibilityManager.getInstance(mContext).isEnabled()) { + if (!AccessibilityManager.getInstance(mContext).isEnabled() || mAttachInfo == null) { return; } if ((mPrivateFlags2 & PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED) == 0) { diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java index d06cd83..3572f1e 100644 --- a/core/java/android/view/ViewStructure.java +++ b/core/java/android/view/ViewStructure.java @@ -150,7 +150,7 @@ public abstract class ViewStructure { * @param size The size, in pixels, of the text. * @param fgColor The foreground color, packed as 0xAARRGGBB. * @param bgColor The background color, packed as 0xAARRGGBB. - * @param style Style flags, as defined by {@link android.app.AssistStructure.ViewNode}. + * @param style Style flags, as defined by {@link android.app.assist.AssistStructure.ViewNode}. */ public abstract void setTextStyle(float size, int fgColor, int bgColor, int style); diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index ab793e0..87706ef 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -1115,7 +1115,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par record.mParcelableData = parcel.readParcelable(null); parcel.readList(record.mText, null); record.mSourceWindowId = parcel.readInt(); - record.mSourceNodeId = parcel.readLong(); + record.mSourceNode = parcel.readParcelable(null); record.mSealed = (parcel.readInt() == 1); } @@ -1167,7 +1167,10 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par parcel.writeParcelable(record.mParcelableData, flags); parcel.writeList(record.mText); parcel.writeInt(record.mSourceWindowId); - parcel.writeLong(record.mSourceNodeId); + // create copy of the node here because the node would be recycled just after it is written + // to parcel + parcel.writeParcelable(record.mSourceNode != null ? + AccessibilityNodeInfo.obtain(record.mSourceNode) : null, flags); parcel.writeInt(record.mSealed ? 1 : 0); } @@ -1191,7 +1194,9 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par builder.append("\n"); builder.append("; ContentChangeTypes: ").append(mContentChangeTypes); builder.append("; sourceWindowId: ").append(mSourceWindowId); - builder.append("; mSourceNodeId: ").append(mSourceNodeId); + if (mSourceNode != null) { + builder.append("; mSourceNodeId: ").append(mSourceNode.getSourceNodeId()); + } for (int i = 0; i < getRecordCount(); i++) { final AccessibilityRecord record = getRecord(i); builder.append(" Record "); diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 36de8f3..86ed499 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -2821,7 +2821,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}. */ private void initFromParcel(Parcel parcel) { - mSealed = (parcel.readInt() == 1); + final boolean sealed = (parcel.readInt() == 1); mSourceNodeId = parcel.readLong(); mWindowId = parcel.readInt(); mParentNodeId = parcel.readLong(); @@ -2911,6 +2911,8 @@ public class AccessibilityNodeInfo implements Parcelable { parcel.readInt() == 1, parcel.readInt() == 1); } + + mSealed = sealed; } /** diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java index cc6a71d..f99690a 100644 --- a/core/java/android/view/accessibility/AccessibilityRecord.java +++ b/core/java/android/view/accessibility/AccessibilityRecord.java @@ -90,7 +90,7 @@ public class AccessibilityRecord { int mAddedCount= UNDEFINED; int mRemovedCount = UNDEFINED; - long mSourceNodeId = AccessibilityNodeInfo.makeNodeId(UNDEFINED, UNDEFINED); + AccessibilityNodeInfo mSourceNode; int mSourceWindowId = UNDEFINED; CharSequence mClassName; @@ -135,16 +135,24 @@ public class AccessibilityRecord { */ public void setSource(View root, int virtualDescendantId) { enforceNotSealed(); - final boolean important; - if (virtualDescendantId == UNDEFINED) { - important = (root != null) ? root.isImportantForAccessibility() : true; - } else { - important = true; + boolean important = true; + mSourceWindowId = UNDEFINED; + clearSourceNode(); + if (root != null) { + if (virtualDescendantId == UNDEFINED || + virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { + important = root.isImportantForAccessibility(); + mSourceNode = root.createAccessibilityNodeInfo(); + } else { + AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider(); + if (provider != null) { + mSourceNode = provider.createAccessibilityNodeInfo(virtualDescendantId); + } + } + + mSourceWindowId = root.getAccessibilityWindowId(); } setBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY, important); - mSourceWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED; - final int rootViewId = (root != null) ? root.getAccessibilityViewId() : UNDEFINED; - mSourceNodeId = AccessibilityNodeInfo.makeNodeId(rootViewId, virtualDescendantId); } /** @@ -158,13 +166,11 @@ public class AccessibilityRecord { */ public AccessibilityNodeInfo getSource() { enforceSealed(); - if (mConnectionId == UNDEFINED || mSourceWindowId == UNDEFINED - || AccessibilityNodeInfo.getAccessibilityViewId(mSourceNodeId) == UNDEFINED) { - return null; + if (mSourceNode != null) { + return AccessibilityNodeInfo.obtain(mSourceNode); } - AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); - return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mSourceWindowId, - mSourceNodeId, false, GET_SOURCE_PREFETCH_FLAGS); + + return null; } /** @@ -619,7 +625,7 @@ public class AccessibilityRecord { * @hide */ public long getSourceNodeId() { - return mSourceNodeId; + return mSourceNode != null ? mSourceNode.getSourceNodeId() : UNDEFINED; } /** @@ -633,6 +639,9 @@ public class AccessibilityRecord { public void setConnectionId(int connectionId) { enforceNotSealed(); mConnectionId = connectionId; + if (mSourceNode != null) { + mSourceNode.setConnectionId(mConnectionId); + } } /** @@ -644,6 +653,9 @@ public class AccessibilityRecord { */ public void setSealed(boolean sealed) { mSealed = sealed; + if (mSourceNode != null) { + mSourceNode.setSealed(sealed); + } } /** @@ -782,7 +794,9 @@ public class AccessibilityRecord { mParcelableData = record.mParcelableData; mText.addAll(record.mText); mSourceWindowId = record.mSourceWindowId; - mSourceNodeId = record.mSourceNodeId; + if (record.mSourceNode != null) { + mSourceNode = AccessibilityNodeInfo.obtain(record.mSourceNode); + } mConnectionId = record.mConnectionId; } @@ -807,11 +821,18 @@ public class AccessibilityRecord { mBeforeText = null; mParcelableData = null; mText.clear(); - mSourceNodeId = AccessibilityNodeInfo.makeNodeId(UNDEFINED, UNDEFINED); + clearSourceNode(); mSourceWindowId = UNDEFINED; mConnectionId = UNDEFINED; } + private void clearSourceNode() { + if (mSourceNode != null) { + mSourceNode.recycle(); + mSourceNode = null; + } + } + @Override public String toString() { StringBuilder builder = new StringBuilder(); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index f733eab..e84ba99 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -25,7 +25,7 @@ import android.annotation.StringRes; import android.annotation.StyleRes; import android.annotation.XmlRes; import android.app.Activity; -import android.app.AssistStructure; +import android.app.assist.AssistStructure; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; @@ -9443,10 +9443,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public void onRtlPropertiesChanged(int layoutDirection) { super.onRtlPropertiesChanged(layoutDirection); - mTextDir = getTextDirectionHeuristic(); - - if (mLayout != null) { - checkForRelayout(); + final TextDirectionHeuristic newTextDir = getTextDirectionHeuristic(); + if (mTextDir != newTextDir) { + mTextDir = newTextDir; + if (mLayout != null) { + checkForRelayout(); + } } } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 678e92b..1b55557 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -100,6 +100,10 @@ public class ChooserActivity extends ResolverActivity { mChooserListAdapter.addServiceResults(sri.originalTarget, sri.resultTargets); unbindService(sri.connection); mServiceConnections.remove(sri.connection); + if (mServiceConnections.isEmpty()) { + mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); + sendVoiceChoicesIfNeeded(); + } break; case CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT: @@ -107,6 +111,7 @@ public class ChooserActivity extends ResolverActivity { Log.d(TAG, "CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT; unbinding services"); } unbindRemainingServices(); + sendVoiceChoicesIfNeeded(); break; default: @@ -384,6 +389,8 @@ public class ChooserActivity extends ResolverActivity { + WATCHDOG_TIMEOUT_MILLIS + "ms"); mChooserHandler.sendEmptyMessageDelayed(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT, WATCHDOG_TIMEOUT_MILLIS); + } else { + sendVoiceChoicesIfNeeded(); } } @@ -418,6 +425,10 @@ public class ChooserActivity extends ResolverActivity { mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); } + void onSetupVoiceInteraction() { + // Do nothing. We'll send the voice stuff ourselves. + } + void onRefinementResult(TargetInfo selectedTarget, Intent matchingIntent) { if (mRefinementResultReceiver != null) { mRefinementResultReceiver.destroy(); @@ -956,6 +967,10 @@ public class ChooserActivity extends ResolverActivity { if (DEBUG) Log.d(TAG, "onServiceDisconnected: " + name); unbindService(this); mServiceConnections.remove(this); + if (mServiceConnections.isEmpty()) { + mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); + sendVoiceChoicesIfNeeded(); + } } @Override diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index e14f058..fe3ab9e 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -16,10 +16,17 @@ package com.android.internal.app; +import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityThread; +import android.app.VoiceInteractor; +import android.app.VoiceInteractor.PickOptionRequest; +import android.app.VoiceInteractor.PickOptionRequest.Option; +import android.app.VoiceInteractor.Prompt; +import android.app.VoiceInteractor.Request; import android.os.AsyncTask; import android.provider.Settings; +import android.service.chooser.ChooserTarget; import android.text.TextUtils; import android.util.Slog; import android.widget.AbsListView; @@ -96,6 +103,7 @@ public class ResolverActivity extends Activity { private int mProfileSwitchMessageId = -1; private final ArrayList<Intent> mIntents = new ArrayList<>(); private ResolverComparator mResolverComparator; + private PickTargetOptionRequest mPickOptionRequest; private boolean mRegistered; private final PackageMonitor mPackageMonitor = new PackageMonitor() { @@ -242,6 +250,9 @@ public class ResolverActivity extends Activity { finish(); } }); + if (isVoiceInteraction()) { + rdl.setCollapsed(false); + } } if (title == null) { @@ -313,6 +324,39 @@ public class ResolverActivity extends Activity { }); bindProfileView(); } + + if (isVoiceInteraction()) { + onSetupVoiceInteraction(); + } + } + + /** + * Perform any initialization needed for voice interaction. + */ + void onSetupVoiceInteraction() { + // Do it right now. Subclasses may delay this and send it later. + sendVoiceChoicesIfNeeded(); + } + + void sendVoiceChoicesIfNeeded() { + if (!isVoiceInteraction()) { + // Clearly not needed. + return; + } + + + final Option[] options = new Option[mAdapter.getCount()]; + for (int i = 0, N = options.length; i < N; i++) { + options[i] = optionForChooserTarget(mAdapter.getItem(i), i); + } + + mPickOptionRequest = new PickTargetOptionRequest( + new Prompt(getTitle()), options, null); + getVoiceInteractor().submitRequest(mPickOptionRequest); + } + + Option optionForChooserTarget(TargetInfo target, int index) { + return new Option(target.getDisplayLabel(), index); } protected final void setAdditionalTargets(Intent[] intents) { @@ -473,6 +517,14 @@ public class ResolverActivity extends Activity { } @Override + protected void onDestroy() { + super.onDestroy(); + if (!isChangingConfigurations() && mPickOptionRequest != null) { + mPickOptionRequest.cancel(); + } + } + + @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); if (mAlwaysUseOption) { @@ -510,16 +562,12 @@ public class ResolverActivity extends Activity { try { ApplicationInfo appInfo = getPackageManager().getApplicationInfo( resolveInfo.activityInfo.packageName, 0 /* default flags */); - return versionNumberAtLeastL(appInfo.targetSdkVersion); + return appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP; } catch (NameNotFoundException e) { return false; } } - private boolean versionNumberAtLeastL(int versionNumber) { - return versionNumber >= Build.VERSION_CODES.LOLLIPOP; - } - private void setAlwaysButtonEnabled(boolean hasValidSelection, int checkedPos, boolean filtered) { boolean enabled = false; @@ -1644,4 +1692,39 @@ public class ResolverActivity extends Activity { && match <= IntentFilter.MATCH_CATEGORY_PATH; } + static class PickTargetOptionRequest extends PickOptionRequest { + public PickTargetOptionRequest(@Nullable Prompt prompt, Option[] options, + @Nullable Bundle extras) { + super(prompt, options, extras); + } + + @Override + public void onCancel() { + super.onCancel(); + final ResolverActivity ra = (ResolverActivity) getActivity(); + if (ra != null) { + ra.mPickOptionRequest = null; + ra.finish(); + } + } + + @Override + public void onPickOptionResult(boolean finished, Option[] selections, Bundle result) { + super.onPickOptionResult(finished, selections, result); + if (selections.length != 1) { + // TODO In a better world we would filter the UI presented here and let the + // user refine. Maybe later. + return; + } + + final ResolverActivity ra = (ResolverActivity) getActivity(); + if (ra != null) { + final TargetInfo ti = ra.mAdapter.getItem(selections[0].getIndex()); + if (ra.onTargetSelected(ti, false)) { + ra.mPickOptionRequest = null; + ra.finish(); + } + } + } + } } diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java index e4ccb4b..b78eca7 100644 --- a/core/java/com/android/internal/logging/MetricsLogger.java +++ b/core/java/com/android/internal/logging/MetricsLogger.java @@ -37,6 +37,9 @@ public class MetricsLogger implements MetricsConstants { public static final int ACTION_ACTIVITY_CHOOSER_PICKED_APP_TARGET = 215; public static final int ACTION_ACTIVITY_CHOOSER_PICKED_SERVICE_TARGET = 216; public static final int ACTION_ACTIVITY_CHOOSER_PICKED_STANDARD_TARGET = 217; + public static final int ACTION_BRIGHTNESS = 218; + public static final int ACTION_BRIGHTNESS_AUTO = 219; + public static final int BRIGHTNESS_DIALOG = 220; // Temporary constants go here, to await migration to MetricsConstants. public static void visible(Context context, int category) throws IllegalArgumentException { diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index 663c838..aea1585 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -19,6 +19,7 @@ package com.android.internal.statusbar; import com.android.internal.statusbar.IStatusBar; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; +import com.android.internal.statusbar.NotificationVisibility; import android.service.notification.StatusBarNotification; /** @hide */ @@ -53,8 +54,8 @@ interface IStatusBarService int uid, int initialPid, String message, int userId); void onClearAllNotifications(int userId); void onNotificationClear(String pkg, String tag, int id, int userId); - void onNotificationVisibilityChanged( - in String[] newlyVisibleKeys, in String[] noLongerVisibleKeys); + void onNotificationVisibilityChanged( in NotificationVisibility[] newlyVisibleKeys, + in NotificationVisibility[] noLongerVisibleKeys); void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded); void setSystemUiVisibility(int vis, int mask, String cause); void setWindowState(int window, int state); diff --git a/core/java/com/android/internal/statusbar/NotificationVisibility.aidl b/core/java/com/android/internal/statusbar/NotificationVisibility.aidl new file mode 100644 index 0000000..c067551 --- /dev/null +++ b/core/java/com/android/internal/statusbar/NotificationVisibility.aidl @@ -0,0 +1,20 @@ +/* + * 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.internal.statusbar; + +parcelable NotificationVisibility; + diff --git a/core/java/com/android/internal/statusbar/NotificationVisibility.java b/core/java/com/android/internal/statusbar/NotificationVisibility.java new file mode 100644 index 0000000..2139ad0 --- /dev/null +++ b/core/java/com/android/internal/statusbar/NotificationVisibility.java @@ -0,0 +1,161 @@ +/* + * 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.internal.statusbar; + +import android.os.Message; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.util.ArrayDeque; +import java.util.Collection; + +public class NotificationVisibility implements Parcelable { + private static final String TAG = "NoViz"; + private static final int MAX_POOL_SIZE = 25; + private static ArrayDeque<NotificationVisibility> sPool = new ArrayDeque<>(MAX_POOL_SIZE); + private static int sNexrId = 0; + + public String key; + public int rank; + public boolean visible = true; + /*package*/ int id; + + private NotificationVisibility() { + id = sNexrId++; + } + + private NotificationVisibility(String key, int rank, boolean visibile) { + this(); + this.key = key; + this.rank = rank; + this.visible = visibile; + } + + @Override + public String toString() { + return "NotificationVisibility(id=" + id + + "key=" + key + + " rank=" + rank + + (visible?" visible":"") + + " )"; + } + + @Override + public NotificationVisibility clone() { + return obtain(this.key, this.rank, this.visible); + } + + @Override + public int hashCode() { + // allow lookups by key, which _should_ never be null. + return key == null ? 0 : key.hashCode(); + } + + @Override + public boolean equals(Object that) { + // allow lookups by key, which _should_ never be null. + if (that instanceof NotificationVisibility) { + NotificationVisibility thatViz = (NotificationVisibility) that; + return (key == null && thatViz.key == null) || key.equals(thatViz.key); + } + return false; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeString(this.key); + out.writeInt(this.rank); + out.writeInt(this.visible ? 1 : 0); + } + + private void readFromParcel(Parcel in) { + this.key = in.readString(); + this.rank = in.readInt(); + this.visible = in.readInt() != 0; + } + + /** + * Return a new NotificationVisibility instance from the global pool. Allows us to + * avoid allocating new objects in many cases. + */ + public static NotificationVisibility obtain(String key, int rank, boolean visible) { + NotificationVisibility vo = obtain(); + vo.key = key; + vo.rank = rank; + vo.visible = visible; + return vo; + } + + private static NotificationVisibility obtain(Parcel in) { + NotificationVisibility vo = obtain(); + vo.readFromParcel(in); + return vo; + } + + private static NotificationVisibility obtain() { + synchronized (sPool) { + if (!sPool.isEmpty()) { + return sPool.poll(); + } + } + return new NotificationVisibility(); + } + + /** + * Return a NotificationVisibility instance to the global pool. + * <p> + * You MUST NOT touch the NotificationVisibility after calling this function because it has + * effectively been freed. + * </p> + */ + public void recycle() { + if (key == null) { + // do nothing on multiple recycles + return; + } + key = null; + if (sPool.size() < MAX_POOL_SIZE) { + synchronized (sPool) { + sPool.offer(this); + } + } + } + + /** + * Parcelable.Creator that instantiates NotificationVisibility objects + */ + public static final Parcelable.Creator<NotificationVisibility> CREATOR + = new Parcelable.Creator<NotificationVisibility>() + { + public NotificationVisibility createFromParcel(Parcel parcel) + { + return obtain(parcel); + } + + public NotificationVisibility[] newArray(int size) + { + return new NotificationVisibility[size]; + } + }; +} + diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index 585cbc9..1071e12 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -144,6 +144,14 @@ public class ResolverDrawerLayout extends ViewGroup { return mCollapseOffset > 0; } + public void setCollapsed(boolean collapsed) { + if (!isLaidOut()) { + mOpenOnLayout = collapsed; + } else { + smoothScrollTo(collapsed ? mCollapsibleHeight : 0, 0); + } + } + private boolean isMoving() { return mIsDragging || !mScroller.isFinished(); } @@ -575,7 +583,13 @@ public class ResolverDrawerLayout extends ViewGroup { @Override public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) { if (!consumed && Math.abs(velocityY) > mMinFlingVelocity) { - smoothScrollTo(velocityY > 0 ? 0 : mCollapsibleHeight, velocityY); + if (mOnDismissedListener != null + && velocityY < 0 && mCollapseOffset > mCollapsibleHeight) { + smoothScrollTo(mCollapsibleHeight + mUncollapsibleHeight, velocityY); + mDismissOnScrollerFinished = true; + } else { + smoothScrollTo(velocityY > 0 ? 0 : mCollapsibleHeight, velocityY); + } return true; } return false; diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp index f5f8b1f..c9d609c 100644 --- a/core/jni/android_hardware_SensorManager.cpp +++ b/core/jni/android_hardware_SensorManager.cpp @@ -257,6 +257,8 @@ private: case SENSOR_TYPE_MAGNETIC_FIELD: case SENSOR_TYPE_ACCELEROMETER: case SENSOR_TYPE_GYROSCOPE: + case SENSOR_TYPE_GRAVITY: + case SENSOR_TYPE_LINEAR_ACCELERATION: status = buffer[i].vector.status; break; case SENSOR_TYPE_HEART_RATE: diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp index 5bef653..63915ed 100644 --- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp +++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp @@ -436,6 +436,23 @@ static jint LegacyCameraDevice_nativeDetectSurfaceType(JNIEnv* env, jobject thiz return fmt; } +static jint LegacyCameraDevice_nativeDetectSurfaceDataspace(JNIEnv* env, jobject thiz, jobject surface) { + ALOGV("nativeDetectSurfaceDataspace"); + sp<ANativeWindow> anw; + if ((anw = getNativeWindow(env, surface)) == NULL) { + ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__); + return BAD_VALUE; + } + int32_t fmt = 0; + status_t err = anw->query(anw.get(), NATIVE_WINDOW_DEFAULT_DATASPACE, &fmt); + if(err != NO_ERROR) { + ALOGE("%s: Error while querying surface dataspace %s (%d).", __FUNCTION__, strerror(-err), + err); + return err; + } + return fmt; +} + static jint LegacyCameraDevice_nativeDetectSurfaceDimens(JNIEnv* env, jobject thiz, jobject surface, jintArray dimens) { ALOGV("nativeGetSurfaceDimens"); @@ -717,6 +734,9 @@ static JNINativeMethod gCameraDeviceMethods[] = { { "nativeDetectSurfaceType", "(Landroid/view/Surface;)I", (void *)LegacyCameraDevice_nativeDetectSurfaceType }, + { "nativeDetectSurfaceDataspace", + "(Landroid/view/Surface;)I", + (void *)LegacyCameraDevice_nativeDetectSurfaceDataspace }, { "nativeDetectSurfaceDimens", "(Landroid/view/Surface;[I)I", (void *)LegacyCameraDevice_nativeDetectSurfaceDimens }, diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp index 91a3c7e..0e2ec6b 100644 --- a/core/jni/android_view_DisplayEventReceiver.cpp +++ b/core/jni/android_view_DisplayEventReceiver.cpp @@ -77,7 +77,7 @@ NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak, const sp<MessageQueue>& messageQueue) : mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)), mMessageQueue(messageQueue), mWaitingForVsync(false) { - ALOGV("receiver %p ~ Initializing input event receiver.", this); + ALOGV("receiver %p ~ Initializing display event receiver.", this); } NativeDisplayEventReceiver::~NativeDisplayEventReceiver() { diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp index eb28c4d..98c17c0 100644 --- a/core/jni/android_view_MotionEvent.cpp +++ b/core/jni/android_view_MotionEvent.cpp @@ -462,6 +462,12 @@ static int android_view_MotionEvent_nativeGetActionButton(JNIEnv* env, jclass cl return event->getActionButton(); } +static void android_view_MotionEvent_nativeSetActionButton(JNIEnv* env, jclass clazz, + jlong nativePtr, jint button) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + event->setActionButton(button); +} + static jboolean android_view_MotionEvent_nativeIsTouchEvent(JNIEnv* env, jclass clazz, jlong nativePtr) { MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); @@ -779,6 +785,9 @@ static JNINativeMethod gMotionEventMethods[] = { { "nativeGetActionButton", "(J)I", (void*)android_view_MotionEvent_nativeGetActionButton}, + { "nativeSetActionButton", + "(JI)V", + (void*)android_view_MotionEvent_nativeSetActionButton}, { "nativeIsTouchEvent", "(J)Z", (void*)android_view_MotionEvent_nativeIsTouchEvent }, diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index b1b772a..f9b41a9 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2457,6 +2457,7 @@ <intent-filter> <action android:name="android.intent.action.CHOOSER" /> <category android:name="android.intent.category.DEFAULT" /> + <category android:name="android.intent.category.VOICE" /> </intent-filter> </activity> <activity android:name="com.android.internal.app.IntentForwarderActivity" diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml index 18c83b4..9548e51 100644 --- a/core/res/res/xml/sms_short_codes.xml +++ b/core/res/res/xml/sms_short_codes.xml @@ -93,11 +93,11 @@ <!-- France: 5 digits, free: 3xxxx, premium [4-8]xxxx, plus EU: http://clients.txtnation.com/entries/161972-france-premium-sms-short-code-requirements --> - <shortcode country="fr" premium="[4-8]\\d{4}" free="3\\d{4}|116\\d{3}" /> + <shortcode country="fr" premium="[4-8]\\d{4}" free="3\\d{4}|116\\d{3}|21101" /> <!-- United Kingdom (Great Britain): 4-6 digits, common codes [5-8]xxxx, plus EU: http://www.short-codes.com/media/Co-regulatoryCodeofPracticeforcommonshortcodes170206.pdf --> - <shortcode country="gb" pattern="\\d{4,6}" premium="[5-8]\\d{4}" free="116\\d{3}" /> + <shortcode country="gb" pattern="\\d{4,6}" premium="[5-8]\\d{4}" free="116\\d{3}|887" /> <!-- Georgia: 4 digits, known premium codes listed --> <shortcode country="ge" pattern="\\d{4}" premium="801[234]|888[239]" /> @@ -184,6 +184,6 @@ <shortcode country="ua" pattern="\\d{4}" premium="444[3-9]|70[579]4|7540" /> <!-- USA: 5-6 digits (premium codes from https://www.premiumsmsrefunds.com/ShortCodes.htm) --> - <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" free="87902" /> + <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" free="122|87902" /> </shortcodes> diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java index 250bad7..515be1d 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java @@ -113,7 +113,7 @@ public class AndroidKeyStoreKeyFactorySpi extends KeyFactorySpi { return result; } else { throw new InvalidKeySpecException( - "Obtaining RSAPublicKeySpec not supported for " + key.getAlgorithm() + " " + "Obtaining ECPublicKeySpec not supported for " + key.getAlgorithm() + " " + ((key instanceof AndroidKeyStorePrivateKey) ? "private" : "public") + " key"); } diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java index 532b330..ff265cf 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -511,15 +511,23 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato return generateSelfSignedCertificateWithFakeSignature(publicKey); } else { // Key can be used to sign a certificate - return generateSelfSignedCertificateWithValidSignature( - privateKey, publicKey, signatureAlgorithm); + try { + return generateSelfSignedCertificateWithValidSignature( + privateKey, publicKey, signatureAlgorithm); + } catch (Exception e) { + // Failed to generate the self-signed certificate with valid signature. Fall back + // to generating a self-signed certificate with a fake signature. This is done for + // all exception types because we prefer key pair generation to succeed and end up + // producing a self-signed certificate with an invalid signature to key pair + // generation failing. + return generateSelfSignedCertificateWithFakeSignature(publicKey); + } } } @SuppressWarnings("deprecation") private X509Certificate generateSelfSignedCertificateWithValidSignature( - PrivateKey privateKey, PublicKey publicKey, String signatureAlgorithm) - throws Exception { + PrivateKey privateKey, PublicKey publicKey, String signatureAlgorithm) throws Exception { final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator(); certGen.setPublicKey(publicKey); certGen.setSerialNumber(mSpec.getCertificateSerialNumber()); diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index 3d23399..919dd48 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -71,6 +71,8 @@ import javax.security.auth.x500.X500Principal; * <li>{@link KeyProperties#PURPOSE_SIGN},</li> * <li>operation without requiring the user to be authenticated (see * {@link Builder#setUserAuthenticationRequired(boolean)}),</li> + * <li>signing/origination at this moment in time (see {@link Builder#setKeyValidityStart(Date)} + * and {@link Builder#setKeyValidityForOriginationEnd(Date)}),</li> * <li>suitable digest or {@link KeyProperties#DIGEST_NONE},</li> * <li>(RSA keys only) padding scheme {@link KeyProperties#SIGNATURE_PADDING_RSA_PKCS1} or * {@link KeyProperties#ENCRYPTION_PADDING_NONE}.</li> diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index d9b40ae..00add29 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -14,15 +14,13 @@ * limitations under the License. */ -#define LOG_TAG "OpenGLRenderer" -#define ATRACE_TAG ATRACE_TAG_VIEW - #include "LayerCache.h" #include "LayerRenderer.h" #include "Matrix.h" #include "Properties.h" #include "Rect.h" #include "renderstate/RenderState.h" +#include "utils/GLUtils.h" #include "utils/TraceUtils.h" #include <ui/Rect.h> @@ -238,8 +236,9 @@ Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width layer->allocateTexture(); // This should only happen if we run out of memory - if (glGetError() != GL_NO_ERROR) { - ALOGE("Could not allocate texture for layer (fbo=%d %dx%d)", fbo, width, height); + if (CC_UNLIKELY(GLUtils::dumpGLErrors())) { + LOG_ALWAYS_FATAL("Could not allocate texture for layer (fbo=%d %dx%d)", + fbo, width, height); renderState.bindFramebuffer(previousFbo); layer->decStrong(nullptr); return nullptr; diff --git a/libs/hwui/utils/GLUtils.cpp b/libs/hwui/utils/GLUtils.cpp index 9b298ca..55104de 100644 --- a/libs/hwui/utils/GLUtils.cpp +++ b/libs/hwui/utils/GLUtils.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "OpenGLRenderer" - #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> @@ -26,9 +24,11 @@ namespace android { namespace uirenderer { -void GLUtils::dumpGLErrors() { +bool GLUtils::dumpGLErrors() { + bool errorObserved = false; GLenum status = GL_NO_ERROR; while ((status = glGetError()) != GL_NO_ERROR) { + errorObserved = true; switch (status) { case GL_INVALID_ENUM: ALOGE("GL error: GL_INVALID_ENUM"); @@ -46,6 +46,7 @@ void GLUtils::dumpGLErrors() { ALOGE("GL error: 0x%x", status); } } + return errorObserved; } }; // namespace uirenderer diff --git a/libs/hwui/utils/GLUtils.h b/libs/hwui/utils/GLUtils.h index 890e374..7020461 100644 --- a/libs/hwui/utils/GLUtils.h +++ b/libs/hwui/utils/GLUtils.h @@ -20,12 +20,11 @@ namespace android { namespace uirenderer { class GLUtils { -private: public: /** - * Print out any GL errors with ALOGE + * Print out any GL errors with ALOGE, returns true if any errors were found. */ - static void dumpGLErrors(); + static bool dumpGLErrors(); }; // class GLUtils diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java index 9609c35..d303a2e 100644 --- a/media/java/android/media/CamcorderProfile.java +++ b/media/java/android/media/CamcorderProfile.java @@ -150,6 +150,25 @@ public class CamcorderProfile /** * High speed ( >= 100fps) quality level corresponding to the lowest available resolution. + * <p> + * For all the high speed profiles defined below ((from {@link #QUALITY_HIGH_SPEED_LOW} to + * {@link #QUALITY_HIGH_SPEED_2160P}), they are similar as normal recording profiles, with just + * higher output frame rate and bit rate. Therefore, setting these profiles with + * {@link MediaRecorder#setProfile} without specifying any other encoding parameters will + * produce high speed videos rather than slow motion videos that have different capture and + * output (playback) frame rates. To record slow motion videos, the application must set video + * output (playback) frame rate and bit rate appropriately via + * {@link MediaRecorder#setVideoFrameRate} and {@link MediaRecorder#setVideoEncodingBitRate} + * based on the slow motion factor. If the application intends to do the video recording with + * {@link MediaCodec} encoder, it must set each individual field of {@link MediaFormat} + * similarly according to this CamcorderProfile. + * </p> + * + * @see #videoBitRate + * @see #videoFrameRate + * @see MediaRecorder + * @see MediaCodec + * @see MediaFormat */ public static final int QUALITY_HIGH_SPEED_LOW = 2000; @@ -212,11 +231,56 @@ public class CamcorderProfile /** * The target video output bit rate in bits per second + * <p> + * This is the target recorded video output bit rate if the application configures the video + * recording via {@link MediaRecorder#setProfile} without specifying any other + * {@link MediaRecorder} encoding parameters. For example, for high speed quality profiles (from + * {@link #QUALITY_HIGH_SPEED_LOW} to {@link #QUALITY_HIGH_SPEED_2160P}), this is the bit rate + * where the video is recorded with. If the application intends to record slow motion videos + * with the high speed quality profiles, it must set a different video bit rate that is + * corresponding to the desired recording output bit rate (i.e., the encoded video bit rate + * during normal playback) via {@link MediaRecorder#setVideoEncodingBitRate}. For example, if + * {@link #QUALITY_HIGH_SPEED_720P} advertises 240fps {@link #videoFrameRate} and 64Mbps + * {@link #videoBitRate} in the high speed CamcorderProfile, and the application intends to + * record 1/8 factor slow motion recording videos, the application must set 30fps via + * {@link MediaRecorder#setVideoFrameRate} and 8Mbps ( {@link #videoBitRate} * slow motion + * factor) via {@link MediaRecorder#setVideoEncodingBitRate}. Failing to do so will result in + * videos with unexpected frame rate and bit rate, or {@link MediaRecorder} error if the output + * bit rate exceeds the encoder limit. If the application intends to do the video recording with + * {@link MediaCodec} encoder, it must set each individual field of {@link MediaFormat} + * similarly according to this CamcorderProfile. + * </p> + * + * @see #videoFrameRate + * @see MediaRecorder + * @see MediaCodec + * @see MediaFormat */ public int videoBitRate; /** - * The target video frame rate in frames per second + * The target video frame rate in frames per second. + * <p> + * This is the target recorded video output frame rate per second if the application configures + * the video recording via {@link MediaRecorder#setProfile} without specifying any other + * {@link MediaRecorder} encoding parameters. For example, for high speed quality profiles (from + * {@link #QUALITY_HIGH_SPEED_LOW} to {@link #QUALITY_HIGH_SPEED_2160P}), this is the frame rate + * where the video is recorded and played back with. If the application intends to create slow + * motion use case with the high speed quality profiles, it must set a different video frame + * rate that is corresponding to the desired output (playback) frame rate via + * {@link MediaRecorder#setVideoFrameRate}. For example, if {@link #QUALITY_HIGH_SPEED_720P} + * advertises 240fps {@link #videoFrameRate} in the CamcorderProfile, and the application + * intends to create 1/8 factor slow motion recording videos, the application must set 30fps via + * {@link MediaRecorder#setVideoFrameRate}. Failing to do so will result in high speed videos + * with normal speed playback frame rate (240fps for above example). If the application intends + * to do the video recording with {@link MediaCodec} encoder, it must set each individual field + * of {@link MediaFormat} similarly according to this CamcorderProfile. + * </p> + * + * @see #videoBitRate + * @see MediaRecorder + * @see MediaCodec + * @see MediaFormat */ public int videoFrameRate; diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java index da5f33e..7cd086e 100644 --- a/media/java/android/media/tv/TvContract.java +++ b/media/java/android/media/tv/TvContract.java @@ -710,7 +710,7 @@ public final class TvContract { public static final String COLUMN_LOCKED = "locked"; /** - * The app badge icon of the app link template for this channel. + * The URI for the app badge icon of the app link template for this channel. * * <p>This small icon is overlaid at the bottom of the poster art specified by * {@link #COLUMN_APP_LINK_POSTER_ART_URI}. The data in the column must be a URI in one of @@ -736,9 +736,10 @@ public final class TvContract { public static final String COLUMN_APP_LINK_ICON_URI = "app_link_icon_uri"; /** - * The poster art used as the background of the app link template for this channel. + * The URI for the poster art used as the background of the app link template for this + * channel. * - * <p>The data in the column must be a URL or a URI in one of the following formats: + * <p>The data in the column must be a URL, or a URI in one of the following formats: * * <ul> * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li> @@ -1100,6 +1101,15 @@ public final class TvContract { /** * The URI for the poster art of this TV program. * + * <p>The data in the column must be a URL, or a URI in one of the following formats: + * + * <ul> + * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li> + * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE}) + * </li> + * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li> + * </ul> + * * <p>Can be empty. * * <p>Type: TEXT @@ -1109,6 +1119,19 @@ public final class TvContract { /** * The URI for the thumbnail of this TV program. * + * <p>The system can generate a thumbnail from the poster art if this column is not + * specified. Thus it is not necessary for TV input services to include a thumbnail if it is + * just a scaled image of the poster art. + * + * <p>The data in the column must be a URL, or a URI in one of the following formats: + * + * <ul> + * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li> + * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE}) + * </li> + * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li> + * </ul> + * * <p>Can be empty. * * <p>Type: TEXT diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java index f4e4ea1..ef08e19 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java @@ -346,7 +346,7 @@ public class WifiTracker { accessPoint.update(mLastInfo, mLastNetworkInfo); } - if (result.passpointNetwork) { + if (result.isPasspointNetwork()) { WifiConfiguration config = mWifiManager.getMatchingWifiConfig(result); if (config != null) { accessPoint.update(config); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index aff6ad8..44b9d8b 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -904,16 +904,16 @@ public class SettingsProvider extends ContentProvider { private boolean mutateSystemSetting(String name, String value, int runAsUserId, int operation) { - // Make sure the caller can change the settings. - enforceWritePermission(Manifest.permission.WRITE_SETTINGS); + // Check for permissions first. + hasPermissionsToMutateSystemSettings(); // Verify whether this operation is allowed for the calling package. if (!isAppOpWriteSettingsAllowedForCallingPackage()) { return false; } - // Enforce what the calling package can mutate in the system settings. - enforceRestrictedSystemSettingsMutationForCallingPackageLocked(operation, name); + // Enforce what the calling package can mutate the system settings. + enforceRestrictedSystemSettingsMutationForCallingPackage(operation, name); // Resolve the userId on whose behalf the call is made. final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(runAsUserId); @@ -954,6 +954,28 @@ public class SettingsProvider extends ContentProvider { } } + private boolean hasPermissionsToMutateSystemSettings() { + // Write secure settings is a more protected permission. If caller has it we are good. + if (getContext().checkCallingOrSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) + == PackageManager.PERMISSION_GRANTED) { + return true; + } + + // The write settings permission gates mutation of system settings. + if (getContext().checkCallingOrSelfPermission(Manifest.permission.WRITE_SETTINGS) + == PackageManager.PERMISSION_GRANTED) { + return true; + } + + // Excpet we let system apps change system settings without the permission. + PackageInfo packageInfo = getCallingPackageInfoOrThrow(); + if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + return true; + } + + return false; + } + private void validateSystemSettingValue(String name, String value) { Settings.System.Validator validator = Settings.System.VALIDATORS.get(name); if (validator != null && !validator.validate(value)) { @@ -1000,7 +1022,7 @@ public class SettingsProvider extends ContentProvider { return userId; } - private void enforceRestrictedSystemSettingsMutationForCallingPackageLocked(int operation, + private void enforceRestrictedSystemSettingsMutationForCallingPackage(int operation, String name) { // System/root/shell can mutate whatever secure settings they want. final int callingUid = Binder.getCallingUid(); diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index 674356b..7838119 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java @@ -311,4 +311,12 @@ public class AssistManager { public void showDisclosure() { mAssistDisclosure.postShow(); } + + public void onUserSwitched(int newUserId) { + updateAssistInfo(); + } + + public void prepareBeforeInvocation() { + updateAssistInfo(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java index d6a16fa..77c27fa 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java +++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java @@ -30,6 +30,8 @@ import android.os.UserHandle; import android.provider.Settings; import android.widget.ImageView; +import com.android.internal.logging.MetricsLogger; + import java.util.ArrayList; public class BrightnessController implements ToggleSlider.Listener { @@ -195,12 +197,16 @@ public class BrightnessController implements ToggleSlider.Listener { } @Override - public void onChanged(ToggleSlider view, boolean tracking, boolean automatic, int value) { + public void onChanged(ToggleSlider view, boolean tracking, boolean automatic, int value, + boolean stopTracking) { updateIcon(mAutomatic); if (mExternalChange) return; if (!mAutomatic) { final int val = value + mMinimumBacklight; + if (stopTracking) { + MetricsLogger.action(mContext, MetricsLogger.ACTION_BRIGHTNESS, val); + } setBrightness(val); if (!tracking) { AsyncTask.execute(new Runnable() { @@ -213,6 +219,9 @@ public class BrightnessController implements ToggleSlider.Listener { } } else { final float adj = value / (BRIGHTNESS_ADJ_RESOLUTION / 2f) - 1; + if (stopTracking) { + MetricsLogger.action(mContext, MetricsLogger.ACTION_BRIGHTNESS_AUTO, value); + } setBrightnessAdj(adj); if (!tracking) { AsyncTask.execute(new Runnable() { diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java index 74267a5..cef4d34 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java +++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java @@ -24,6 +24,7 @@ import android.view.Window; import android.view.WindowManager; import android.widget.ImageView; +import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; /** A dialog that provides controls for adjusting the screen brightness. */ @@ -52,11 +53,13 @@ public class BrightnessDialog extends Activity { protected void onStart() { super.onStart(); mBrightnessController.registerCallbacks(); + MetricsLogger.visible(this, MetricsLogger.BRIGHTNESS_DIALOG); } @Override protected void onStop() { super.onStop(); + MetricsLogger.hidden(this, MetricsLogger.BRIGHTNESS_DIALOG); mBrightnessController.unregisterCallbacks(); } diff --git a/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java b/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java index cdb8e69..d247711 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java +++ b/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java @@ -35,7 +35,8 @@ import com.android.systemui.statusbar.policy.BrightnessMirrorController; public class ToggleSlider extends RelativeLayout { public interface Listener { public void onInit(ToggleSlider v); - public void onChanged(ToggleSlider v, boolean tracking, boolean checked, int value); + public void onChanged(ToggleSlider v, boolean tracking, boolean checked, int value, + boolean stopTracking); } private Listener mListener; @@ -143,7 +144,7 @@ public class ToggleSlider extends RelativeLayout { if (mListener != null) { mListener.onChanged( - ToggleSlider.this, mTracking, checked, mSlider.getProgress()); + ToggleSlider.this, mTracking, checked, mSlider.getProgress(), false); } if (mMirror != null) { @@ -157,7 +158,7 @@ public class ToggleSlider extends RelativeLayout { public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (mListener != null) { mListener.onChanged( - ToggleSlider.this, mTracking, mToggle.isChecked(), progress); + ToggleSlider.this, mTracking, mToggle.isChecked(), progress, false); } } @@ -166,8 +167,8 @@ public class ToggleSlider extends RelativeLayout { mTracking = true; if (mListener != null) { - mListener.onChanged( - ToggleSlider.this, mTracking, mToggle.isChecked(), mSlider.getProgress()); + mListener.onChanged(ToggleSlider.this, mTracking, mToggle.isChecked(), + mSlider.getProgress(), false); } mToggle.setChecked(false); @@ -183,8 +184,8 @@ public class ToggleSlider extends RelativeLayout { mTracking = false; if (mListener != null) { - mListener.onChanged( - ToggleSlider.this, mTracking, mToggle.isChecked(), mSlider.getProgress()); + mListener.onChanged(ToggleSlider.this, mTracking, mToggle.isChecked(), + mSlider.getProgress(), true); } if (mMirrorController != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 295fdc8..9d6204c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -1613,17 +1613,11 @@ public abstract class BaseStatusBar extends SystemUI implements /** * The LEDs are turned off when the notification panel is shown, even just a little bit. - * This was added last-minute and is inconsistent with the way the rest of the notifications - * are handled, because the notification isn't really cancelled. The lights are just - * turned off. If any other notifications happen, the lights will turn back on. Steve says - * this is what he wants. (see bug 1131461) */ protected void handleVisibleToUserChanged(boolean visibleToUser) { try { if (visibleToUser) { - // Only stop blinking, vibrating, ringing when the user went into the shade - // manually (SHADE or SHADE_LOCKED). - boolean clearNotificationEffects = + boolean clearNotificationEffects = mShowLockscreenNotifications || (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED); mBarService.onPanelRevealed(clearNotificationEffects); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 16df64c..9e6dd7e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -96,6 +96,7 @@ import android.widget.ImageView; import android.widget.TextView; import com.android.internal.logging.MetricsLogger; +import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.statusbar.StatusBarIcon; import com.android.keyguard.KeyguardHostView.OnDismissAction; import com.android.keyguard.ViewMediatorCallback; @@ -457,7 +458,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private int mDisabledUnmodified2; /** Keys of notifications currently visible to the user. */ - private final ArraySet<String> mCurrentlyVisibleNotifications = new ArraySet<String>(); + private final ArraySet<NotificationVisibility> mCurrentlyVisibleNotifications = + new ArraySet<>(); private long mLastVisibilityReportUptimeMs; private final ShadeUpdates mShadeUpdates = new ShadeUpdates(); @@ -471,9 +473,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private int mLastLoggedStateFingerprint; private static final int VISIBLE_LOCATIONS = StackViewState.LOCATION_FIRST_CARD - | StackViewState.LOCATION_TOP_STACK_PEEKING - | StackViewState.LOCATION_MAIN_AREA - | StackViewState.LOCATION_BOTTOM_STACK_PEEKING; + | StackViewState.LOCATION_MAIN_AREA; private final OnChildLocationsChangedListener mNotificationLocationsChangedListener = new OnChildLocationsChangedListener() { @@ -498,12 +498,17 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // Tracks notifications currently visible in mNotificationStackScroller and // emits visibility events via NoMan on changes. private final Runnable mVisibilityReporter = new Runnable() { - private final ArrayList<String> mTmpNewlyVisibleNotifications = new ArrayList<String>(); - private final ArrayList<String> mTmpCurrentlyVisibleNotifications = new ArrayList<String>(); + private final ArraySet<NotificationVisibility> mTmpNewlyVisibleNotifications = + new ArraySet<>(); + private final ArraySet<NotificationVisibility> mTmpCurrentlyVisibleNotifications = + new ArraySet<>(); + private final ArraySet<NotificationVisibility> mTmpNoLongerVisibleNotifications = + new ArraySet<>(); @Override public void run() { mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis(); + final String mediaKey = getCurrentMediaNotificationKey(); // 1. Loop over mNotificationData entries: // A. Keep list of visible notifications. @@ -518,31 +523,45 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, for (int i = 0; i < N; i++) { Entry entry = activeNotifications.get(i); String key = entry.notification.getKey(); - boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(key); - boolean currentlyVisible = + boolean isVisible = (mStackScroller.getChildLocation(entry.row) & VISIBLE_LOCATIONS) != 0; - if (currentlyVisible) { + NotificationVisibility visObj = NotificationVisibility.obtain(key, i, isVisible); + boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(visObj); + if (isVisible) { // Build new set of visible notifications. - mTmpCurrentlyVisibleNotifications.add(key); - } - if (!previouslyVisible && currentlyVisible) { - mTmpNewlyVisibleNotifications.add(key); + mTmpCurrentlyVisibleNotifications.add(visObj); + if (!previouslyVisible) { + mTmpNewlyVisibleNotifications.add(visObj); + } + } else { + // release object + visObj.recycle(); } } - ArraySet<String> noLongerVisibleNotifications = mCurrentlyVisibleNotifications; - noLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications); + mTmpNoLongerVisibleNotifications.addAll(mCurrentlyVisibleNotifications); + mTmpNoLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications); logNotificationVisibilityChanges( - mTmpNewlyVisibleNotifications, noLongerVisibleNotifications); + mTmpNewlyVisibleNotifications, mTmpNoLongerVisibleNotifications); - mCurrentlyVisibleNotifications.clear(); + recycleAllVisibilityObjects(mCurrentlyVisibleNotifications); mCurrentlyVisibleNotifications.addAll(mTmpCurrentlyVisibleNotifications); - mTmpNewlyVisibleNotifications.clear(); + recycleAllVisibilityObjects(mTmpNoLongerVisibleNotifications); mTmpCurrentlyVisibleNotifications.clear(); + mTmpNewlyVisibleNotifications.clear(); + mTmpNoLongerVisibleNotifications.clear(); } }; + private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) { + final int N = array.size(); + for (int i = 0 ; i < N; i++) { + array.valueAt(i).recycle(); + } + array.clear(); + } + private final View.OnClickListener mOverflowClickListener = new View.OnClickListener() { @Override public void onClick(View v) { @@ -1028,6 +1047,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private int mShowSearchHoldoff = 0; private Runnable mInvokeAssist = new Runnable() { public void run() { + mAssistManager.prepareBeforeInvocation(); invokeAssistGesture(true /* vibrate */); awakenDreams(); if (mNavigationBarView != null) { @@ -2917,6 +2937,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, updateNotifications(); resetUserSetupObserver(); setControllerUsers(); + mAssistManager.onUserSwitched(newUserId); } private void setControllerUsers() { @@ -2987,9 +3008,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // Report all notifications as invisible and turn down the // reporter. if (!mCurrentlyVisibleNotifications.isEmpty()) { - logNotificationVisibilityChanges( - Collections.<String>emptyList(), mCurrentlyVisibleNotifications); - mCurrentlyVisibleNotifications.clear(); + logNotificationVisibilityChanges(Collections.<NotificationVisibility>emptyList(), + mCurrentlyVisibleNotifications); + recycleAllVisibilityObjects(mCurrentlyVisibleNotifications); } mHandler.removeCallbacks(mVisibilityReporter); mStackScroller.setChildLocationsChangedListener(null); @@ -3007,18 +3028,27 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } private void logNotificationVisibilityChanges( - Collection<String> newlyVisible, Collection<String> noLongerVisible) { + Collection<NotificationVisibility> newlyVisible, + Collection<NotificationVisibility> noLongerVisible) { if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) { return; } - String[] newlyVisibleAr = newlyVisible.toArray(new String[newlyVisible.size()]); - String[] noLongerVisibleAr = noLongerVisible.toArray(new String[noLongerVisible.size()]); + NotificationVisibility[] newlyVisibleAr = + newlyVisible.toArray(new NotificationVisibility[newlyVisible.size()]); + NotificationVisibility[] noLongerVisibleAr = + noLongerVisible.toArray(new NotificationVisibility[noLongerVisible.size()]); try { mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr); } catch (RemoteException e) { // Ignore. } - setNotificationsShown(newlyVisibleAr); + + final int N = newlyVisible.size(); + String[] newlyVisibleKeyAr = new String[N]; + for (int i = 0; i < N; i++) { + newlyVisibleKeyAr[i] = newlyVisibleAr[i].key; + } + setNotificationsShown(newlyVisibleKeyAr); } // State logging diff --git a/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java b/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java index 2b76c31..a5b244e 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java @@ -150,13 +150,23 @@ public class QsTuner extends Fragment implements Callback { } public void onStartDrag() { - mDropTarget.setVisibility(View.VISIBLE); - mAddTarget.setVisibility(View.GONE); + mDropTarget.post(new Runnable() { + @Override + public void run() { + mDropTarget.setVisibility(View.VISIBLE); + mAddTarget.setVisibility(View.GONE); + } + }); } public void stopDrag() { - mDropTarget.setVisibility(View.GONE); - mAddTarget.setVisibility(View.VISIBLE); + mDropTarget.post(new Runnable() { + @Override + public void run() { + mDropTarget.setVisibility(View.GONE); + mAddTarget.setVisibility(View.VISIBLE); + } + }); } @Override @@ -230,9 +240,16 @@ public class QsTuner extends Fragment implements Callback { public void showAddDialog() { List<String> tiles = mTileSpecs; + int numBroadcast = 0; + for (int i = 0; i < tiles.size(); i++) { + if (tiles.get(i).startsWith(IntentTile.PREFIX)) { + numBroadcast++; + } + } String[] defaults = getContext().getString(R.string.quick_settings_tiles_default).split(","); - final String[] available = new String[defaults.length + 1 - tiles.size()]; + final String[] available = new String[defaults.length + 1 + - (tiles.size() - numBroadcast)]; final String[] availableTiles = new String[available.length]; int index = 0; for (int i = 0; i < defaults.length; i++) { diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 25d4d5e..d9e5b75 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -23,6 +23,7 @@ import static android.net.ConnectivityManager.TYPE_NONE; import static android.net.ConnectivityManager.TYPE_VPN; import static android.net.ConnectivityManager.getNetworkTypeName; import static android.net.ConnectivityManager.isNetworkTypeValid; +import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; @@ -1869,7 +1870,14 @@ public class ConnectivityService extends IConnectivityManager.Stub if (nai == null) { loge("EVENT_NETWORK_CAPABILITIES_CHANGED from unknown NetworkAgent"); } else { - updateCapabilities(nai, (NetworkCapabilities)msg.obj); + final NetworkCapabilities networkCapabilities = + (NetworkCapabilities)msg.obj; + if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL) || + networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) { + Slog.wtf(TAG, "BUG: " + nai + " has stateful capability."); + } + updateCapabilities(nai, networkCapabilities, + NascentState.NOT_JUST_VALIDATED); } break; } @@ -1956,20 +1964,16 @@ public class ConnectivityService extends IConnectivityManager.Stub if (isLiveNetworkAgent(nai, "EVENT_NETWORK_TESTED")) { final boolean valid = (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID); - final boolean validationChanged = (valid != nai.lastValidated); - nai.lastValidated = valid; - if (valid) { - if (DBG) log("Validated " + nai.name()); - nai.networkCapabilities.addCapability(NET_CAPABILITY_VALIDATED); - if (!nai.everValidated) { - nai.everValidated = true; - rematchNetworkAndRequests(nai, NascentState.JUST_VALIDATED, - ReapUnvalidatedNetworks.REAP); - // If score has changed, rebroadcast to NetworkFactories. b/17726566 - sendUpdatedScoreToFactories(nai); - } - } else { - nai.networkCapabilities.removeCapability(NET_CAPABILITY_VALIDATED); + if (DBG) log(nai.name() + " validation " + (valid ? " passed" : "failed")); + if (valid != nai.lastValidated) { + final int oldScore = nai.getCurrentScore(); + final NascentState nascent = (valid && !nai.everValidated) ? + NascentState.JUST_VALIDATED : NascentState.NOT_JUST_VALIDATED; + nai.lastValidated = valid; + nai.everValidated |= valid; + updateCapabilities(nai, nai.networkCapabilities, nascent); + // If score has changed, rebroadcast to NetworkFactories. b/17726566 + if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai); } updateInetCondition(nai); // Let the NetworkAgent know the state of its network @@ -1977,10 +1981,6 @@ public class ConnectivityService extends IConnectivityManager.Stub android.net.NetworkAgent.CMD_REPORT_NETWORK_STATUS, (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK), 0, null); - - if (validationChanged) { - notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED); - } } break; } @@ -1993,18 +1993,25 @@ public class ConnectivityService extends IConnectivityManager.Stub } case NetworkMonitor.EVENT_PROVISIONING_NOTIFICATION: { final int netId = msg.arg2; - if (msg.arg1 == 0) { + final boolean visible = (msg.arg1 != 0); + final NetworkAgentInfo nai; + synchronized (mNetworkForNetId) { + nai = mNetworkForNetId.get(netId); + } + // If captive portal status has changed, update capabilities. + if (nai != null && (visible != nai.lastCaptivePortalDetected)) { + nai.lastCaptivePortalDetected = visible; + nai.everCaptivePortalDetected |= visible; + updateCapabilities(nai, nai.networkCapabilities, + NascentState.NOT_JUST_VALIDATED); + } + if (!visible) { setProvNotificationVisibleIntent(false, netId, null, 0, null, null); } else { - final NetworkAgentInfo nai; - synchronized (mNetworkForNetId) { - nai = mNetworkForNetId.get(netId); - } if (nai == null) { loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor"); break; } - nai.captivePortalDetected = true; setProvNotificationVisibleIntent(true, netId, NotificationType.SIGN_IN, nai.networkInfo.getType(),nai.networkInfo.getExtraInfo(), (PendingIntent)msg.obj); @@ -2390,7 +2397,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (accept != nai.networkMisc.acceptUnvalidated) { int oldScore = nai.getCurrentScore(); nai.networkMisc.acceptUnvalidated = accept; - rematchAllNetworksAndRequests(nai, oldScore); + rematchAllNetworksAndRequests(nai, oldScore, NascentState.NOT_JUST_VALIDATED); sendUpdatedScoreToFactories(nai); } @@ -2426,7 +2433,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // Only prompt if the network is unvalidated and was explicitly selected by the user, and if // we haven't already been told to switch to it regardless of whether it validated or not. // Also don't prompt on captive portals because we're already prompting the user to sign in. - if (nai == null || nai.everValidated || nai.captivePortalDetected || + if (nai == null || nai.everValidated || nai.everCaptivePortalDetected || !nai.networkMisc.explicitlySelected || nai.networkMisc.acceptUnvalidated) { return; } @@ -3571,12 +3578,24 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + private void ensureImmutableCapabilities(NetworkCapabilities networkCapabilities) { + if (networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) { + throw new IllegalArgumentException( + "Cannot request network with NET_CAPABILITY_VALIDATED"); + } + if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) { + throw new IllegalArgumentException( + "Cannot request network with NET_CAPABILITY_CAPTIVE_PORTAL"); + } + } + @Override public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities, Messenger messenger, int timeoutMs, IBinder binder, int legacyType) { networkCapabilities = new NetworkCapabilities(networkCapabilities); enforceNetworkRequestPermissions(networkCapabilities); enforceMeteredApnPolicy(networkCapabilities); + ensureImmutableCapabilities(networkCapabilities); if (timeoutMs < 0 || timeoutMs > ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_MS) { throw new IllegalArgumentException("Bad timeout specified"); @@ -3645,6 +3664,7 @@ public class ConnectivityService extends IConnectivityManager.Stub networkCapabilities = new NetworkCapabilities(networkCapabilities); enforceNetworkRequestPermissions(networkCapabilities); enforceMeteredApnPolicy(networkCapabilities); + ensureImmutableCapabilities(networkCapabilities); NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE, nextNetworkRequestId()); @@ -4046,18 +4066,35 @@ public class ConnectivityService extends IConnectivityManager.Stub mNumDnsEntries = last; } + /** + * Update the NetworkCapabilities for {@code networkAgent} to {@code networkCapabilities} + * augmented with any stateful capabilities implied from {@code networkAgent} + * (e.g., validated status and captive portal status). + * + * @param networkAgent the network having its capabilities updated. + * @param networkCapabilities the new network capabilities. + * @param nascent indicates whether {@code networkAgent} was validated + * (i.e. had everValidated set for the first time) immediately prior to this call. + */ private void updateCapabilities(NetworkAgentInfo networkAgent, - NetworkCapabilities networkCapabilities) { + NetworkCapabilities networkCapabilities, NascentState nascent) { + // Don't modify caller's NetworkCapabilities. + networkCapabilities = new NetworkCapabilities(networkCapabilities); + if (networkAgent.lastValidated) { + networkCapabilities.addCapability(NET_CAPABILITY_VALIDATED); + } else { + networkCapabilities.removeCapability(NET_CAPABILITY_VALIDATED); + } + if (networkAgent.lastCaptivePortalDetected) { + networkCapabilities.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL); + } else { + networkCapabilities.removeCapability(NET_CAPABILITY_CAPTIVE_PORTAL); + } if (!Objects.equals(networkAgent.networkCapabilities, networkCapabilities)) { synchronized (networkAgent) { networkAgent.networkCapabilities = networkCapabilities; } - if (networkAgent.lastValidated) { - networkAgent.networkCapabilities.addCapability(NET_CAPABILITY_VALIDATED); - // There's no need to remove the capability if we think the network is unvalidated, - // because NetworkAgents don't set the validated capability. - } - rematchAllNetworksAndRequests(networkAgent, networkAgent.getCurrentScore()); + rematchAllNetworksAndRequests(networkAgent, networkAgent.getCurrentScore(), nascent); notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_CAP_CHANGED); } } @@ -4401,15 +4438,21 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - // Attempt to rematch all Networks with NetworkRequests. This may result in Networks - // being disconnected. - // If only one Network's score or capabilities have been modified since the last time - // this function was called, pass this Network in via the "changed" arugment, otherwise - // pass null. - // If only one Network has been changed but its NetworkCapabilities have not changed, - // pass in the Network's score (from getCurrentScore()) prior to the change via - // "oldScore", otherwise pass changed.getCurrentScore() or 0 if "changed" is null. - private void rematchAllNetworksAndRequests(NetworkAgentInfo changed, int oldScore) { + /** + * Attempt to rematch all Networks with NetworkRequests. This may result in Networks + * being disconnected. + * @param changed If only one Network's score or capabilities have been modified since the last + * time this function was called, pass this Network in this argument, otherwise pass + * null. + * @param oldScore If only one Network has been changed but its NetworkCapabilities have not + * changed, pass in the Network's score (from getCurrentScore()) prior to the change via + * this argument, otherwise pass {@code changed.getCurrentScore()} or 0 if + * {@code changed} is {@code null}. This is because NetworkCapabilities influence a + * network's score. + * @param nascent indicates if {@code changed} has just been validated. + */ + private void rematchAllNetworksAndRequests(NetworkAgentInfo changed, int oldScore, + NascentState nascent) { // TODO: This may get slow. The "changed" parameter is provided for future optimization // to avoid the slowness. It is not simply enough to process just "changed", for // example in the case where "changed"'s score decreases and another network should begin @@ -4418,9 +4461,9 @@ public class ConnectivityService extends IConnectivityManager.Stub // Optimization: Only reprocess "changed" if its score improved. This is safe because it // can only add more NetworkRequests satisfied by "changed", and this is exactly what // rematchNetworkAndRequests() handles. - if (changed != null && oldScore < changed.getCurrentScore()) { - rematchNetworkAndRequests(changed, NascentState.NOT_JUST_VALIDATED, - ReapUnvalidatedNetworks.REAP); + if (changed != null && + (oldScore < changed.getCurrentScore() || nascent == NascentState.JUST_VALIDATED)) { + rematchNetworkAndRequests(changed, nascent, ReapUnvalidatedNetworks.REAP); } else { for (Iterator i = mNetworkAgentInfos.values().iterator(); i.hasNext(); ) { rematchNetworkAndRequests((NetworkAgentInfo)i.next(), @@ -4547,7 +4590,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final int oldScore = nai.getCurrentScore(); nai.setCurrentScore(score); - rematchAllNetworksAndRequests(nai, oldScore); + rematchAllNetworksAndRequests(nai, oldScore, NascentState.NOT_JUST_VALIDATED); sendUpdatedScoreToFactories(nai); } diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags index 49d4c22..43b640b 100644 --- a/services/core/java/com/android/server/EventLogTags.logtags +++ b/services/core/java/com/android/server/EventLogTags.logtags @@ -74,7 +74,7 @@ option java_package com.android.server # when a notification has been canceled 27530 notification_canceled (key|3),(reason|1),(lifespan|1),(freshness|1),(exposure|1) # replaces 27510 with a row per notification -27531 notification_visibility (key|3),(visibile|1),(lifespan|1),(freshness|1) +27531 notification_visibility (key|3),(visibile|1),(lifespan|1),(freshness|1),(exposure|1),(rank|1) # a notification emited noise, vibration, or light 27532 notification_alert (key|3),(buzz|1),(beep|1),(blink|1) diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index baa55e7..7afb192 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -214,9 +214,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub */ @GuardedBy("mQuotaLock") private SparseIntArray mUidFirewallDozableRules = new SparseIntArray(); - - private boolean mStandbyChainEnabled = false; - private boolean mDozableChainEnabled = false; + /** Set of states for the child firewall chains. True if the chain is active. */ + @GuardedBy("mQuotaLock") + final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray(); private Object mIdleTimerLock = new Object(); /** Set of interfaces with active idle timers. */ @@ -307,9 +307,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub } public void systemReady() { - // init firewall states - mDozableChainEnabled = false; - mStandbyChainEnabled = true; prepareNativeDaemon(); if (DBG) Slog.d(TAG, "Prepared"); } @@ -611,7 +608,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub uidFirewallRules.valueAt(i)); } } - if (mStandbyChainEnabled) { + if (mFirewallChainStates.get(FIREWALL_CHAIN_STANDBY)) { setFirewallChainEnabled(FIREWALL_CHAIN_STANDBY, true); } @@ -625,7 +622,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub uidFirewallRules.valueAt(i)); } } - if (mDozableChainEnabled) { + if (mFirewallChainStates.get(FIREWALL_CHAIN_DOZABLE)) { setFirewallChainEnabled(FIREWALL_CHAIN_DOZABLE, true); } } @@ -2013,24 +2010,31 @@ public class NetworkManagementService extends INetworkManagementService.Stub @Override public void setFirewallChainEnabled(int chain, boolean enable) { enforceSystemUid(); - final String operation = enable ? "enable_chain" : "disable_chain"; - try { - String chainName; - switch(chain) { - case FIREWALL_CHAIN_STANDBY: - chainName = FIREWALL_CHAIN_NAME_STANDBY; - mStandbyChainEnabled = enable; - break; - case FIREWALL_CHAIN_DOZABLE: - chainName = FIREWALL_CHAIN_NAME_DOZABLE; - mDozableChainEnabled = enable; - break; - default: - throw new IllegalArgumentException("Bad child chain: " + chain); + synchronized (mQuotaLock) { + if (mFirewallChainStates.indexOfKey(chain) >= 0 && + mFirewallChainStates.get(chain) == enable) { + // All is the same, nothing to do. + return; + } + mFirewallChainStates.put(chain, enable); + + final String operation = enable ? "enable_chain" : "disable_chain"; + try { + String chainName; + switch(chain) { + case FIREWALL_CHAIN_STANDBY: + chainName = FIREWALL_CHAIN_NAME_STANDBY; + break; + case FIREWALL_CHAIN_DOZABLE: + chainName = FIREWALL_CHAIN_NAME_DOZABLE; + break; + default: + throw new IllegalArgumentException("Bad child chain: " + chain); + } + mConnector.execute("firewall", operation, chainName); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); } - mConnector.execute("firewall", operation, chainName); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); } } @@ -2048,27 +2052,29 @@ public class NetworkManagementService extends INetworkManagementService.Stub @Override public void setFirewallUidRules(int chain, int[] uids, int[] rules) { enforceSystemUid(); - SparseIntArray uidFirewallRules = getUidFirewallRules(chain); - SparseIntArray newRules = new SparseIntArray(); - // apply new set of rules - for (int index = uids.length - 1; index >= 0; --index) { - int uid = uids[index]; - int rule = rules[index]; - setFirewallUidRule(chain, uid, rule); - newRules.put(uid, rule); - } - // collect the rules to remove. - SparseIntArray rulesToRemove = new SparseIntArray(); - for (int index = uidFirewallRules.size() - 1; index >= 0; --index) { - int uid = uidFirewallRules.keyAt(index); - if (newRules.indexOfKey(uid) < 0) { - rulesToRemove.put(uid, FIREWALL_RULE_DEFAULT); + synchronized (mQuotaLock) { + SparseIntArray uidFirewallRules = getUidFirewallRules(chain); + SparseIntArray newRules = new SparseIntArray(); + // apply new set of rules + for (int index = uids.length - 1; index >= 0; --index) { + int uid = uids[index]; + int rule = rules[index]; + setFirewallUidRule(chain, uid, rule); + newRules.put(uid, rule); + } + // collect the rules to remove. + SparseIntArray rulesToRemove = new SparseIntArray(); + for (int index = uidFirewallRules.size() - 1; index >= 0; --index) { + int uid = uidFirewallRules.keyAt(index); + if (newRules.indexOfKey(uid) < 0) { + rulesToRemove.put(uid, FIREWALL_RULE_DEFAULT); + } + } + // remove dead rules + for (int index = rulesToRemove.size() - 1; index >= 0; --index) { + int uid = rulesToRemove.keyAt(index); + setFirewallUidRuleInternal(chain, uid, FIREWALL_RULE_DEFAULT); } - } - // remove dead rules - for (int index = rulesToRemove.size() - 1; index >= 0; --index) { - int uid = rulesToRemove.keyAt(index); - setFirewallUidRuleInternal(chain, uid, FIREWALL_RULE_DEFAULT); } } @@ -2094,34 +2100,43 @@ public class NetworkManagementService extends INetworkManagementService.Stub } try { - String ruleName; - if (getFirewallType(chain) == FIREWALL_TYPE_WHITELIST) { - if (rule == NetworkPolicyManager.FIREWALL_RULE_ALLOW) { - ruleName = "allow"; - } else { - ruleName = "deny"; - } - } else { // Blacklist mode - if (rule == NetworkPolicyManager.FIREWALL_RULE_DENY) { - ruleName = "deny"; - } else { - ruleName = "allow"; - } - } + String ruleName = getFirewallRuleName(chain, rule); + String oldRuleName = getFirewallRuleName(chain, oldUidFirewallRule); if (rule == NetworkPolicyManager.FIREWALL_RULE_DEFAULT) { uidFirewallRules.delete(uid); } else { uidFirewallRules.put(uid, rule); } - mConnector.execute("firewall", "set_uid_rule", getFirewallChainName(chain), uid, - ruleName); + + if (!ruleName.equals(oldRuleName)) { + mConnector.execute("firewall", "set_uid_rule", getFirewallChainName(chain), uid, + ruleName); + } } catch (NativeDaemonConnectorException e) { throw e.rethrowAsParcelableException(); } } } + private @NonNull String getFirewallRuleName(int chain, int rule) { + String ruleName; + if (getFirewallType(chain) == FIREWALL_TYPE_WHITELIST) { + if (rule == NetworkPolicyManager.FIREWALL_RULE_ALLOW) { + ruleName = "allow"; + } else { + ruleName = "deny"; + } + } else { // Blacklist mode + if (rule == NetworkPolicyManager.FIREWALL_RULE_DENY) { + ruleName = "deny"; + } else { + ruleName = "allow"; + } + } + return ruleName; + } + private @NonNull SparseIntArray getUidFirewallRules(int chain) { switch (chain) { case FIREWALL_CHAIN_STANDBY: @@ -2272,7 +2287,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub pw.println("]"); } - pw.println("UID firewall standby chain enabled: " + mStandbyChainEnabled); + pw.println("UID firewall standby chain enabled: " + + mFirewallChainStates.get(FIREWALL_CHAIN_STANDBY)); synchronized (mUidFirewallStandbyRules) { pw.print("UID firewall standby rule: ["); final int size = mUidFirewallStandbyRules.size(); @@ -2285,7 +2301,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub pw.println("]"); } - pw.println("UID firewall dozable chain enabled: " + mDozableChainEnabled); + pw.println("UID firewall dozable chain enabled: " + + mFirewallChainStates.get(FIREWALL_CHAIN_DOZABLE)); synchronized (mUidFirewallDozableRules) { pw.print("UID firewall dozable rule: ["); final int size = mUidFirewallDozableRules.size(); diff --git a/services/core/java/com/android/server/camera/CameraService.java b/services/core/java/com/android/server/camera/CameraService.java index 9347c24..0be24f4 100644 --- a/services/core/java/com/android/server/camera/CameraService.java +++ b/services/core/java/com/android/server/camera/CameraService.java @@ -16,7 +16,10 @@ package com.android.server.camera; import android.app.ActivityManager; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.UserInfo; import android.hardware.ICameraService; import android.hardware.ICameraServiceProxy; @@ -67,6 +70,32 @@ public class CameraService extends SystemService implements Handler.Callback { private final Object mLock = new Object(); private Set<Integer> mEnabledCameraUsers; + private int mLastUser; + + private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (action == null) return; + + switch (action) { + case Intent.ACTION_USER_ADDED: + case Intent.ACTION_USER_REMOVED: + case Intent.ACTION_USER_INFO_CHANGED: + case Intent.ACTION_MANAGED_PROFILE_ADDED: + case Intent.ACTION_MANAGED_PROFILE_REMOVED: + synchronized(mLock) { + // Return immediately if we haven't seen any users start yet + if (mEnabledCameraUsers == null) return; + switchUserLocked(mLastUser); + } + break; + default: + break; // do nothing + } + + } + }; private final ICameraServiceProxy.Stub mCameraServiceProxy = new ICameraServiceProxy.Stub() { @Override @@ -103,6 +132,15 @@ public class CameraService extends SystemService implements Handler.Callback { // Should never see this unless someone messes up the SystemServer service boot order. throw new IllegalStateException("UserManagerService must start before CameraService!"); } + + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_USER_ADDED); + filter.addAction(Intent.ACTION_USER_REMOVED); + filter.addAction(Intent.ACTION_USER_INFO_CHANGED); + filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); + filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); + mContext.registerReceiver(mIntentReceiver, filter); + publishBinderService(CAMERA_SERVICE_PROXY_BINDER_NAME, mCameraServiceProxy); } @@ -125,6 +163,7 @@ public class CameraService extends SystemService implements Handler.Callback { private void switchUserLocked(int userHandle) { Set<Integer> currentUserHandles = getEnabledUserHandles(userHandle); + mLastUser = userHandle; if (mEnabledCameraUsers == null || !mEnabledCameraUsers.equals(currentUserHandles)) { // Some user handles have been added or removed, update mediaserver. mEnabledCameraUsers = currentUserHandles; diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 3bf1183..51c6628 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -45,6 +45,7 @@ public class NetworkAgentInfo { // This Network object is always valid. public final Network network; public LinkProperties linkProperties; + // This should only be modified via ConnectivityService.updateCapabilities(). public NetworkCapabilities networkCapabilities; public final NetworkMonitor networkMonitor; public final NetworkMisc networkMisc; @@ -66,7 +67,10 @@ public class NetworkAgentInfo { // Whether a captive portal was ever detected on this network. // This is a sticky bit; once set it is never cleared. - public boolean captivePortalDetected; + public boolean everCaptivePortalDetected; + + // Whether a captive portal was found during the last network validation attempt. + public boolean lastCaptivePortalDetected; // This represents the last score received from the NetworkAgent. private int currentScore; @@ -174,7 +178,8 @@ public class NetworkAgentInfo { "created{" + created + "} " + "explicitlySelected{" + networkMisc.explicitlySelected + "} " + "acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} " + - "captivePortalDetected{" + captivePortalDetected + "} " + + "everCaptivePortalDetected{" + everCaptivePortalDetected + "} " + + "lastCaptivePortalDetected{" + lastCaptivePortalDetected + "} " + "}"; } diff --git a/services/core/java/com/android/server/content/AppIdleMonitor.java b/services/core/java/com/android/server/content/AppIdleMonitor.java index 9598de8..fe5c2da 100644 --- a/services/core/java/com/android/server/content/AppIdleMonitor.java +++ b/services/core/java/com/android/server/content/AppIdleMonitor.java @@ -18,11 +18,6 @@ package com.android.server.content; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.BatteryManager; import android.os.UserHandle; import com.android.server.LocalServices; @@ -31,53 +26,32 @@ import com.android.server.LocalServices; * Helper to listen for app idle and charging status changes and restart backed off * sync operations. */ -class AppIdleMonitor implements AppIdleStateChangeListener { +class AppIdleMonitor extends AppIdleStateChangeListener { private final SyncManager mSyncManager; private final UsageStatsManagerInternal mUsageStats; - final BatteryManager mBatteryManager; - /** Is the device currently plugged into power. */ - private boolean mPluggedIn; + private boolean mAppIdleParoleOn; - private final BroadcastReceiver mReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - onPluggedIn(mBatteryManager.isCharging()); - } - }; - - AppIdleMonitor(SyncManager syncManager, Context context) { + AppIdleMonitor(SyncManager syncManager) { mSyncManager = syncManager; mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class); - mUsageStats.addAppIdleStateChangeListener(this); - mBatteryManager = context.getSystemService(BatteryManager.class); - mPluggedIn = isPowered(); - registerReceivers(context); - } - - private void registerReceivers(Context context) { - // Monitor battery charging state - IntentFilter filter = new IntentFilter(BatteryManager.ACTION_CHARGING); - filter.addAction(BatteryManager.ACTION_DISCHARGING); - context.registerReceiver(mReceiver, filter); - } + mAppIdleParoleOn = mUsageStats.isAppIdleParoleOn(); - private boolean isPowered() { - return mBatteryManager.isCharging(); + mUsageStats.addAppIdleStateChangeListener(this); } - void onPluggedIn(boolean pluggedIn) { - if (mPluggedIn == pluggedIn) { + void setAppIdleParoleOn(boolean appIdleParoleOn) { + if (mAppIdleParoleOn == appIdleParoleOn) { return; } - mPluggedIn = pluggedIn; - if (mPluggedIn) { + mAppIdleParoleOn = appIdleParoleOn; + if (mAppIdleParoleOn) { mSyncManager.onAppNotIdle(null, UserHandle.USER_ALL); } } boolean isAppIdle(String packageName, int userId) { - return !mPluggedIn && mUsageStats.isAppIdle(packageName, userId); + return !mAppIdleParoleOn && mUsageStats.isAppIdle(packageName, userId); } @Override @@ -86,4 +60,9 @@ class AppIdleMonitor implements AppIdleStateChangeListener { if (idle) return; mSyncManager.onAppNotIdle(packageName, userId); } + + @Override + public void onParoleStateChanged(boolean isParoleOn) { + setAppIdleParoleOn(isParoleOn); + } } diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 7415b0e..658f6f8 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -19,6 +19,7 @@ package com.android.server.content; import android.accounts.Account; import android.accounts.AccountAndUser; import android.accounts.AccountManager; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.AlarmManager; import android.app.AppGlobals; @@ -461,7 +462,7 @@ public class SyncManager { mSyncAlarmIntent = PendingIntent.getBroadcast( mContext, 0 /* ignored */, new Intent(ACTION_SYNC_ALARM), 0); - mAppIdleMonitor = new AppIdleMonitor(this, mContext); + mAppIdleMonitor = new AppIdleMonitor(this); IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); context.registerReceiver(mConnectivityIntentReceiver, intentFilter); @@ -1271,7 +1272,7 @@ public class SyncManager { * @param userId The user for which the package has become active. Can be USER_ALL if * the device just plugged in. */ - void onAppNotIdle(String packageName, int userId) { + void onAppNotIdle(@Nullable String packageName, int userId) { synchronized (mSyncQueue) { // For all sync operations in sync queue, if marked as idle, compare with package name // and unmark. And clear backoff for the operation. diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 35fbef6..8d2687b 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -231,6 +231,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // The elapsed real time when the screen on was blocked. private long mScreenOnBlockStartRealTime; + // True if we told the window manager policy that the screen was off. + private boolean mReportedScreenOffToPolicy; + // Remembers whether certain kinds of brightness adjustments // were recently applied so that we can decide how to transition. private boolean mAppliedAutoBrightness; @@ -764,24 +767,30 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } catch (RemoteException ex) { // same process } + } - // Tell the window manager what's happening. - // Temporarily block turning the screen on until the window manager is ready - // by leaving a black surface covering the screen. This surface is essentially - // the final state of the color fade animation. - boolean isOn = (state != Display.STATE_OFF); - if (wasOn && !isOn) { + // Tell the window manager policy when the screen is turned off or on unless it's due + // to the proximity sensor. We temporarily block turning the screen on until the + // window manager is ready by leaving a black surface covering the screen. + // This surface is essentially the final state of the color fade animation and + // it is only removed once the window manager tells us that the activity has + // finished drawing underneath. + final boolean isOff = (state == Display.STATE_OFF); + if (isOff && !mReportedScreenOffToPolicy && !mScreenOffBecauseOfProximity) { + mReportedScreenOffToPolicy = true; + unblockScreenOn(); + mWindowManagerPolicy.screenTurnedOff(); + } else if (!isOff && mReportedScreenOffToPolicy) { + mReportedScreenOffToPolicy = false; + if (mPowerState.getColorFadeLevel() == 0.0f) { + blockScreenOn(); + } else { unblockScreenOn(); - mWindowManagerPolicy.screenTurnedOff(); - } else if (!wasOn && isOn) { - if (mPowerState.getColorFadeLevel() == 0.0f) { - blockScreenOn(); - } else { - unblockScreenOn(); - } - mWindowManagerPolicy.screenTurningOn(mPendingScreenOnUnblocker); } + mWindowManagerPolicy.screenTurningOn(mPendingScreenOnUnblocker); } + + // Return true if the screen isn't blocked. return mPendingScreenOnUnblocker == null; } @@ -1086,6 +1095,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println(" mAppliedLowPower=" + mAppliedLowPower); pw.println(" mPendingScreenOnUnblocker=" + mPendingScreenOnUnblocker); pw.println(" mPendingScreenOff=" + mPendingScreenOff); + pw.println(" mReportedScreenOffToPolicy=" + mReportedScreenOffToPolicy); pw.println(" mScreenBrightnessRampAnimator.isAnimating()=" + mScreenBrightnessRampAnimator.isAnimating()); diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java index 98fb11b..02d4f40 100644 --- a/services/core/java/com/android/server/job/controllers/AppIdleController.java +++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java @@ -17,12 +17,7 @@ package com.android.server.job.controllers; import android.app.usage.UsageStatsManagerInternal; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.BatteryManager; -import android.os.BatteryManagerInternal; import android.util.Slog; import com.android.server.LocalServices; @@ -38,8 +33,7 @@ import java.util.ArrayList; * for a certain amount of time (maybe hours or days) are considered idle. When the app comes * out of idle state, it will be allowed to run scheduled jobs. */ -public class AppIdleController extends StateController - implements UsageStatsManagerInternal.AppIdleStateChangeListener { +public class AppIdleController extends StateController { private static final String LOG_TAG = "AppIdleController"; private static final boolean DEBUG = false; @@ -49,14 +43,7 @@ public class AppIdleController extends StateController private static volatile AppIdleController sController; final ArrayList<JobStatus> mTrackedTasks = new ArrayList<JobStatus>(); private final UsageStatsManagerInternal mUsageStatsInternal; - private final BatteryManager mBatteryManager; - private boolean mPluggedIn; - - private final BroadcastReceiver mReceiver = new BroadcastReceiver() { - @Override public void onReceive(Context context, Intent intent) { - onPluggedIn(mBatteryManager.isCharging()); - } - }; + boolean mAppIdleParoleOn; public static AppIdleController get(JobSchedulerService service) { synchronized (sCreationLock) { @@ -70,17 +57,8 @@ public class AppIdleController extends StateController private AppIdleController(StateChangedListener stateChangedListener, Context context) { super(stateChangedListener, context); mUsageStatsInternal = LocalServices.getService(UsageStatsManagerInternal.class); - mBatteryManager = context.getSystemService(BatteryManager.class); - mPluggedIn = mBatteryManager.isCharging(); - mUsageStatsInternal.addAppIdleStateChangeListener(this); - registerReceivers(); - } - - private void registerReceivers() { - // Monitor battery charging state - IntentFilter filter = new IntentFilter(BatteryManager.ACTION_CHARGING); - filter.addAction(BatteryManager.ACTION_DISCHARGING); - mContext.registerReceiver(mReceiver, filter); + mAppIdleParoleOn = mUsageStatsInternal.isAppIdleParoleOn(); + mUsageStatsInternal.addAppIdleStateChangeListener(new AppIdleStateChangeListener()); } @Override @@ -88,7 +66,7 @@ public class AppIdleController extends StateController synchronized (mTrackedTasks) { mTrackedTasks.add(jobStatus); String packageName = jobStatus.job.getService().getPackageName(); - final boolean appIdle = !mPluggedIn && mUsageStatsInternal.isAppIdle(packageName, + final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName, jobStatus.getUserId()); if (DEBUG) { Slog.d(LOG_TAG, "Start tracking, setting idle state of " @@ -108,7 +86,7 @@ public class AppIdleController extends StateController @Override public void dumpControllerState(PrintWriter pw) { pw.println("AppIdle"); - pw.println("Plugged In: " + mPluggedIn); + pw.println("Parole On: " + mAppIdleParoleOn); synchronized (mTrackedTasks) { for (JobStatus task : mTrackedTasks) { pw.print(task.job.getService().getPackageName()); @@ -119,48 +97,20 @@ public class AppIdleController extends StateController } } - @Override - public void onAppIdleStateChanged(String packageName, int userId, boolean idle) { - boolean changed = false; - synchronized (mTrackedTasks) { - // If currently plugged in, we don't care about app idle state - if (mPluggedIn) { - return; - } - for (JobStatus task : mTrackedTasks) { - if (task.job.getService().getPackageName().equals(packageName) - && task.getUserId() == userId) { - if (task.appNotIdleConstraintSatisfied.get() != !idle) { - if (DEBUG) { - Slog.d(LOG_TAG, "App Idle state changed, setting idle state of " - + packageName + " to " + idle); - } - task.appNotIdleConstraintSatisfied.set(!idle); - changed = true; - } - } - } - } - if (changed) { - mStateChangedListener.onControllerStateChanged(); - } - } - - void onPluggedIn(boolean pluggedIn) { + void setAppIdleParoleOn(boolean isAppIdleParoleOn) { // Flag if any app's idle state has changed boolean changed = false; synchronized (mTrackedTasks) { - if (mPluggedIn == pluggedIn) { + if (mAppIdleParoleOn == isAppIdleParoleOn) { return; } - mPluggedIn = pluggedIn; + mAppIdleParoleOn = isAppIdleParoleOn; for (JobStatus task : mTrackedTasks) { String packageName = task.job.getService().getPackageName(); - final boolean appIdle = !mPluggedIn && mUsageStatsInternal.isAppIdle(packageName, + final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName, task.getUserId()); if (DEBUG) { - Slog.d(LOG_TAG, "Plugged in " + pluggedIn + ", setting idle state of " - + packageName + " to " + appIdle); + Slog.d(LOG_TAG, "Setting idle state of " + packageName + " to " + appIdle); } if (task.appNotIdleConstraintSatisfied.get() == appIdle) { task.appNotIdleConstraintSatisfied.set(!appIdle); @@ -172,4 +122,41 @@ public class AppIdleController extends StateController mStateChangedListener.onControllerStateChanged(); } } + + private class AppIdleStateChangeListener + extends UsageStatsManagerInternal.AppIdleStateChangeListener { + @Override + public void onAppIdleStateChanged(String packageName, int userId, boolean idle) { + boolean changed = false; + synchronized (mTrackedTasks) { + if (mAppIdleParoleOn) { + return; + } + for (JobStatus task : mTrackedTasks) { + if (task.job.getService().getPackageName().equals(packageName) + && task.getUserId() == userId) { + if (task.appNotIdleConstraintSatisfied.get() != !idle) { + if (DEBUG) { + Slog.d(LOG_TAG, "App Idle state changed, setting idle state of " + + packageName + " to " + idle); + } + task.appNotIdleConstraintSatisfied.set(!idle); + changed = true; + } + } + } + } + if (changed) { + mStateChangedListener.onControllerStateChanged(); + } + } + + @Override + public void onParoleStateChanged(boolean isParoleOn) { + if (DEBUG) { + Slog.d(LOG_TAG, "Parole on: " + isParoleOn); + } + setAppIdleParoleOn(isParoleOn); + } + } } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index b0550d6..847bcb5 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -86,7 +86,6 @@ import android.app.IUidObserver; import android.app.Notification; import android.app.PendingIntent; import android.app.usage.UsageStatsManagerInternal; -import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -154,7 +153,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; -import com.android.server.DeviceIdleController; import com.android.server.LocalServices; import com.google.android.collect.Lists; @@ -182,8 +180,7 @@ import java.util.List; * and delivers to listeners, such as {@link ConnectivityManager}, for * enforcement. */ -public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub - implements AppIdleStateChangeListener { +public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final String TAG = "NetworkPolicy"; private static final boolean LOGD = false; private static final boolean LOGV = false; @@ -279,6 +276,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub final SparseIntArray mUidPolicy = new SparseIntArray(); /** Currently derived rules for each UID. */ final SparseIntArray mUidRules = new SparseIntArray(); + /** Set of states for the child firewall chains. True if the chain is active. */ final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray(); /** @@ -508,7 +506,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub WifiManager.NETWORK_STATE_CHANGED_ACTION); mContext.registerReceiver(mWifiStateReceiver, wifiStateFilter, null, mHandler); - mUsageStats.addAppIdleStateChangeListener(this); + mUsageStats.addAppIdleStateChangeListener(new AppIdleStateChangeListener()); } @@ -2043,7 +2041,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub } setUidFirewallRules(FIREWALL_CHAIN_DOZABLE, uidRules); } - enableFirewallChain(FIREWALL_CHAIN_DOZABLE, mDeviceIdleMode); + enableFirewallChainLocked(FIREWALL_CHAIN_DOZABLE, mDeviceIdleMode); + } + + void updateRulesForAppIdleParoleLocked() { + boolean enableChain = !mUsageStats.isAppIdleParoleOn(); + enableFirewallChainLocked(FIREWALL_CHAIN_STANDBY, enableChain); } /** @@ -2187,9 +2190,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub final boolean firewallReject = (uidRules & RULE_REJECT_ALL) != 0; if (oldFirewallReject != firewallReject) { setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, firewallReject); - if (mDeviceIdleMode && !firewallReject) { - // if we are in device idle mode, and we decide to allow this uid. we need to punch - // a hole in the device idle chain. + if (mFirewallChainStates.get(FIREWALL_CHAIN_DOZABLE) && !firewallReject) { + // if the dozable chain is on, and we decide to allow this uid. we need to punch + // a hole in the dozable chain. setUidFirewallRule(FIREWALL_CHAIN_DOZABLE, uid, false); } } @@ -2207,15 +2210,25 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub } } - @Override - public void onAppIdleStateChanged(String packageName, int userId, boolean idle) { - try { - int uid = mContext.getPackageManager().getPackageUid(packageName, userId); + private class AppIdleStateChangeListener + extends UsageStatsManagerInternal.AppIdleStateChangeListener { + + @Override + public void onAppIdleStateChanged(String packageName, int userId, boolean idle) { + try { + int uid = mContext.getPackageManager().getPackageUid(packageName, userId); + synchronized (mRulesLock) { + updateRulesForUidLocked(uid); + } + } catch (NameNotFoundException nnfe) { + } + } + + @Override + public void onParoleStateChanged(boolean isParoleOn) { synchronized (mRulesLock) { - updateRulesForUidLocked(uid); + updateRulesForAppIdleParoleLocked(); } - } catch (NameNotFoundException nnfe) { - return; } } @@ -2381,12 +2394,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub /** * Add or remove a uid to the firewall blacklist for all network ifaces. */ - private void enableFirewallChain(int chain, boolean enable) { + private void enableFirewallChainLocked(int chain, boolean enable) { if (mFirewallChainStates.indexOfKey(chain) >= 0 && mFirewallChainStates.get(chain) == enable) { // All is the same, nothing to do. return; } + mFirewallChainStates.put(chain, enable); try { mNetworkManager.setFirewallChainEnabled(chain, enable); } catch (IllegalStateException e) { diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java index fdb443e..87b4f8c 100644 --- a/services/core/java/com/android/server/notification/NotificationDelegate.java +++ b/services/core/java/com/android/server/notification/NotificationDelegate.java @@ -16,6 +16,8 @@ package com.android.server.notification; +import com.android.internal.statusbar.NotificationVisibility; + public interface NotificationDelegate { void onSetDisabled(int status); void onClearAll(int callingUid, int callingPid, int userId); @@ -30,6 +32,7 @@ public interface NotificationDelegate { void onPanelHidden(); void clearEffects(); void onNotificationVisibilityChanged( - String[] newlyVisibleKeys, String[] noLongerVisibleKeys); + NotificationVisibility[] newlyVisibleKeys, + NotificationVisibility[] noLongerVisibleKeys); void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 0dcad82..4524ff8 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -97,6 +97,7 @@ import android.view.accessibility.AccessibilityManager; import android.widget.Toast; import com.android.internal.R; +import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.util.FastXmlSerializer; import com.android.server.EventLogTags; import com.android.server.LocalServices; @@ -622,22 +623,24 @@ public class NotificationManagerService extends SystemService { } @Override - public void onNotificationVisibilityChanged( - String[] newlyVisibleKeys, String[] noLongerVisibleKeys) { + public void onNotificationVisibilityChanged(NotificationVisibility[] newlyVisibleKeys, + NotificationVisibility[] noLongerVisibleKeys) { synchronized (mNotificationList) { - for (String key : newlyVisibleKeys) { - NotificationRecord r = mNotificationsByKey.get(key); + for (NotificationVisibility nv : newlyVisibleKeys) { + NotificationRecord r = mNotificationsByKey.get(nv.key); if (r == null) continue; - r.setVisibility(true); + r.setVisibility(true, nv.rank); + nv.recycle(); } // Note that we might receive this event after notifications // have already left the system, e.g. after dismissing from the // shade. Hence not finding notifications in // mNotificationsByKey is not an exceptional condition. - for (String key : noLongerVisibleKeys) { - NotificationRecord r = mNotificationsByKey.get(key); + for (NotificationVisibility nv : noLongerVisibleKeys) { + NotificationRecord r = mNotificationsByKey.get(nv.key); if (r == null) continue; - r.setVisibility(false); + r.setVisibility(false, nv.rank); + nv.recycle(); } } } diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index c4773ca..b7aea9d 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -314,13 +314,15 @@ public final class NotificationRecord { /** * Set the visibility of the notification. */ - public void setVisibility(boolean visible) { + public void setVisibility(boolean visible, int rank) { final long now = System.currentTimeMillis(); mVisibleSinceMs = visible ? now : mVisibleSinceMs; stats.onVisibilityChanged(visible); EventLogTags.writeNotificationVisibility(getKey(), visible ? 1 : 0, (int) (now - mCreationTimeMs), - (int) (now - mUpdateTimeMs)); + (int) (now - mUpdateTimeMs), + 0, // exposure time + rank); } /** diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java index 2d5c199..2f0cc0f 100644 --- a/services/core/java/com/android/server/notification/NotificationUsageStats.java +++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java @@ -16,11 +16,13 @@ package com.android.server.notification; +import android.app.Notification; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; +import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; @@ -57,8 +59,8 @@ public class NotificationUsageStats { private static final boolean DEBUG = false; public static final int TEN_SECONDS = 1000 * 10; - public static final int ONE_HOUR = 1000 * 60 * 60; - private static final long EMIT_PERIOD = DEBUG ? TEN_SECONDS : ONE_HOUR; + public static final int FOUR_HOURS = 1000 * 60 * 60 * 4; + private static final long EMIT_PERIOD = DEBUG ? TEN_SECONDS : FOUR_HOURS; // Guarded by synchronized(this). private final Map<String, AggregatedStats> mStats = new HashMap<>(); @@ -98,6 +100,7 @@ public class NotificationUsageStats { AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification); for (AggregatedStats stats : aggregatedStatsArray) { stats.numPostedByApp++; + stats.countApiUse(notification); } releaseAggregatedStatsLocked(aggregatedStatsArray); if (ENABLE_SQLITE_LOG) { @@ -113,6 +116,7 @@ public class NotificationUsageStats { AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification); for (AggregatedStats stats : aggregatedStatsArray) { stats.numUpdatedByApp++; + stats.countApiUse(notification); } releaseAggregatedStatsLocked(aggregatedStatsArray); } @@ -246,6 +250,7 @@ public class NotificationUsageStats { private final Context mContext; public final String key; + private AggregatedStats mPrevious; // ---- Updated as the respective events occur. public int numPostedByApp; @@ -256,14 +261,103 @@ public class NotificationUsageStats { public int numWithStaredPeople; public int numWithValidPeople; public int numBlocked; - - private AggregatedStats mPrevious; + public int numWithActions; + public int numPrivate; + public int numSecret; + public int numPriorityMax; + public int numPriorityHigh; + public int numPriorityLow; + public int numPriorityMin; + public int numWithBigText; + public int numWithBigPicture; + public int numForegroundService; + public int numOngoing; + public int numAutoCancel; + public int numWithLargeIcon; + public int numWithInbox; + public int numWithMediaSession; + public int numWithTitle; + public int numWithText; + public int numWithSubText; + public int numWithInfoText; + public int numInterrupt; public AggregatedStats(Context context, String key) { this.key = key; mContext = context; } + public void countApiUse(NotificationRecord record) { + final Notification n = record.getNotification(); + if (n.actions != null) { + numWithActions++; + } + + if ((n.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) { + numForegroundService++; + } + + if ((n.flags & Notification.FLAG_ONGOING_EVENT) != 0) { + numOngoing++; + } + + if ((n.flags & Notification.FLAG_AUTO_CANCEL) != 0) { + numAutoCancel++; + } + + if ((n.defaults & Notification.DEFAULT_SOUND) != 0 || + (n.defaults & Notification.DEFAULT_VIBRATE) != 0 || + n.sound != null || n.vibrate != null) { + numInterrupt++; + } + + switch (n.visibility) { + case Notification.VISIBILITY_PRIVATE: + numPrivate++; + break; + case Notification.VISIBILITY_SECRET: + numSecret++; + break; + } + + switch (n.priority) { + case Notification.PRIORITY_MAX: + numPriorityMax++; + break; + case Notification.PRIORITY_HIGH: + numPriorityHigh++; + break; + case Notification.PRIORITY_LOW: + numPriorityLow++; + break; + case Notification.PRIORITY_MIN: + numPriorityMin++; + break; + } + + for (String Key : n.extras.keySet()) { + if (Notification.EXTRA_BIG_TEXT.equals(key)) { + numWithBigText++; + } else if (Notification.EXTRA_PICTURE.equals(key)) { + numWithBigPicture++; + } else if (Notification.EXTRA_LARGE_ICON.equals(key)) { + numWithLargeIcon++; + } else if (Notification.EXTRA_TEXT_LINES.equals(key)) { + numWithInbox++; + } else if (Notification.EXTRA_MEDIA_SESSION.equals(key)) { + numWithMediaSession++; + } else if (Notification.EXTRA_TITLE.equals(key)) { + numWithTitle++; + } else if (Notification.EXTRA_TEXT.equals(key)) { + numWithText++; + } else if (Notification.EXTRA_SUB_TEXT.equals(key)) { + numWithSubText++; + } else if (Notification.EXTRA_INFO_TEXT.equals(key)) { + numWithInfoText++; + } + } + } + public void emit() { if (mPrevious == null) { mPrevious = new AggregatedStats(null, key); @@ -277,6 +371,26 @@ public class NotificationUsageStats { maybeCount("people_cache_hit", (numPeopleCacheHit - mPrevious.numPeopleCacheHit)); maybeCount("people_cache_miss", (numPeopleCacheMiss - mPrevious.numPeopleCacheMiss)); maybeCount("note_blocked", (numBlocked - mPrevious.numBlocked)); + maybeCount("note_with_actions", (numWithActions - mPrevious.numWithActions)); + maybeCount("note_private", (numPrivate - mPrevious.numPrivate)); + maybeCount("note_secret", (numSecret - mPrevious.numSecret)); + maybeCount("note_prio_max", (numPriorityMax - mPrevious.numPriorityMax)); + maybeCount("note_prio_high", (numPriorityHigh - mPrevious.numPriorityHigh)); + maybeCount("note_prio_low", (numPriorityLow - mPrevious.numPriorityLow)); + maybeCount("note_prio_min", (numPriorityMin - mPrevious.numPriorityMin)); + maybeCount("note_interupt", (numInterrupt - mPrevious.numInterrupt)); + maybeCount("note_big_text", (numWithBigText - mPrevious.numWithBigText)); + maybeCount("note_big_pic", (numWithBigPicture - mPrevious.numWithBigPicture)); + maybeCount("note_fg", (numForegroundService - mPrevious.numForegroundService)); + maybeCount("note_ongoing", (numOngoing - mPrevious.numOngoing)); + maybeCount("note_auto", (numAutoCancel - mPrevious.numAutoCancel)); + maybeCount("note_large_icon", (numWithLargeIcon - mPrevious.numWithLargeIcon)); + maybeCount("note_inbox", (numWithInbox - mPrevious.numWithInbox)); + maybeCount("note_media", (numWithMediaSession - mPrevious.numWithMediaSession)); + maybeCount("note_title", (numWithTitle - mPrevious.numWithTitle)); + maybeCount("note_text", (numWithText - mPrevious.numWithText)); + maybeCount("note_sub_text", (numWithSubText - mPrevious.numWithSubText)); + maybeCount("note_info_text", (numWithInfoText - mPrevious.numWithInfoText)); mPrevious.numPostedByApp = numPostedByApp; mPrevious.numUpdatedByApp = numUpdatedByApp; @@ -286,6 +400,26 @@ public class NotificationUsageStats { mPrevious.numWithStaredPeople = numWithStaredPeople; mPrevious.numWithValidPeople = numWithValidPeople; mPrevious.numBlocked = numBlocked; + mPrevious.numWithActions = numWithActions; + mPrevious.numPrivate = numPrivate; + mPrevious.numSecret = numSecret; + mPrevious.numPriorityMax = numPriorityMax; + mPrevious.numPriorityHigh = numPriorityHigh; + mPrevious.numPriorityLow = numPriorityLow; + mPrevious.numPriorityMin = numPriorityMin; + mPrevious.numInterrupt = numInterrupt; + mPrevious.numWithBigText = numWithBigText; + mPrevious.numWithBigPicture = numWithBigPicture; + mPrevious.numForegroundService = numForegroundService; + mPrevious.numOngoing = numOngoing; + mPrevious.numAutoCancel = numAutoCancel; + mPrevious.numWithLargeIcon = numWithLargeIcon; + mPrevious.numWithInbox = numWithInbox; + mPrevious.numWithMediaSession = numWithMediaSession; + mPrevious.numWithTitle = numWithTitle; + mPrevious.numWithText = numWithText; + mPrevious.numWithSubText = numWithSubText; + mPrevious.numWithInfoText = numWithInfoText; } void maybeCount(String name, int value) { diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java index fe3103b..62c686c 100644 --- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java @@ -106,7 +106,7 @@ final class DefaultPermissionGrantPolicy { private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>(); static { -// STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE); + STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE); STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 41d3ffd..7a39c2b 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -3354,11 +3354,8 @@ public class PackageManagerService extends IPackageManager.Stub { enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "updatePermissionFlags"); - // Only the system can change policy and system fixed flags. + // Only the system can change system fixed flags. if (getCallingUid() != Process.SYSTEM_UID) { - flagMask &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED; - flagValues &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED; - flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; } @@ -3387,18 +3384,63 @@ public class PackageManagerService extends IPackageManager.Stub { return; } + boolean hadState = permissionsState.getRuntimePermissionState(name, userId) != null; + if (permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues)) { // Install and runtime permissions are stored in different places, // so figure out what permission changed and persist the change. if (permissionsState.getInstallPermissionState(name) != null) { scheduleWriteSettingsLocked(); - } else if (permissionsState.getRuntimePermissionState(name, userId) != null) { + } else if (permissionsState.getRuntimePermissionState(name, userId) != null + || hadState) { mSettings.writeRuntimePermissionsForUserLPr(userId, false); } } } } + /** + * Update the permission flags for all packages and runtime permissions of a user in order + * to allow device or profile owner to remove POLICY_FIXED. + */ + @Override + public void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId) { + if (!sUserManager.exists(userId)) { + return; + } + + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, + "updatePermissionFlagsForAllApps"); + + enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, + "updatePermissionFlagsForAllApps"); + + // Only the system can change system fixed flags. + if (getCallingUid() != Process.SYSTEM_UID) { + flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; + flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; + } + + synchronized (mPackages) { + boolean changed = false; + final int packageCount = mPackages.size(); + for (int pkgIndex = 0; pkgIndex < packageCount; pkgIndex++) { + final PackageParser.Package pkg = mPackages.valueAt(pkgIndex); + SettingBase sb = (SettingBase) pkg.mExtras; + if (sb == null) { + continue; + } + PermissionsState permissionsState = sb.getPermissionsState(); + changed |= permissionsState.updatePermissionFlagsForAllPermissions( + userId, flagMask, flagValues); + } + if (changed) { + mSettings.writeRuntimePermissionsForUserLPr(userId, false); + } + } + } + @Override public boolean shouldShowRequestPermissionRationale(String permissionName, String packageName, int userId) { diff --git a/services/core/java/com/android/server/pm/PermissionsState.java b/services/core/java/com/android/server/pm/PermissionsState.java index ad662be..04beafd 100644 --- a/services/core/java/com/android/server/pm/PermissionsState.java +++ b/services/core/java/com/android/server/pm/PermissionsState.java @@ -344,6 +344,22 @@ public final class PermissionsState { return permissionData.updateFlags(userId, flagMask, flagValues); } + public boolean updatePermissionFlagsForAllPermissions( + int userId, int flagMask, int flagValues) { + enforceValidUserId(userId); + + if (mPermissions == null) { + return false; + } + boolean changed = false; + final int permissionCount = mPermissions.size(); + for (int i = 0; i < permissionCount; i++) { + PermissionData permissionData = mPermissions.valueAt(i); + changed |= permissionData.updateFlags(userId, flagMask, flagValues); + } + return changed; + } + /** * Compute the Linux gids for a given device user from the permissions * granted to this user. Note that these are computed to avoid additional diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index a754379..7640837 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -29,6 +29,7 @@ import android.util.Slog; import com.android.internal.statusbar.IStatusBar; import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; import com.android.server.LocalServices; @@ -660,7 +661,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub { @Override public void onNotificationVisibilityChanged( - String[] newlyVisibleKeys, String[] noLongerVisibleKeys) throws RemoteException { + NotificationVisibility[] newlyVisibleKeys, NotificationVisibility[] noLongerVisibleKeys) + throws RemoteException { enforceStatusBarService(); long identity = Binder.clearCallingIdentity(); try { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index ace5997..b285b66 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2671,6 +2671,16 @@ public class WindowManagerService extends IWindowManager.Stub synchronized(mWindowMap) { mScreenCaptureDisabled.put(userId, disabled); + // Update secure surface for all windows belonging to this user. + for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) { + WindowList windows = mDisplayContents.valueAt(displayNdx).getWindowList(); + for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { + final WindowState win = windows.get(winNdx); + if (win.mHasSurface && userId == UserHandle.getUserId(win.mOwnerUid)) { + win.mWinAnimator.setSecureLocked(disabled); + } + } + } } } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index f1331e9..d818519 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -658,6 +658,11 @@ class WindowStateAnimator { } @Override + public void setSecure(boolean isSecure) { + super.setSecure(isSecure); + } + + @Override public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { if (dsdx != mDsdx || dtdx != mDtdx || dsdy != mDsdy || dtdy != mDtdy) { if (logSurfaceTrace) Slog.v(SURFACE_TAG, "setMatrix(" + dsdx + "," + dtdx + "," @@ -1663,6 +1668,22 @@ class WindowStateAnimator { } } + void setSecureLocked(boolean isSecure) { + if (mSurfaceControl == null) { + return; + } + if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setSecureLocked"); + SurfaceControl.openTransaction(); + try { + if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, "isSecure=" + isSecure, + null); + mSurfaceControl.setSecure(isSecure); + } finally { + SurfaceControl.closeTransaction(); + if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setSecureLocked"); + } + } + // This must be called while inside a transaction. boolean performShowLocked() { if (mWin.isHiddenFromUserLocked()) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 416ea73..e44a7ab 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -246,6 +246,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.USB_MASS_STORAGE_ENABLED); GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.WIFI_SLEEP_POLICY); GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.STAY_ON_WHILE_PLUGGED_IN); + GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN); } // Keyguard features that when set of a profile will affect the profiles @@ -4216,11 +4217,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { long ident = Binder.clearCallingIdentity(); try { clearUserRestrictions(new UserHandle(UserHandle.USER_OWNER)); + AppGlobals.getPackageManager().updatePermissionFlagsForAllApps( + PackageManager.FLAG_PERMISSION_POLICY_FIXED, + 0, UserHandle.USER_OWNER); if (mDeviceOwner != null) { mDeviceOwner.clearDeviceOwner(); mDeviceOwner.writeOwnerFile(); updateDeviceOwnerLocked(); } + } catch (RemoteException re) { } finally { Binder.restoreCallingIdentity(ident); } @@ -4387,10 +4392,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { long ident = Binder.clearCallingIdentity(); try { clearUserRestrictions(callingUser); + AppGlobals.getPackageManager().updatePermissionFlagsForAllApps( + PackageManager.FLAG_PERMISSION_POLICY_FIXED, + 0, callingUser.getIdentifier()); if (mDeviceOwner != null) { mDeviceOwner.removeProfileOwner(userId); mDeviceOwner.writeOwnerFile(); } + } catch (RemoteException re) { } finally { Binder.restoreCallingIdentity(ident); } @@ -6389,21 +6398,27 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); long ident = Binder.clearCallingIdentity(); try { - PackageManager packageManager = mContext.getPackageManager(); + final ApplicationInfo ai = AppGlobals.getPackageManager() + .getApplicationInfo(packageName, 0, user.getIdentifier()); + final int targetSdkVersion = ai == null ? 0 : ai.targetSdkVersion; + if (targetSdkVersion < android.os.Build.VERSION_CODES.MNC) { + return false; + } + final PackageManager packageManager = mContext.getPackageManager(); switch (grantState) { case DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED: { + packageManager.grantRuntimePermission(packageName, permission, user); packageManager.updatePermissionFlags(permission, packageName, PackageManager.FLAG_PERMISSION_POLICY_FIXED, PackageManager.FLAG_PERMISSION_POLICY_FIXED, user); - packageManager.grantRuntimePermission(packageName, permission, user); } break; case DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED: { + packageManager.revokeRuntimePermission(packageName, + permission, user); packageManager.updatePermissionFlags(permission, packageName, PackageManager.FLAG_PERMISSION_POLICY_FIXED, PackageManager.FLAG_PERMISSION_POLICY_FIXED, user); - packageManager.revokeRuntimePermission(packageName, - permission, user); } break; case DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT: { diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java index 712db09..fb8a5bb 100644 --- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java @@ -34,6 +34,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; +import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.ContextWrapper; @@ -792,6 +793,30 @@ public class ConnectivityServiceTest extends AndroidTestCase { handlerThread.quit(); } + @LargeTest + public void testNoMutableNetworkRequests() throws Exception { + PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent("a"), 0); + NetworkRequest.Builder builder = new NetworkRequest.Builder(); + builder.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); + try { + mCm.requestNetwork(builder.build(), new NetworkCallback()); + fail(); + } catch (IllegalArgumentException expected) {} + try { + mCm.requestNetwork(builder.build(), pendingIntent); + fail(); + } catch (IllegalArgumentException expected) {} + builder = new NetworkRequest.Builder(); + builder.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL); + try { + mCm.requestNetwork(builder.build(), new NetworkCallback()); + fail(); + } catch (IllegalArgumentException expected) {} + try { + mCm.requestNetwork(builder.build(), pendingIntent); + fail(); + } catch (IllegalArgumentException expected) {} + } // @Override // public void tearDown() throws Exception { diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 490236e..c49a5f9 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -119,6 +119,7 @@ public class UsageStatsService extends SystemService implements static final int MSG_CHECK_PAROLE_TIMEOUT = 6; static final int MSG_PAROLE_END_TIMEOUT = 7; static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8; + static final int MSG_PAROLE_STATE_CHANGED = 9; private final Object mLock = new Object(); Handler mHandler; @@ -313,7 +314,7 @@ public class UsageStatsService extends SystemService implements mLastAppIdleParoledTime = checkAndGetTimeLocked(); postNextParoleTimeout(); } - postCheckIdleStates(UserHandle.USER_ALL); + postParoleStateChanged(); } } } @@ -338,6 +339,12 @@ public class UsageStatsService extends SystemService implements mHandler.sendEmptyMessageDelayed(MSG_PAROLE_END_TIMEOUT, mAppIdleParoleDurationMillis); } + private void postParoleStateChanged() { + if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_STATE_CHANGED"); + mHandler.removeMessages(MSG_PAROLE_STATE_CHANGED); + mHandler.sendEmptyMessage(MSG_PAROLE_STATE_CHANGED); + } + void postCheckIdleStates(int userId) { mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0)); } @@ -756,6 +763,13 @@ public class UsageStatsService extends SystemService implements } } + boolean isAppIdleFilteredOrParoled(String packageName, int userId, long timeNow) { + if (mAppIdleParoled) { + return false; + } + return isAppIdleFiltered(packageName, userId, timeNow); + } + boolean isAppIdleFiltered(String packageName, int userId, long timeNow) { final UserUsageStatsService userService; final long screenOnTime; @@ -782,13 +796,6 @@ public class UsageStatsService extends SystemService implements if (!mAppIdleEnabled) { return false; } - synchronized (mLock) { - // Temporary exemption, probably due to device charging or occasional allowance to - // be allowed to sync, etc. - if (mAppIdleParoled) { - return false; - } - } if (packageName.equals("android")) return false; try { if (mDeviceIdleController.isPowerSaveWhitelistApp(packageName)) { @@ -846,6 +853,12 @@ public class UsageStatsService extends SystemService implements } } + void informParoleStateChanged() { + for (AppIdleStateChangeListener listener : mPackageAccessListeners) { + listener.onParoleStateChanged(mAppIdleParoled); + } + } + private static boolean validRange(long currentTime, long beginTime, long endTime) { return beginTime <= currentTime && beginTime < endTime; } @@ -975,6 +988,11 @@ public class UsageStatsService extends SystemService implements args.recycle(); break; + case MSG_PAROLE_STATE_CHANGED: + if (DEBUG) Slog.d(TAG, "Parole state changed: " + mAppIdleParoled); + informParoleStateChanged(); + break; + default: super.handleMessage(msg); break; @@ -1126,7 +1144,7 @@ public class UsageStatsService extends SystemService implements } final long token = Binder.clearCallingIdentity(); try { - return UsageStatsService.this.isAppIdleFiltered(packageName, userId, -1); + return UsageStatsService.this.isAppIdleFilteredOrParoled(packageName, userId, -1); } finally { Binder.restoreCallingIdentity(token); } @@ -1251,6 +1269,11 @@ public class UsageStatsService extends SystemService implements } @Override + public boolean isAppIdleParoleOn() { + return mAppIdleParoled; + } + + @Override public void prepareShutdown() { // This method *WILL* do IO work, but we must block until it is finished or else // we might not shutdown cleanly. This is ok to do with the 'am' lock held, because @@ -1261,6 +1284,7 @@ public class UsageStatsService extends SystemService implements @Override public void addAppIdleStateChangeListener(AppIdleStateChangeListener listener) { UsageStatsService.this.addListener(listener); + listener.onParoleStateChanged(isAppIdleParoleOn()); } @Override diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java index 8a72341..439ace8 100644 --- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java @@ -17,7 +17,7 @@ package com.android.test.voiceinteraction; import android.annotation.Nullable; -import android.app.AssistStructure; +import android.app.assist.AssistStructure; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java index 3090a11..90a781c 100644 --- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java @@ -18,8 +18,8 @@ package com.android.test.voiceinteraction; import android.app.ActivityManager; import android.app.VoiceInteractor; -import android.app.AssistContent; -import android.app.AssistStructure; +import android.app.assist.AssistContent; +import android.app.assist.AssistStructure; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; @@ -123,34 +123,8 @@ public class MainInteractionSession extends VoiceInteractionSession } public void onHandleAssist(Bundle assistBundle) { - boolean hasStructure = false; - if (assistBundle != null) { - Bundle assistContext = assistBundle.getBundle(Intent.EXTRA_ASSIST_CONTEXT); - if (assistContext != null) { - mAssistStructure = AssistStructure.getAssistStructure(assistContext); - if (mAssistStructure != null) { - if (mAssistVisualizer != null) { - mAssistVisualizer.setAssistStructure(mAssistStructure); - hasStructure = true; - } - } - AssistContent content = AssistContent.getAssistContent(assistContext); - if (content != null) { - Log.i(TAG, "Assist intent: " + content.getIntent()); - Log.i(TAG, "Assist clipdata: " + content.getClipData()); - } - } - Uri referrer = assistBundle.getParcelable(Intent.EXTRA_REFERRER); - if (referrer != null) { - Log.i(TAG, "Referrer: " + referrer); - } - } - if (!hasStructure && mAssistVisualizer != null) { - mAssistVisualizer.clearAssistData(); - } } - /* @Override public void onHandleAssist(Bundle data, AssistStructure structure, AssistContent content) { mAssistStructure = structure; @@ -158,13 +132,22 @@ public class MainInteractionSession extends VoiceInteractionSession if (mAssistVisualizer != null) { mAssistVisualizer.setAssistStructure(mAssistStructure); } + } else { + if (mAssistVisualizer != null) { + mAssistVisualizer.clearAssistData(); + } } if (content != null) { Log.i(TAG, "Assist intent: " + content.getIntent()); Log.i(TAG, "Assist clipdata: " + content.getClipData()); } + if (data != null) { + Uri referrer = data.getParcelable(Intent.EXTRA_REFERRER); + if (referrer != null) { + Log.i(TAG, "Referrer: " + referrer); + } + } } - */ @Override public void onHandleScreenshot(Bitmap screenshot) { diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java index c038414..943c647 100644 --- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java @@ -16,6 +16,7 @@ package com.android.test.voiceinteraction; +import android.annotation.Nullable; import android.app.Activity; import android.app.VoiceInteractor; import android.content.ComponentName; @@ -111,38 +112,10 @@ public class TestInteractionActivity extends Activity implements View.OnClickLis @Override public void onClick(View v) { if (v == mAbortButton) { - VoiceInteractor.AbortVoiceRequest req = new VoiceInteractor.AbortVoiceRequest( - new VoiceInteractor.Prompt("Dammit, we suck :("), null) { - @Override - public void onCancel() { - Log.i(TAG, "Canceled!"); - mLog.append("Canceled abort\n"); - } - - @Override - public void onAbortResult(Bundle result) { - Log.i(TAG, "Abort result: result=" + result); - mLog.append("Abort: result=" + result + "\n"); - getActivity().finish(); - } - }; + VoiceInteractor.AbortVoiceRequest req = new TestAbortVoice(); mInteractor.submitRequest(req, REQUEST_ABORT); } else if (v == mCompleteButton) { - VoiceInteractor.CompleteVoiceRequest req = new VoiceInteractor.CompleteVoiceRequest( - new VoiceInteractor.Prompt("Woohoo, completed!"), null) { - @Override - public void onCancel() { - Log.i(TAG, "Canceled!"); - mLog.append("Canceled complete\n"); - } - - @Override - public void onCompleteResult(Bundle result) { - Log.i(TAG, "Complete result: result=" + result); - mLog.append("Complete: result=" + result + "\n"); - getActivity().finish(); - } - }; + VoiceInteractor.CompleteVoiceRequest req = new TestCompleteVoice(); mInteractor.submitRequest(req, REQUEST_COMPLETE); } else if (v == mPickButton) { VoiceInteractor.PickOptionRequest.Option[] options = @@ -152,36 +125,7 @@ public class TestInteractionActivity extends Activity implements View.OnClickLis options[2] = new VoiceInteractor.PickOptionRequest.Option("Three"); options[3] = new VoiceInteractor.PickOptionRequest.Option("Four"); options[4] = new VoiceInteractor.PickOptionRequest.Option("Five"); - VoiceInteractor.PickOptionRequest req = new VoiceInteractor.PickOptionRequest( - new VoiceInteractor.Prompt("Need to pick something"), options, null) { - @Override - public void onCancel() { - Log.i(TAG, "Canceled!"); - mLog.append("Canceled pick\n"); - } - - @Override - public void onPickOptionResult(boolean finished, Option[] selections, Bundle result) { - Log.i(TAG, "Pick result: finished=" + finished + " selections=" + selections - + " result=" + result); - StringBuilder sb = new StringBuilder(); - if (finished) { - sb.append("Pick final result: "); - } else { - sb.append("Pick intermediate result: "); - } - for (int i=0; i<selections.length; i++) { - if (i >= 1) { - sb.append(", "); - } - sb.append(selections[i].getLabel()); - } - mLog.append(sb.toString()); - if (finished) { - getActivity().finish(); - } - } - }; + VoiceInteractor.PickOptionRequest req = new TestPickOption(options); mInteractor.submitRequest(req, REQUEST_PICK); } else if (v == mJumpOutButton) { Log.i(TAG, "Jump out"); @@ -200,4 +144,66 @@ public class TestInteractionActivity extends Activity implements View.OnClickLis public void onDestroy() { super.onDestroy(); } + + static class TestAbortVoice extends VoiceInteractor.AbortVoiceRequest { + public TestAbortVoice() { + super(new VoiceInteractor.Prompt("Dammit, we suck :("), null); + } + @Override public void onCancel() { + Log.i(TAG, "Canceled!"); + ((TestInteractionActivity)getActivity()).mLog.append("Canceled abort\n"); + } + @Override public void onAbortResult(Bundle result) { + Log.i(TAG, "Abort result: result=" + result); + ((TestInteractionActivity)getActivity()).mLog.append("Abort: result=" + result + "\n"); + getActivity().finish(); + } + } + + static class TestCompleteVoice extends VoiceInteractor.CompleteVoiceRequest { + public TestCompleteVoice() { + super(new VoiceInteractor.Prompt("Woohoo, completed!"), null); + } + @Override public void onCancel() { + Log.i(TAG, "Canceled!"); + ((TestInteractionActivity)getActivity()).mLog.append("Canceled complete\n"); + } + @Override public void onCompleteResult(Bundle result) { + Log.i(TAG, "Complete result: result=" + result); + ((TestInteractionActivity)getActivity()).mLog.append("Complete: result=" + + result + "\n"); + getActivity().finish(); + } + } + + static class TestPickOption extends VoiceInteractor.PickOptionRequest { + public TestPickOption(Option[] options) { + super(new VoiceInteractor.Prompt("Need to pick something"), options, null); + } + @Override public void onCancel() { + Log.i(TAG, "Canceled!"); + ((TestInteractionActivity)getActivity()).mLog.append("Canceled pick\n"); + } + @Override + public void onPickOptionResult(boolean finished, Option[] selections, Bundle result) { + Log.i(TAG, "Pick result: finished=" + finished + " selections=" + selections + + " result=" + result); + StringBuilder sb = new StringBuilder(); + if (finished) { + sb.append("Pick final result: "); + } else { + sb.append("Pick intermediate result: "); + } + for (int i=0; i<selections.length; i++) { + if (i >= 1) { + sb.append(", "); + } + sb.append(selections[i].getLabel()); + } + ((TestInteractionActivity)getActivity()).mLog.append(sb.toString()); + if (finished) { + getActivity().finish(); + } + } + } } diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java index 5dc70bd..fc6a3de 100644 --- a/wifi/java/android/net/wifi/ScanResult.java +++ b/wifi/java/android/net/wifi/ScanResult.java @@ -18,6 +18,7 @@ package android.net.wifi; import android.os.Parcel; import android.os.Parcelable; +import android.util.Log; /** * Describes information about a detected access point. In addition @@ -80,27 +81,30 @@ public class ScanResult implements Parcelable { public static final int CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4; /** - * AP Channel bandwidth + * AP Channel bandwidth; one of {@link #CHANNEL_WIDTH_20MHZ}, {@link #CHANNEL_WIDTH_40MHZ}, + * {@link #CHANNEL_WIDTH_80MHZ}, {@link #CHANNEL_WIDTH_160MHZ} + * or {@link #CHANNEL_WIDTH_80MHZ_PLUS_MHZ}. */ public int channelWidth; /** * Not used if the AP bandwidth is 20 MHz - * If the AP use 40, 80 or 160 MHz, this is the center frequency - * if the AP use 80 + 80 MHz, this is the center frequency of the first segment + * If the AP use 40, 80 or 160 MHz, this is the center frequency (in MHz) + * if the AP use 80 + 80 MHz, this is the center frequency of the first segment (in MHz) */ public int centerFreq0; /** * Only used if the AP bandwidth is 80 + 80 MHz - * if the AP use 80 + 80 MHz, this is the center frequency of the second segment + * if the AP use 80 + 80 MHz, this is the center frequency of the second segment (in MHz) */ public int centerFreq1; /** - * Whether the AP support 802.11mc Responder + * @deprecated use is80211mcResponder() instead + * @hide */ - public boolean is80211McRTTResponder; + public boolean is80211McRTTResponder; /** * timestamp in microseconds (since boot) when @@ -123,7 +127,7 @@ public class ScanResult implements Parcelable { /** * @hide * Update RSSI of the scan result - * @param previousRSSI + * @param previousRssi * @param previousSeen * @param maxAge */ @@ -206,26 +210,56 @@ public class ScanResult implements Parcelable { public int distanceCm; /** - * The standard deviation of the distance to the AP, if available. + * The standard deviation of the distance to the access point, if available. * Else {@link UNSPECIFIED}. * {@hide} */ public int distanceSdCm; + public static final long FLAG_PASSPOINT_NETWORK = 0x0000000000000001; + public static final long FLAG_80211mc_RESPONDER = 0x0000000000000002; + /** - * Indicates if the scan result represents a passpoint AP + * Defines flags; such as {@link #FLAG_PASSPOINT_NETWORK}. */ - public boolean passpointNetwork; + public long flags; /** - * Indicates if venue name + * sets a flag in {@link #flags} field + * @param flag flag to set + * @hide */ - public String venueName; + public void setFlag(long flag) { + flags |= flag; + } /** - * Indicates operator name + * clears a flag in {@link #flags} field + * @param flag flag to set + * @hide */ - public String operatorFriendlyName; + public void clearFlag(long flag) { + flags &= ~flag; + } + + public boolean is80211mcResponder() { + return (flags & FLAG_80211mc_RESPONDER) != 0; + } + + public boolean isPasspointNetwork() { + return (flags & FLAG_PASSPOINT_NETWORK) != 0; + } + + /** + * Indicates venue name (such as 'San Francisco Airport') published by access point; only + * available on passpoint network and if published by access point. + */ + public CharSequence venueName; + + /** + * Indicates passpoint operator name published by access point. + */ + public CharSequence operatorFriendlyName; /** * {@hide} @@ -267,7 +301,7 @@ public class ScanResult implements Parcelable { **/ public byte[] bytes; - /** information element from beacon + /** information elements from beacon * @hide */ public static class InformationElement { @@ -303,8 +337,7 @@ public class ScanResult implements Parcelable { this.channelWidth = UNSPECIFIED; this.centerFreq0 = UNSPECIFIED; this.centerFreq1 = UNSPECIFIED; - this.is80211McRTTResponder = false; - this.passpointNetwork = false; + this.flags = 0; } /** {@hide} */ @@ -322,8 +355,7 @@ public class ScanResult implements Parcelable { this.channelWidth = UNSPECIFIED; this.centerFreq0 = UNSPECIFIED; this.centerFreq1 = UNSPECIFIED; - this.is80211McRTTResponder = false; - this.passpointNetwork = false; + this.flags = 0; } /** {@hide} */ @@ -342,8 +374,11 @@ public class ScanResult implements Parcelable { this.channelWidth = channelWidth; this.centerFreq0 = centerFreq0; this.centerFreq1 = centerFreq1; - this.is80211McRTTResponder = is80211McRTTResponder; - this.passpointNetwork = false; + if (is80211McRTTResponder) { + this.flags = FLAG_80211mc_RESPONDER; + } else { + this.flags = 0; + } } /** copy constructor {@hide} */ @@ -358,7 +393,6 @@ public class ScanResult implements Parcelable { channelWidth = source.channelWidth; centerFreq0 = source.centerFreq0; centerFreq1 = source.centerFreq1; - is80211McRTTResponder = source.is80211McRTTResponder; timestamp = source.timestamp; distanceCm = source.distanceCm; distanceSdCm = source.distanceSdCm; @@ -369,9 +403,9 @@ public class ScanResult implements Parcelable { numUsage = source.numUsage; numIpConfigFailures = source.numIpConfigFailures; isAutoJoinCandidate = source.isAutoJoinCandidate; - passpointNetwork = source.passpointNetwork; venueName = source.venueName; operatorFriendlyName = source.operatorFriendlyName; + flags = source.flags; } } @@ -405,15 +439,16 @@ public class ScanResult implements Parcelable { sb.append(", distanceSd: ").append((distanceSdCm != UNSPECIFIED ? distanceSdCm : "?")). append("(cm)"); - sb.append(", passpoint: ").append(passpointNetwork ? "yes" : "no"); + sb.append(", passpoint: "); + sb.append(((flags & FLAG_PASSPOINT_NETWORK) != 0) ? "yes" : "no"); if (autoJoinStatus != 0) { sb.append(", status: ").append(autoJoinStatus); } sb.append(", ChannelBandwidth: ").append(channelWidth); sb.append(", centerFreq0: ").append(centerFreq0); sb.append(", centerFreq1: ").append(centerFreq1); - sb.append(", 80211mcResponder: ").append(is80211McRTTResponder? - "is supported":"is not supported"); + sb.append(", 80211mcResponder: "); + sb.append(((flags & FLAG_80211mc_RESPONDER) != 0) ? "is supported" : "is not supported"); return sb.toString(); } @@ -440,7 +475,6 @@ public class ScanResult implements Parcelable { dest.writeInt(channelWidth); dest.writeInt(centerFreq0); dest.writeInt(centerFreq1); - dest.writeInt(is80211McRTTResponder ? 1 : 0); dest.writeLong(seen); dest.writeInt(autoJoinStatus); dest.writeInt(untrusted ? 1 : 0); @@ -448,9 +482,9 @@ public class ScanResult implements Parcelable { dest.writeInt(numUsage); dest.writeInt(numIpConfigFailures); dest.writeInt(isAutoJoinCandidate); - dest.writeInt(passpointNetwork ? 1 : 0); - dest.writeString(venueName); - dest.writeString(operatorFriendlyName); + dest.writeString((venueName != null) ? venueName.toString() : ""); + dest.writeString((operatorFriendlyName != null) ? operatorFriendlyName.toString() : ""); + dest.writeLong(this.flags); if (informationElements != null) { dest.writeInt(informationElements.length); @@ -474,18 +508,19 @@ public class ScanResult implements Parcelable { } ScanResult sr = new ScanResult( wifiSsid, - in.readString(), - in.readString(), - in.readInt(), - in.readInt(), - in.readLong(), - in.readInt(), - in.readInt(), - in.readInt(), - in.readInt(), - in.readInt(), - in.readInt() == 1 + in.readString(), /* BSSID */ + in.readString(), /* capabilities */ + in.readInt(), /* level */ + in.readInt(), /* frequency */ + in.readLong(), /* timestamp */ + in.readInt(), /* distanceCm */ + in.readInt(), /* distanceSdCm */ + in.readInt(), /* channelWidth */ + in.readInt(), /* centerFreq0 */ + in.readInt(), /* centerFreq1 */ + false /* rtt responder, fixed with flags below */ ); + sr.seen = in.readLong(); sr.autoJoinStatus = in.readInt(); sr.untrusted = in.readInt() != 0; @@ -493,9 +528,9 @@ public class ScanResult implements Parcelable { sr.numUsage = in.readInt(); sr.numIpConfigFailures = in.readInt(); sr.isAutoJoinCandidate = in.readInt(); - sr.passpointNetwork = in.readInt() == 1; sr.venueName = in.readString(); sr.operatorFriendlyName = in.readString(); + sr.flags = in.readLong(); int n = in.readInt(); if (n != 0) { sr.informationElements = new InformationElement[n]; diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 5d55ec6..5d834f6 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -344,14 +344,15 @@ public class WifiConfiguration implements Parcelable { public String FQDN; /** - * Service provider name, for Passpoint credential. + * Name of passpoint credential provider */ public String providerFriendlyName; /** - * Roaming Consortium Id, for Passpoint credential. + * Roaming Consortium Id list for passpoint credential; identifies a set of networks where + * passpoint credential will be considered valid */ - public HashSet<Long> roamingConsortiumIds; + public Long[] roamingConsortiumIds; /** * @hide @@ -906,7 +907,7 @@ public class WifiConfiguration implements Parcelable { SSID = null; BSSID = null; FQDN = null; - roamingConsortiumIds = new HashSet<Long>(); + roamingConsortiumIds = new Long[0]; priority = 0; hiddenSSID = false; disableReason = DISABLED_UNKNOWN_REASON; @@ -1437,11 +1438,7 @@ public class WifiConfiguration implements Parcelable { SSID = source.SSID; BSSID = source.BSSID; FQDN = source.FQDN; - roamingConsortiumIds = new HashSet<Long>(); - for (Long roamingConsortiumId : source.roamingConsortiumIds) { - roamingConsortiumIds.add(roamingConsortiumId); - } - + roamingConsortiumIds = source.roamingConsortiumIds.clone(); providerFriendlyName = source.providerFriendlyName; preSharedKey = source.preSharedKey; @@ -1546,7 +1543,7 @@ public class WifiConfiguration implements Parcelable { dest.writeString(autoJoinBSSID); dest.writeString(FQDN); dest.writeString(providerFriendlyName); - dest.writeInt(roamingConsortiumIds.size()); + dest.writeInt(roamingConsortiumIds.length); for (Long roamingConsortiumId : roamingConsortiumIds) { dest.writeLong(roamingConsortiumId); } @@ -1622,8 +1619,9 @@ public class WifiConfiguration implements Parcelable { config.FQDN = in.readString(); config.providerFriendlyName = in.readString(); int numRoamingConsortiumIds = in.readInt(); + config.roamingConsortiumIds = new Long[numRoamingConsortiumIds]; for (int i = 0; i < numRoamingConsortiumIds; i++) { - config.roamingConsortiumIds.add(in.readLong()); + config.roamingConsortiumIds[i] = in.readLong(); } config.preSharedKey = in.readString(); for (int i = 0; i < config.wepKeys.length; i++) { diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java index 3525ec2..e611ea4 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -604,12 +604,13 @@ public class WifiEnterpriseConfig implements Parcelable { * Get the domain_suffix_match value. See setDomSuffixMatch. * @return The domain value. */ - public String getDomainSubjectMatch() { + public String getDomainSuffixMatch() { return getFieldValue(DOM_SUFFIX_MATCH_KEY, ""); } /** - * Set realm for passpoint credential + * Set realm for passpoint credential; realm identifies a set of networks where your + * passpoint credential can be used * @param realm the realm */ public void setRealm(String realm) { @@ -617,7 +618,7 @@ public class WifiEnterpriseConfig implements Parcelable { } /** - * Get realm for passpoint credential + * Get realm for passpoint credential; see {@link #setRealm(String)} for more information * @return the realm */ public String getRealm() { @@ -625,15 +626,16 @@ public class WifiEnterpriseConfig implements Parcelable { } /** - * Set plmn for passpoint credential - * @param plmn the plmn value derived from mcc & mnc + * Set plmn (Public Land Mobile Network) of the provider of passpoint credential + * @param plmn the plmn value derived from mcc (mobile country code) & mnc (mobile network code) */ public void setPlmn(String plmn) { setFieldValue(PLMN_KEY, plmn, ""); } /** - * Get plmn for passpoint credential + * Get plmn (Public Land Mobile Network) for passpoint credential; see {@link #setPlmn + * (String)} for more information * @return the plmn */ public String getPlmn() { diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 64fa0e5..d00c654 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -399,14 +399,16 @@ public class WifiManager { public static final int CHANGE_REASON_CONFIG_CHANGE = 2; /** * An access point scan has completed, and results are available from the supplicant. - * Call {@link #getScanResults()} to obtain the results. + * Call {@link #getScanResults()} to obtain the results. {@link #EXTRA_RESULTS_UPDATED} + * indicates if the scan was completed successfully. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS"; /** - * The result of previous scan, reported with {@link #SCAN_RESULTS_AVAILABLE_ACTION}. - * @return true scan was successful, results updated + * Lookup key for a {@code boolean} representing the result of previous {@link #startScan} + * operation, reported with {@link #SCAN_RESULTS_AVAILABLE_ACTION}. + * @return true scan was successful, results are updated * @return false scan was not successful, results haven't been updated since previous scan */ public static final String EXTRA_RESULTS_UPDATED = "resultsUpdated"; |