diff options
336 files changed, 10431 insertions, 9549 deletions
diff --git a/Android.mk b/Android.mk index 53e892f3351f..1d797c49b3e7 100644 --- a/Android.mk +++ b/Android.mk @@ -417,7 +417,6 @@ LOCAL_SRC_FILES += \ telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl \ telephony/java/com/android/internal/telephony/IWapPushManager.aidl \ wifi/java/android/net/wifi/IWifiManager.aidl \ - wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl \ wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl \ wifi/java/android/net/wifi/nan/IWifiNanManager.aidl \ wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl \ @@ -430,6 +429,14 @@ LOCAL_SRC_FILES += \ core/java/android/service/quicksettings/IQSService.aidl \ core/java/android/service/quicksettings/IQSTileService.aidl \ +# The following are native binders that need to go with the native component +# at system/update_engine/binder_bindings/. Use relative path to refer to them. +LOCAL_SRC_FILES += \ + ../../system/update_engine/binder_bindings/android/os/IUpdateEngine.aidl \ + ../../system/update_engine/binder_bindings/android/os/IUpdateEngineCallback.aidl \ + +LOCAL_AIDL_INCLUDES += system/update_engine/binder_bindings + # FRAMEWORKS_BASE_JAVA_SRC_DIRS comes from build/core/pathmap.mk LOCAL_AIDL_INCLUDES += \ $(FRAMEWORKS_BASE_JAVA_SRC_DIRS) \ diff --git a/api/current.txt b/api/current.txt index 7e69ca434b42..47a635062807 100644 --- a/api/current.txt +++ b/api/current.txt @@ -67,7 +67,7 @@ package android { field public static final java.lang.String DUMP = "android.permission.DUMP"; field public static final java.lang.String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR"; field public static final java.lang.String FACTORY_TEST = "android.permission.FACTORY_TEST"; - field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS"; + field public static final deprecated java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS"; field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED"; field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE"; field public static final deprecated java.lang.String GET_TASKS = "android.permission.GET_TASKS"; @@ -10108,7 +10108,6 @@ package android.content.res { method public java.lang.String getQuantityString(int, int, java.lang.Object...) throws android.content.res.Resources.NotFoundException; method public java.lang.String getQuantityString(int, int) throws android.content.res.Resources.NotFoundException; method public java.lang.CharSequence getQuantityText(int, int) throws android.content.res.Resources.NotFoundException; - method public java.util.Locale getResolvedLocale(); method public java.lang.String getResourceEntryName(int) throws android.content.res.Resources.NotFoundException; method public java.lang.String getResourceName(int) throws android.content.res.Resources.NotFoundException; method public java.lang.String getResourcePackageName(int) throws android.content.res.Resources.NotFoundException; @@ -13841,6 +13840,7 @@ package android.hardware.camera2 { field public static final int HOT_PIXEL_MODE_FAST = 1; // 0x1 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_3 = 3; // 0x3 field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; // 0x1 field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; // 0x2 field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; // 0x0 @@ -20857,6 +20857,7 @@ package android.media { field public static final int DEFAULT = 0; // 0x0 field public static final int H263 = 1; // 0x1 field public static final int H264 = 2; // 0x2 + field public static final int HEVC = 5; // 0x5 field public static final int MPEG_4_SP = 3; // 0x3 field public static final int VP8 = 4; // 0x4 } @@ -22421,6 +22422,13 @@ package android.media.tv { field public static final int TYPE_VGA = 1005; // 0x3ed } + public static final class TvInputInfo.Builder { + ctor public TvInputInfo.Builder(android.content.Context, java.lang.Class<?>); + method public android.media.tv.TvInputInfo build() throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; + method public android.media.tv.TvInputInfo.Builder setCanRecord(boolean); + method public android.media.tv.TvInputInfo.Builder setTunerCount(int); + } + public final class TvInputManager { method public int getInputState(java.lang.String); method public android.media.tv.TvInputInfo getTvInputInfo(java.lang.String); @@ -22463,7 +22471,7 @@ package android.media.tv { public abstract class TvInputService extends android.app.Service { ctor public TvInputService(); method public final android.os.IBinder onBind(android.content.Intent); - method public abstract android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(java.lang.String); + method public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(java.lang.String); method public abstract android.media.tv.TvInputService.Session onCreateSession(java.lang.String); method public final void setTvInputInfo(java.lang.String, android.media.tv.TvInputInfo); field public static final java.lang.String SERVICE_INTERFACE = "android.media.tv.TvInputService"; @@ -23961,6 +23969,7 @@ package android.net.wifi { method public java.lang.String getAltSubjectMatch(); method public java.lang.String getAnonymousIdentity(); method public java.security.cert.X509Certificate getCaCertificate(); + method public java.security.cert.X509Certificate[] getCaCertificates(); method public java.security.cert.X509Certificate getClientCertificate(); method public java.lang.String getDomainSuffixMatch(); method public int getEapMethod(); @@ -23973,6 +23982,7 @@ package android.net.wifi { method public void setAltSubjectMatch(java.lang.String); method public void setAnonymousIdentity(java.lang.String); method public void setCaCertificate(java.security.cert.X509Certificate); + method public void setCaCertificates(java.security.cert.X509Certificate[]); method public void setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate); method public void setDomainSuffixMatch(java.lang.String); method public void setEapMethod(int); @@ -30855,10 +30865,14 @@ package android.provider { public static final class ContactsContract.Intents { ctor public ContactsContract.Intents(); + field public static final java.lang.String ACTION_VOICE_SEND_MESSAGE_TO_CONTACTS = "android.provider.action.VOICE_SEND_MESSAGE_TO_CONTACTS"; field public static final java.lang.String ATTACH_IMAGE = "com.android.contacts.action.ATTACH_IMAGE"; field public static final java.lang.String CONTACTS_DATABASE_CREATED = "android.provider.Contacts.DATABASE_CREATED"; field public static final java.lang.String EXTRA_CREATE_DESCRIPTION = "com.android.contacts.action.CREATE_DESCRIPTION"; field public static final java.lang.String EXTRA_FORCE_CREATE = "com.android.contacts.action.FORCE_CREATE"; + field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_CHAT_ID = "android.provider.extra.RECIPIENT_CONTACT_CHAT_ID"; + field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_NAME = "android.provider.extra.RECIPIENT_CONTACT_NAME"; + field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_URI = "android.provider.extra.RECIPIENT_CONTACT_URI"; field public static final java.lang.String INVITE_CONTACT = "com.android.contacts.action.INVITE_CONTACT"; field public static final java.lang.String SEARCH_SUGGESTION_CLICKED = "android.provider.Contacts.SEARCH_SUGGESTION_CLICKED"; field public static final java.lang.String SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED = "android.provider.Contacts.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED"; @@ -31104,6 +31118,7 @@ package android.provider { method public static android.net.Uri buildRootsUri(java.lang.String); method public static android.net.Uri buildSearchDocumentsUri(java.lang.String, java.lang.String, java.lang.String); method public static android.net.Uri buildTreeDocumentUri(java.lang.String, java.lang.String); + method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri); method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String); method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri); method public static java.lang.String getDocumentId(android.net.Uri); @@ -31112,6 +31127,7 @@ package android.provider { method public static java.lang.String getSearchDocumentsQuery(android.net.Uri); method public static java.lang.String getTreeDocumentId(android.net.Uri); method public static boolean isDocumentUri(android.content.Context, android.net.Uri); + method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri); method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String); field public static final java.lang.String EXTRA_ERROR = "error"; field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF"; @@ -35253,6 +35269,7 @@ package android.telecom { method public static boolean hasProperty(int, int); method public boolean hasProperty(int); method public static java.lang.String propertiesToString(int); + field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 4194304; // 0x400000 field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000 field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000 field public static final int CAPABILITY_HOLD = 1; // 0x1 @@ -35393,6 +35410,7 @@ package android.telecom { method public final void setVideoProvider(android.telecom.Connection.VideoProvider); method public final void setVideoState(int); method public static java.lang.String stateToString(int); + field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000 field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000 field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000 field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000 @@ -35959,8 +35977,6 @@ package android.telephony { public final class CellIdentityGsm implements android.os.Parcelable { method public int describeContents(); - method public int getArfcn(); - method public int getBsic(); method public int getCid(); method public int getLac(); method public int getMcc(); @@ -35973,7 +35989,6 @@ package android.telephony { public final class CellIdentityLte implements android.os.Parcelable { method public int describeContents(); method public int getCi(); - method public int getEarfcn(); method public int getMcc(); method public int getMnc(); method public int getPci(); @@ -35989,7 +36004,6 @@ package android.telephony { method public int getMcc(); method public int getMnc(); method public int getPsc(); - method public int getUarfcn(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityWcdma> CREATOR; } @@ -36411,10 +36425,15 @@ package android.telephony { method public int getActiveSubscriptionInfoCountMax(); method public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int); method public java.util.List<android.telephony.SubscriptionInfo> getActiveSubscriptionInfoList(); + method public static int getDefaultDataSubscriptionId(); + method public static int getDefaultSmsSubscriptionId(); + method public static int getDefaultSubscriptionId(); + method public static int getDefaultVoiceSubscriptionId(); method public boolean isNetworkRoaming(int); method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener); field public static final int DATA_ROAMING_DISABLE = 0; // 0x0 field public static final int DATA_ROAMING_ENABLE = 1; // 0x1 + field public static final int INVALID_SUBSCRIPTION_ID = -1; // 0xffffffff } public static class SubscriptionManager.OnSubscriptionsChangedListener { @@ -36426,32 +36445,49 @@ package android.telephony { method public boolean canChangeDtmfToneLength(); method public java.util.List<android.telephony.CellInfo> getAllCellInfo(); method public int getCallState(); + method public int getCallState(int); method public android.telephony.CellLocation getCellLocation(); method public int getDataActivity(); + method public int getDataNetworkType(int); method public int getDataState(); method public java.lang.String getDeviceId(); method public java.lang.String getDeviceId(int); method public java.lang.String getDeviceSoftwareVersion(); method public java.lang.String getGroupIdLevel1(); + method public java.lang.String getGroupIdLevel1(int); method public java.lang.String getIccSimChallengeResponse(int, java.lang.String); + method public java.lang.String getLine1AlphaTag(int); method public java.lang.String getLine1Number(); + method public java.lang.String getLine1Number(int); method public java.lang.String getMmsUAProfUrl(); method public java.lang.String getMmsUserAgent(); method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo(); method public java.lang.String getNetworkCountryIso(); + method public java.lang.String getNetworkCountryIso(int); method public java.lang.String getNetworkOperator(); + method public java.lang.String getNetworkOperator(int); method public java.lang.String getNetworkOperatorName(); + method public java.lang.String getNetworkOperatorName(int); method public int getNetworkType(); + method public int getNetworkType(int); method public int getPhoneCount(); method public int getPhoneType(); method public java.lang.String getSimCountryIso(); + method public java.lang.String getSimCountryIso(int); method public java.lang.String getSimOperator(); + method public java.lang.String getSimOperator(int); method public java.lang.String getSimOperatorName(); + method public java.lang.String getSimOperatorName(int); method public java.lang.String getSimSerialNumber(); + method public java.lang.String getSimSerialNumber(int); method public int getSimState(); method public java.lang.String getSubscriberId(); + method public java.lang.String getSubscriberId(int); method public java.lang.String getVoiceMailAlphaTag(); + method public java.lang.String getVoiceMailAlphaTag(int); method public java.lang.String getVoiceMailNumber(); + method public java.lang.String getVoiceMailNumber(int); + method public int getVoiceNetworkType(int); method public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle); method public boolean hasCarrierPrivileges(); method public boolean hasIccCard(); @@ -36462,6 +36498,7 @@ package android.telephony { method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String); method public boolean isHearingAidCompatibilitySupported(); method public boolean isNetworkRoaming(); + method public boolean isNetworkRoaming(int); method public boolean isSmsCapable(); method public boolean isTtyModeSupported(); method public boolean isVoiceCapable(); @@ -36470,9 +36507,11 @@ package android.telephony { method public void listen(android.telephony.PhoneStateListener, int); method public java.lang.String sendEnvelopeWithStatus(java.lang.String); method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String); + method public boolean setLine1NumberForDisplay(int, java.lang.String, java.lang.String); method public boolean setOperatorBrandOverride(java.lang.String); method public boolean setPreferredNetworkTypeToGlobal(); method public boolean setVoiceMailNumber(java.lang.String, java.lang.String); + method public boolean setVoiceMailNumber(int, java.lang.String, java.lang.String); field public static final java.lang.String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL"; field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE"; field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE"; diff --git a/api/system-current.txt b/api/system-current.txt index 5774104125c4..30c464a2f2d9 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -95,7 +95,7 @@ package android { field public static final java.lang.String FACTORY_TEST = "android.permission.FACTORY_TEST"; field public static final java.lang.String FORCE_BACK = "android.permission.FORCE_BACK"; field public static final java.lang.String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES"; - field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS"; + field public static final deprecated java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS"; field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED"; field public static final java.lang.String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS"; field public static final java.lang.String GET_PACKAGE_IMPORTANCE = "android.permission.GET_PACKAGE_IMPORTANCE"; @@ -10504,7 +10504,6 @@ package android.content.res { method public java.lang.String getQuantityString(int, int, java.lang.Object...) throws android.content.res.Resources.NotFoundException; method public java.lang.String getQuantityString(int, int) throws android.content.res.Resources.NotFoundException; method public java.lang.CharSequence getQuantityText(int, int) throws android.content.res.Resources.NotFoundException; - method public java.util.Locale getResolvedLocale(); method public java.lang.String getResourceEntryName(int) throws android.content.res.Resources.NotFoundException; method public java.lang.String getResourceName(int) throws android.content.res.Resources.NotFoundException; method public java.lang.String getResourcePackageName(int) throws android.content.res.Resources.NotFoundException; @@ -14242,6 +14241,7 @@ package android.hardware.camera2 { field public static final int HOT_PIXEL_MODE_FAST = 1; // 0x1 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_3 = 3; // 0x3 field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; // 0x1 field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; // 0x2 field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; // 0x0 @@ -22206,6 +22206,7 @@ package android.media { field public static final int DEFAULT = 0; // 0x0 field public static final int H263 = 1; // 0x1 field public static final int H264 = 2; // 0x2 + field public static final int HEVC = 5; // 0x5 field public static final int MPEG_4_SP = 3; // 0x3 field public static final int VP8 = 4; // 0x4 } @@ -23875,10 +23876,10 @@ package android.media.tv { method public boolean canRecord(); method public android.content.Intent createSettingsIntent(); method public android.content.Intent createSetupIntent(); - method public static android.media.tv.TvInputInfo createTvInputInfo(android.content.Context, android.content.pm.ResolveInfo, android.hardware.hdmi.HdmiDeviceInfo, java.lang.String, java.lang.String, android.net.Uri) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static android.media.tv.TvInputInfo createTvInputInfo(android.content.Context, android.content.pm.ResolveInfo, android.hardware.hdmi.HdmiDeviceInfo, java.lang.String, int, android.graphics.drawable.Icon) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static android.media.tv.TvInputInfo createTvInputInfo(android.content.Context, android.content.pm.ResolveInfo, android.media.tv.TvInputHardwareInfo, java.lang.String, android.net.Uri) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static android.media.tv.TvInputInfo createTvInputInfo(android.content.Context, android.content.pm.ResolveInfo, android.media.tv.TvInputHardwareInfo, int, android.graphics.drawable.Icon) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; + method public static deprecated android.media.tv.TvInputInfo createTvInputInfo(android.content.Context, android.content.pm.ResolveInfo, android.hardware.hdmi.HdmiDeviceInfo, java.lang.String, java.lang.String, android.net.Uri) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; + method public static deprecated android.media.tv.TvInputInfo createTvInputInfo(android.content.Context, android.content.pm.ResolveInfo, android.hardware.hdmi.HdmiDeviceInfo, java.lang.String, int, android.graphics.drawable.Icon) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; + method public static deprecated android.media.tv.TvInputInfo createTvInputInfo(android.content.Context, android.content.pm.ResolveInfo, android.media.tv.TvInputHardwareInfo, java.lang.String, android.net.Uri) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; + method public static deprecated android.media.tv.TvInputInfo createTvInputInfo(android.content.Context, android.content.pm.ResolveInfo, android.media.tv.TvInputHardwareInfo, int, android.graphics.drawable.Icon) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method public int describeContents(); method public android.hardware.hdmi.HdmiDeviceInfo getHdmiDeviceInfo(); method public java.lang.String getId(); @@ -23908,6 +23909,18 @@ package android.media.tv { field public static final int TYPE_VGA = 1005; // 0x3ed } + public static final class TvInputInfo.Builder { + ctor public TvInputInfo.Builder(android.content.Context, java.lang.Class<?>); + method public android.media.tv.TvInputInfo build() throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; + method public android.media.tv.TvInputInfo.Builder setCanRecord(boolean); + method public android.media.tv.TvInputInfo.Builder setHdmiDeviceInfo(android.hardware.hdmi.HdmiDeviceInfo); + method public android.media.tv.TvInputInfo.Builder setIcon(android.graphics.drawable.Icon); + method public android.media.tv.TvInputInfo.Builder setLabel(int); + method public android.media.tv.TvInputInfo.Builder setParentId(java.lang.String); + method public android.media.tv.TvInputInfo.Builder setTunerCount(int); + method public android.media.tv.TvInputInfo.Builder setTvInputHardwareInfo(android.media.tv.TvInputHardwareInfo); + } + public static final class TvInputInfo.TvInputSettings { method public static java.util.Map<java.lang.String, java.lang.String> getCustomLabels(android.content.Context, int); method public static java.util.Set<java.lang.String> getHiddenTvInputIds(android.content.Context, int); @@ -24017,7 +24030,7 @@ package android.media.tv { public abstract class TvInputService extends android.app.Service { ctor public TvInputService(); method public final android.os.IBinder onBind(android.content.Intent); - method public abstract android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(java.lang.String); + method public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(java.lang.String); method public abstract android.media.tv.TvInputService.Session onCreateSession(java.lang.String); method public android.media.tv.TvInputInfo onHardwareAdded(android.media.tv.TvInputHardwareInfo); method public java.lang.String onHardwareRemoved(android.media.tv.TvInputHardwareInfo); @@ -25843,6 +25856,7 @@ package android.net.wifi { method public java.lang.String getAltSubjectMatch(); method public java.lang.String getAnonymousIdentity(); method public java.security.cert.X509Certificate getCaCertificate(); + method public java.security.cert.X509Certificate[] getCaCertificates(); method public java.security.cert.X509Certificate getClientCertificate(); method public java.lang.String getDomainSuffixMatch(); method public int getEapMethod(); @@ -25855,6 +25869,7 @@ package android.net.wifi { method public void setAltSubjectMatch(java.lang.String); method public void setAnonymousIdentity(java.lang.String); method public void setCaCertificate(java.security.cert.X509Certificate); + method public void setCaCertificates(java.security.cert.X509Certificate[]); method public void setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate); method public void setDomainSuffixMatch(java.lang.String); method public void setEapMethod(int); @@ -32939,10 +32954,14 @@ package android.provider { public static final class ContactsContract.Intents { ctor public ContactsContract.Intents(); + field public static final java.lang.String ACTION_VOICE_SEND_MESSAGE_TO_CONTACTS = "android.provider.action.VOICE_SEND_MESSAGE_TO_CONTACTS"; field public static final java.lang.String ATTACH_IMAGE = "com.android.contacts.action.ATTACH_IMAGE"; field public static final java.lang.String CONTACTS_DATABASE_CREATED = "android.provider.Contacts.DATABASE_CREATED"; field public static final java.lang.String EXTRA_CREATE_DESCRIPTION = "com.android.contacts.action.CREATE_DESCRIPTION"; field public static final java.lang.String EXTRA_FORCE_CREATE = "com.android.contacts.action.FORCE_CREATE"; + field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_CHAT_ID = "android.provider.extra.RECIPIENT_CONTACT_CHAT_ID"; + field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_NAME = "android.provider.extra.RECIPIENT_CONTACT_NAME"; + field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_URI = "android.provider.extra.RECIPIENT_CONTACT_URI"; field public static final java.lang.String INVITE_CONTACT = "com.android.contacts.action.INVITE_CONTACT"; field public static final java.lang.String SEARCH_SUGGESTION_CLICKED = "android.provider.Contacts.SEARCH_SUGGESTION_CLICKED"; field public static final java.lang.String SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED = "android.provider.Contacts.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED"; @@ -33218,6 +33237,7 @@ package android.provider { method public static android.net.Uri buildRootsUri(java.lang.String); method public static android.net.Uri buildSearchDocumentsUri(java.lang.String, java.lang.String, java.lang.String); method public static android.net.Uri buildTreeDocumentUri(java.lang.String, java.lang.String); + method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri); method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String); method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri); method public static java.lang.String getDocumentId(android.net.Uri); @@ -33226,6 +33246,7 @@ package android.provider { method public static java.lang.String getSearchDocumentsQuery(android.net.Uri); method public static java.lang.String getTreeDocumentId(android.net.Uri); method public static boolean isDocumentUri(android.content.Context, android.net.Uri); + method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri); method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String); field public static final java.lang.String EXTRA_ERROR = "error"; field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF"; @@ -37546,6 +37567,7 @@ package android.telecom { method public static boolean hasProperty(int, int); method public boolean hasProperty(int); method public static java.lang.String propertiesToString(int); + field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 4194304; // 0x400000 field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000 field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000 field public static final int CAPABILITY_HOLD = 1; // 0x1 @@ -37697,6 +37719,7 @@ package android.telecom { method public final void setVideoProvider(android.telecom.Connection.VideoProvider); method public final void setVideoState(int); method public static java.lang.String stateToString(int); + field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000 field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000 field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000 field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000 @@ -38348,8 +38371,6 @@ package android.telephony { public final class CellIdentityGsm implements android.os.Parcelable { method public int describeContents(); - method public int getArfcn(); - method public int getBsic(); method public int getCid(); method public int getLac(); method public int getMcc(); @@ -38362,7 +38383,6 @@ package android.telephony { public final class CellIdentityLte implements android.os.Parcelable { method public int describeContents(); method public int getCi(); - method public int getEarfcn(); method public int getMcc(); method public int getMnc(); method public int getPci(); @@ -38378,7 +38398,6 @@ package android.telephony { method public int getMcc(); method public int getMnc(); method public int getPsc(); - method public int getUarfcn(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityWcdma> CREATOR; } @@ -38800,10 +38819,15 @@ package android.telephony { method public int getActiveSubscriptionInfoCountMax(); method public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int); method public java.util.List<android.telephony.SubscriptionInfo> getActiveSubscriptionInfoList(); + method public static int getDefaultDataSubscriptionId(); + method public static int getDefaultSmsSubscriptionId(); + method public static int getDefaultSubscriptionId(); + method public static int getDefaultVoiceSubscriptionId(); method public boolean isNetworkRoaming(int); method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener); field public static final int DATA_ROAMING_DISABLE = 0; // 0x0 field public static final int DATA_ROAMING_ENABLE = 1; // 0x1 + field public static final int INVALID_SUBSCRIPTION_ID = -1; // 0xffffffff } public static class SubscriptionManager.OnSubscriptionsChangedListener { @@ -38824,6 +38848,7 @@ package android.telephony { method public boolean endCall(); method public java.util.List<android.telephony.CellInfo> getAllCellInfo(); method public int getCallState(); + method public int getCallState(int); method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent); method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int); method public java.lang.String getCdmaMdn(); @@ -38836,30 +38861,46 @@ package android.telephony { method public int getDataActivity(); method public boolean getDataEnabled(); method public boolean getDataEnabled(int); + method public int getDataNetworkType(int); method public int getDataState(); method public java.lang.String getDeviceId(); method public java.lang.String getDeviceId(int); method public java.lang.String getDeviceSoftwareVersion(); method public java.lang.String getGroupIdLevel1(); + method public java.lang.String getGroupIdLevel1(int); method public java.lang.String getIccSimChallengeResponse(int, java.lang.String); + method public java.lang.String getLine1AlphaTag(int); method public java.lang.String getLine1Number(); + method public java.lang.String getLine1Number(int); method public java.lang.String getMmsUAProfUrl(); method public java.lang.String getMmsUserAgent(); method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo(); method public java.lang.String getNetworkCountryIso(); + method public java.lang.String getNetworkCountryIso(int); method public java.lang.String getNetworkOperator(); + method public java.lang.String getNetworkOperator(int); method public java.lang.String getNetworkOperatorName(); + method public java.lang.String getNetworkOperatorName(int); method public int getNetworkType(); + method public int getNetworkType(int); method public int getPhoneCount(); method public int getPhoneType(); method public java.lang.String getSimCountryIso(); + method public java.lang.String getSimCountryIso(int); method public java.lang.String getSimOperator(); + method public java.lang.String getSimOperator(int); method public java.lang.String getSimOperatorName(); + method public java.lang.String getSimOperatorName(int); method public java.lang.String getSimSerialNumber(); + method public java.lang.String getSimSerialNumber(int); method public int getSimState(); method public java.lang.String getSubscriberId(); + method public java.lang.String getSubscriberId(int); method public java.lang.String getVoiceMailAlphaTag(); + method public java.lang.String getVoiceMailAlphaTag(int); method public java.lang.String getVoiceMailNumber(); + method public java.lang.String getVoiceMailNumber(int); + method public int getVoiceNetworkType(int); method public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle); method public boolean handlePinMmi(java.lang.String); method public boolean handlePinMmiForSubscriber(int, java.lang.String); @@ -38874,6 +38915,7 @@ package android.telephony { method public boolean isHearingAidCompatibilitySupported(); method public boolean isIdle(); method public boolean isNetworkRoaming(); + method public boolean isNetworkRoaming(int); method public boolean isOffhook(); method public boolean isRadioOn(); method public boolean isRinging(); @@ -38889,11 +38931,13 @@ package android.telephony { method public void setDataEnabled(boolean); method public void setDataEnabled(int, boolean); method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String); + method public boolean setLine1NumberForDisplay(int, java.lang.String, java.lang.String); method public boolean setOperatorBrandOverride(java.lang.String); method public boolean setPreferredNetworkTypeToGlobal(); method public boolean setRadio(boolean); method public boolean setRadioPower(boolean); method public boolean setVoiceMailNumber(java.lang.String, java.lang.String); + method public boolean setVoiceMailNumber(int, java.lang.String, java.lang.String); method public void silenceRinger(); method public boolean supplyPin(java.lang.String); method public int[] supplyPinReportResult(java.lang.String); diff --git a/api/test-current.txt b/api/test-current.txt index 2955158900ca..fab1f045f529 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -67,7 +67,7 @@ package android { field public static final java.lang.String DUMP = "android.permission.DUMP"; field public static final java.lang.String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR"; field public static final java.lang.String FACTORY_TEST = "android.permission.FACTORY_TEST"; - field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS"; + field public static final deprecated java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS"; field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED"; field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE"; field public static final deprecated java.lang.String GET_TASKS = "android.permission.GET_TASKS"; @@ -10116,7 +10116,6 @@ package android.content.res { method public java.lang.String getQuantityString(int, int, java.lang.Object...) throws android.content.res.Resources.NotFoundException; method public java.lang.String getQuantityString(int, int) throws android.content.res.Resources.NotFoundException; method public java.lang.CharSequence getQuantityText(int, int) throws android.content.res.Resources.NotFoundException; - method public java.util.Locale getResolvedLocale(); method public java.lang.String getResourceEntryName(int) throws android.content.res.Resources.NotFoundException; method public java.lang.String getResourceName(int) throws android.content.res.Resources.NotFoundException; method public java.lang.String getResourcePackageName(int) throws android.content.res.Resources.NotFoundException; @@ -13849,6 +13848,7 @@ package android.hardware.camera2 { field public static final int HOT_PIXEL_MODE_FAST = 1; // 0x1 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_3 = 3; // 0x3 field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; // 0x1 field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; // 0x2 field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; // 0x0 @@ -20865,6 +20865,7 @@ package android.media { field public static final int DEFAULT = 0; // 0x0 field public static final int H263 = 1; // 0x1 field public static final int H264 = 2; // 0x2 + field public static final int HEVC = 5; // 0x5 field public static final int MPEG_4_SP = 3; // 0x3 field public static final int VP8 = 4; // 0x4 } @@ -22429,6 +22430,13 @@ package android.media.tv { field public static final int TYPE_VGA = 1005; // 0x3ed } + public static final class TvInputInfo.Builder { + ctor public TvInputInfo.Builder(android.content.Context, java.lang.Class<?>); + method public android.media.tv.TvInputInfo build() throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; + method public android.media.tv.TvInputInfo.Builder setCanRecord(boolean); + method public android.media.tv.TvInputInfo.Builder setTunerCount(int); + } + public final class TvInputManager { method public int getInputState(java.lang.String); method public android.media.tv.TvInputInfo getTvInputInfo(java.lang.String); @@ -22471,7 +22479,7 @@ package android.media.tv { public abstract class TvInputService extends android.app.Service { ctor public TvInputService(); method public final android.os.IBinder onBind(android.content.Intent); - method public abstract android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(java.lang.String); + method public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(java.lang.String); method public abstract android.media.tv.TvInputService.Session onCreateSession(java.lang.String); method public final void setTvInputInfo(java.lang.String, android.media.tv.TvInputInfo); field public static final java.lang.String SERVICE_INTERFACE = "android.media.tv.TvInputService"; @@ -23969,6 +23977,7 @@ package android.net.wifi { method public java.lang.String getAltSubjectMatch(); method public java.lang.String getAnonymousIdentity(); method public java.security.cert.X509Certificate getCaCertificate(); + method public java.security.cert.X509Certificate[] getCaCertificates(); method public java.security.cert.X509Certificate getClientCertificate(); method public java.lang.String getDomainSuffixMatch(); method public int getEapMethod(); @@ -23981,6 +23990,7 @@ package android.net.wifi { method public void setAltSubjectMatch(java.lang.String); method public void setAnonymousIdentity(java.lang.String); method public void setCaCertificate(java.security.cert.X509Certificate); + method public void setCaCertificates(java.security.cert.X509Certificate[]); method public void setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate); method public void setDomainSuffixMatch(java.lang.String); method public void setEapMethod(int); @@ -30867,10 +30877,14 @@ package android.provider { public static final class ContactsContract.Intents { ctor public ContactsContract.Intents(); + field public static final java.lang.String ACTION_VOICE_SEND_MESSAGE_TO_CONTACTS = "android.provider.action.VOICE_SEND_MESSAGE_TO_CONTACTS"; field public static final java.lang.String ATTACH_IMAGE = "com.android.contacts.action.ATTACH_IMAGE"; field public static final java.lang.String CONTACTS_DATABASE_CREATED = "android.provider.Contacts.DATABASE_CREATED"; field public static final java.lang.String EXTRA_CREATE_DESCRIPTION = "com.android.contacts.action.CREATE_DESCRIPTION"; field public static final java.lang.String EXTRA_FORCE_CREATE = "com.android.contacts.action.FORCE_CREATE"; + field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_CHAT_ID = "android.provider.extra.RECIPIENT_CONTACT_CHAT_ID"; + field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_NAME = "android.provider.extra.RECIPIENT_CONTACT_NAME"; + field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_URI = "android.provider.extra.RECIPIENT_CONTACT_URI"; field public static final java.lang.String INVITE_CONTACT = "com.android.contacts.action.INVITE_CONTACT"; field public static final java.lang.String SEARCH_SUGGESTION_CLICKED = "android.provider.Contacts.SEARCH_SUGGESTION_CLICKED"; field public static final java.lang.String SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED = "android.provider.Contacts.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED"; @@ -31116,6 +31130,7 @@ package android.provider { method public static android.net.Uri buildRootsUri(java.lang.String); method public static android.net.Uri buildSearchDocumentsUri(java.lang.String, java.lang.String, java.lang.String); method public static android.net.Uri buildTreeDocumentUri(java.lang.String, java.lang.String); + method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri); method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String); method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri); method public static java.lang.String getDocumentId(android.net.Uri); @@ -31124,6 +31139,7 @@ package android.provider { method public static java.lang.String getSearchDocumentsQuery(android.net.Uri); method public static java.lang.String getTreeDocumentId(android.net.Uri); method public static boolean isDocumentUri(android.content.Context, android.net.Uri); + method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri); method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String); field public static final java.lang.String EXTRA_ERROR = "error"; field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF"; @@ -35267,6 +35283,7 @@ package android.telecom { method public static boolean hasProperty(int, int); method public boolean hasProperty(int); method public static java.lang.String propertiesToString(int); + field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 4194304; // 0x400000 field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000 field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000 field public static final int CAPABILITY_HOLD = 1; // 0x1 @@ -35407,6 +35424,7 @@ package android.telecom { method public final void setVideoProvider(android.telecom.Connection.VideoProvider); method public final void setVideoState(int); method public static java.lang.String stateToString(int); + field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000 field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000 field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000 field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000 @@ -35973,8 +35991,6 @@ package android.telephony { public final class CellIdentityGsm implements android.os.Parcelable { method public int describeContents(); - method public int getArfcn(); - method public int getBsic(); method public int getCid(); method public int getLac(); method public int getMcc(); @@ -35987,7 +36003,6 @@ package android.telephony { public final class CellIdentityLte implements android.os.Parcelable { method public int describeContents(); method public int getCi(); - method public int getEarfcn(); method public int getMcc(); method public int getMnc(); method public int getPci(); @@ -36003,7 +36018,6 @@ package android.telephony { method public int getMcc(); method public int getMnc(); method public int getPsc(); - method public int getUarfcn(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityWcdma> CREATOR; } @@ -36425,10 +36439,15 @@ package android.telephony { method public int getActiveSubscriptionInfoCountMax(); method public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int); method public java.util.List<android.telephony.SubscriptionInfo> getActiveSubscriptionInfoList(); + method public static int getDefaultDataSubscriptionId(); + method public static int getDefaultSmsSubscriptionId(); + method public static int getDefaultSubscriptionId(); + method public static int getDefaultVoiceSubscriptionId(); method public boolean isNetworkRoaming(int); method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener); field public static final int DATA_ROAMING_DISABLE = 0; // 0x0 field public static final int DATA_ROAMING_ENABLE = 1; // 0x1 + field public static final int INVALID_SUBSCRIPTION_ID = -1; // 0xffffffff } public static class SubscriptionManager.OnSubscriptionsChangedListener { @@ -36440,32 +36459,49 @@ package android.telephony { method public boolean canChangeDtmfToneLength(); method public java.util.List<android.telephony.CellInfo> getAllCellInfo(); method public int getCallState(); + method public int getCallState(int); method public android.telephony.CellLocation getCellLocation(); method public int getDataActivity(); + method public int getDataNetworkType(int); method public int getDataState(); method public java.lang.String getDeviceId(); method public java.lang.String getDeviceId(int); method public java.lang.String getDeviceSoftwareVersion(); method public java.lang.String getGroupIdLevel1(); + method public java.lang.String getGroupIdLevel1(int); method public java.lang.String getIccSimChallengeResponse(int, java.lang.String); + method public java.lang.String getLine1AlphaTag(int); method public java.lang.String getLine1Number(); + method public java.lang.String getLine1Number(int); method public java.lang.String getMmsUAProfUrl(); method public java.lang.String getMmsUserAgent(); method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo(); method public java.lang.String getNetworkCountryIso(); + method public java.lang.String getNetworkCountryIso(int); method public java.lang.String getNetworkOperator(); + method public java.lang.String getNetworkOperator(int); method public java.lang.String getNetworkOperatorName(); + method public java.lang.String getNetworkOperatorName(int); method public int getNetworkType(); + method public int getNetworkType(int); method public int getPhoneCount(); method public int getPhoneType(); method public java.lang.String getSimCountryIso(); + method public java.lang.String getSimCountryIso(int); method public java.lang.String getSimOperator(); + method public java.lang.String getSimOperator(int); method public java.lang.String getSimOperatorName(); + method public java.lang.String getSimOperatorName(int); method public java.lang.String getSimSerialNumber(); + method public java.lang.String getSimSerialNumber(int); method public int getSimState(); method public java.lang.String getSubscriberId(); + method public java.lang.String getSubscriberId(int); method public java.lang.String getVoiceMailAlphaTag(); + method public java.lang.String getVoiceMailAlphaTag(int); method public java.lang.String getVoiceMailNumber(); + method public java.lang.String getVoiceMailNumber(int); + method public int getVoiceNetworkType(int); method public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle); method public boolean hasCarrierPrivileges(); method public boolean hasIccCard(); @@ -36476,6 +36512,7 @@ package android.telephony { method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String); method public boolean isHearingAidCompatibilitySupported(); method public boolean isNetworkRoaming(); + method public boolean isNetworkRoaming(int); method public boolean isSmsCapable(); method public boolean isTtyModeSupported(); method public boolean isVoiceCapable(); @@ -36484,9 +36521,11 @@ package android.telephony { method public void listen(android.telephony.PhoneStateListener, int); method public java.lang.String sendEnvelopeWithStatus(java.lang.String); method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String); + method public boolean setLine1NumberForDisplay(int, java.lang.String, java.lang.String); method public boolean setOperatorBrandOverride(java.lang.String); method public boolean setPreferredNetworkTypeToGlobal(); method public boolean setVoiceMailNumber(java.lang.String, java.lang.String); + method public boolean setVoiceMailNumber(int, java.lang.String, java.lang.String); field public static final java.lang.String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL"; field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE"; field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE"; diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java index 6dc3cd1d48e0..b83484d99824 100644 --- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java +++ b/cmds/dpm/src/com/android/commands/dpm/Dpm.java @@ -18,6 +18,7 @@ package com.android.commands.dpm; import android.app.ActivityManagerNative; import android.app.IActivityManager; +import android.app.admin.DevicePolicyManager; import android.app.admin.IDevicePolicyManager; import android.content.ComponentName; import android.content.Context; @@ -143,6 +144,10 @@ public final class Dpm extends BaseCommand { mDevicePolicyManager.removeActiveAdmin(mComponent, UserHandle.USER_SYSTEM); throw e; } + + mDevicePolicyManager.setUserProvisioningState( + DevicePolicyManager.STATE_USER_SETUP_FINALIZED, mUserId); + System.out.println("Success: Device owner set to package " + mComponent); System.out.println("Active admin set to component " + mComponent.toShortString()); } @@ -161,6 +166,10 @@ public final class Dpm extends BaseCommand { mDevicePolicyManager.removeActiveAdmin(mComponent, mUserId); throw e; } + + mDevicePolicyManager.setUserProvisioningState( + DevicePolicyManager.STATE_USER_SETUP_FINALIZED, mUserId); + System.out.println("Success: Active admin and profile owner set to " + mComponent.toShortString() + " for user " + mUserId); } @@ -180,4 +189,4 @@ public final class Dpm extends BaseCommand { throw new IllegalArgumentException ("Invalid integer argument '" + argument + "'", e); } } -}
\ No newline at end of file +} diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index 1d9e3bb4b4bb..35695c47d19b 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -430,47 +430,46 @@ public class AccountManager { } /** - * Lists all accounts of any type registered on the device. - * Equivalent to getAccountsByType(null). + * List every {@link Account} registered on the device that are managed by + * applications whose signatures match the caller. * - * <p>It is safe to call this method from the main thread. + * <p>This method can be called safely from the main thread. It is + * equivalent to calling <code>getAccountsByType(null)</code>. * - * <p>Clients of this method that have not been granted the - * {@link android.Manifest.permission#GET_ACCOUNTS} permission, - * will only see those accounts managed by AbstractAccountAuthenticators whose - * signature matches the client. + * <p><b>NOTE:</b> Apps declaring a {@code targetSdkVersion<=23} in their + * manifests will continue to behave as they did on devices that support + * API level 23. In particular the GET_ACCOUNTS permission is required to + * see all the Accounts registered with the AccountManager. See docs for + * this function in API level 23 for more information. * - * @return An array of {@link Account}, one for each account. Empty - * (never null) if no accounts have been added. + * @return Array of Accounts. The array may be empty if no accounts are + * available to the caller. */ @NonNull - @RequiresPermission(GET_ACCOUNTS) public Account[] getAccounts() { - try { - return mService.getAccounts(null, mContext.getOpPackageName()); - } catch (RemoteException e) { - // won't ever happen - throw new RuntimeException(e); - } + return getAccountsByType(null); } /** * @hide - * Lists all accounts of any type registered on the device for a given - * user id. Equivalent to getAccountsByType(null). + * List every {@link Account} registered on the device for a specific User + * that are managed by applications whose signatures match the caller. * - * <p>It is safe to call this method from the main thread. + * <p><b>NOTE:</b> Apps declaring a {@code targetSdkVersion<=23} in their + * manifests will continue to behave as they did on devices that support + * API level 23. In particular the GET_ACCOUNTS permission is required to + * see all the Accounts registered with the AccountManager for the + * specified userId. See docs for this function in API level 23 for more + * information. * - * <p>Clients of this method that have not been granted the - * {@link android.Manifest.permission#GET_ACCOUNTS} permission, - * will only see those accounts managed by AbstractAccountAuthenticators whose - * signature matches the client. + * <p>This method can be called safely from the main thread. * - * @return An array of {@link Account}, one for each account. Empty - * (never null) if no accounts have been added. + * @param int userId associated with the User whose accounts should be + * queried. + * @return Array of Accounts. The array may be empty if no accounts are + * available to the caller. */ @NonNull - @RequiresPermission(GET_ACCOUNTS) public Account[] getAccountsAsUser(int userId) { try { return mService.getAccountsAsUser(null, userId, mContext.getOpPackageName()); @@ -501,10 +500,11 @@ public class AccountManager { /** * Returns the accounts visible to the specified package, in an environment where some apps * are not authorized to view all accounts. This method can only be called by system apps. + * * @param type The type of accounts to return, null to retrieve all accounts * @param packageName The package name of the app for which the accounts are to be returned - * @return An array of {@link Account}, one per matching account. Empty - * (never null) if no accounts of the specified type have been added. + * @return Array of Accounts. The array may be empty if no accounts of th + * specified type are visible to the caller. */ @NonNull public Account[] getAccountsByTypeForPackage(String type, String packageName) { @@ -518,29 +518,22 @@ public class AccountManager { } /** - * Lists all accounts of a particular type. The account type is a - * string token corresponding to the authenticator and useful domain - * of the account. For example, there are types corresponding to Google - * and Facebook. The exact string token to use will be published somewhere - * associated with the authenticator in question. + * List every {@link Account} of a specified type managed by applications + * whose signatures match the caller. * - * <p>It is safe to call this method from the main thread. - * - * <p>Clients of this method that have not been granted the - * {@link android.Manifest.permission#GET_ACCOUNTS} permission, - * will only see those accounts managed by AbstractAccountAuthenticators whose - * signature matches the client. + * <p><b>NOTE:</b> Apps declaring a {@code targetSdkVersion<=23} in their + * manifests will continue to behave as they did on devices that support + * API level 23. See docs for this function in API level 23 for more + * information. * - * <p><b>NOTE:</b> If targeting your app to work on API level 22 and before, - * GET_ACCOUNTS permission is needed for those platforms, irrespective of uid - * or signature match. See docs for this function in API level 22. + * <p>This method can be called safely from the main thread. * - * @param type The type of accounts to return, null to retrieve all accounts - * @return An array of {@link Account}, one per matching account. Empty - * (never null) if no accounts of the specified type have been added. + * @param type String denoting the type of the accounts to return, + * {@code null} to retrieve all accounts visible to the caller. + * @return An array of Accounts. Empty (never null) if no accounts + * are available to the caller. */ @NonNull - @RequiresPermission(GET_ACCOUNTS) public Account[] getAccountsByType(String type) { return getAccountsByTypeAsUser(type, Process.myUserHandle()); } @@ -586,6 +579,7 @@ public class AccountManager { * @return a future containing the label string * @hide */ + @NonNull public AccountManagerFuture<String> getAuthTokenLabel( final String accountType, final String authTokenType, AccountManagerCallback<String> callback, Handler handler) { @@ -618,9 +612,13 @@ public class AccountManager { * <p>This method may be called from any thread, but the returned * {@link AccountManagerFuture} must not be used on the main thread. * - * <p>This method requires the caller to hold the permission - * {@link android.Manifest.permission#GET_ACCOUNTS} or be a signature - * match with the AbstractAccountAuthenticator that manages the account. + * <p><b>Note:</b>The specified account must be managed by an application + * whose signature matches the caller. + * + * <p><b>Further note:</b>Apps targeting API level 23 or earlier will continue to + * behave as they did on devices that support API level 23. In particular + * they may still require the GET_ACCOUNTS permission. See docs for this + * function in API level 23. * * @param account The {@link Account} to test * @param features An array of the account features to check @@ -629,9 +627,11 @@ public class AccountManager { * @param handler {@link Handler} identifying the callback thread, * null for the main thread * @return An {@link AccountManagerFuture} which resolves to a Boolean, - * true if the account exists and has all of the specified features. + * true if the account exists and has all of the specified features. + * @throws SecurityException if the specified account is managed by an + * application whose signature doesn't match the caller's signature. */ - @RequiresPermission(GET_ACCOUNTS) + @NonNull public AccountManagerFuture<Boolean> hasFeatures(final Account account, final String[] features, AccountManagerCallback<Boolean> callback, Handler handler) { @@ -654,9 +654,10 @@ public class AccountManager { /** * Lists all accounts of a type which have certain features. The account - * type identifies the authenticator (see {@link #getAccountsByType}). - * Account features are authenticator-specific string tokens identifying - * boolean account properties (see {@link #hasFeatures}). + * type identifies the authenticator (see {@link #getAccountsByType}). Said + * authenticator must be in a package whose signature matches the callers + * package signature. Account features are authenticator-specific string tokens + * identifying boolean account properties (see {@link #hasFeatures}). * * <p>Unlike {@link #getAccountsByType}, this method calls the authenticator, * which may contact the server or do other work to check account features, @@ -665,19 +666,14 @@ public class AccountManager { * <p>This method may be called from any thread, but the returned * {@link AccountManagerFuture} must not be used on the main thread. * - * <p>Clients of this method that have not been granted the - * {@link android.Manifest.permission#GET_ACCOUNTS} permission, - * will only see those accounts managed by AbstractAccountAuthenticators whose - * signature matches the client. + * <p><b>NOTE:</b> Apps targeting API level 23 or earlier will continue to + * behave as they did on devices that support API level 23. In particular + * they may still require the GET_ACCOUNTS permission. See docs for this + * function in API level 23. * * @param type The type of accounts to return, must not be null * @param features An array of the account features to require, * may be null or empty - * - * <p><b>NOTE:</b> If targeting your app to work on API level 22 and before, - * GET_ACCOUNTS permission is needed for those platforms, irrespective of uid - * or signature match. See docs for this function in API level 22. - * * @param callback Callback to invoke when the request completes, * null for no callback * @param handler {@link Handler} identifying the callback thread, @@ -686,7 +682,7 @@ public class AccountManager { * {@link Account}, one per account of the specified type which * matches the requested features. */ - @RequiresPermission(GET_ACCOUNTS) + @NonNull public AccountManagerFuture<Account[]> getAccountsByTypeAndFeatures( final String type, final String[] features, AccountManagerCallback<Account[]> callback, Handler handler) { diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index f3e1fc3a9615..1e7457cdd551 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -3328,17 +3328,6 @@ public final class ActivityThread { } r.activity.performResume(); - // If there is a pending relaunch that was requested when the activity was paused, - // it will put the activity into paused state when it finally happens. Since the - // activity resumed before being relaunched, we don't want that to happen, so we - // need to clear the request to relaunch paused. - for (int i = mRelaunchingActivities.size() - 1; i >= 0; i--) { - final ActivityClientRecord relaunching = mRelaunchingActivities.get(i); - if (relaunching.token == r.token && relaunching.startsNotResumed) { - relaunching.startsNotResumed = false; - } - } - EventLog.writeEvent(LOG_AM_ON_RESUME_CALLED, UserHandle.myUserId(), r.activity.getComponentName().getClassName()); @@ -3567,7 +3556,6 @@ public final class ActivityThread { private void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving, int configChanges, boolean dontReport, int seq) { ActivityClientRecord r = mActivities.get(token); - if (DEBUG_ORDER) Slog.d(TAG, "handlePauseActivity " + r + ", seq: " + seq); if (!checkAndUpdateLifecycleSeq(seq, r, "pauseActivity")) { return; } @@ -4207,7 +4195,6 @@ public final class ActivityThread { synchronized (mResourcesManager) { for (int i=0; i<mRelaunchingActivities.size(); i++) { ActivityClientRecord r = mRelaunchingActivities.get(i); - if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: " + this + ", trying: " + r); if (r.token == token) { target = r; if (pendingResults != null) { @@ -4238,19 +4225,14 @@ public final class ActivityThread { } if (target == null) { - if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: target is null, fromServer:" - + fromServer); target = new ActivityClientRecord(); target.token = token; target.pendingResults = pendingResults; target.pendingIntents = pendingNewIntents; target.mPreserveWindow = preserveWindow; if (!fromServer) { - final ActivityClientRecord existing = mActivities.get(token); - if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: " + existing); + ActivityClientRecord existing = mActivities.get(token); if (existing != null) { - if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: paused= " - + existing.paused);; target.startsNotResumed = existing.paused; target.overrideConfig = existing.overrideConfig; } @@ -4273,8 +4255,8 @@ public final class ActivityThread { target.pendingConfigChanges |= configChanges; target.relaunchSeq = getLifecycleSeq(); } - if (DEBUG_ORDER) Slog.d(TAG, "relaunchActivity " + ActivityThread.this + ", target " - + target + " operation received seq: " + target.relaunchSeq); + if (DEBUG_ORDER) Slog.d(TAG, "relaunchActivity " + ActivityThread.this + + " operation received seq: " + target.relaunchSeq); } private void handleRelaunchActivity(ActivityClientRecord tmp) { @@ -4934,8 +4916,10 @@ public final class ActivityThread { /* * Initialize the default locales in this process for the reasons we set the time zone. + * + * We do this through ResourcesManager, since we need to do locale negotiation. */ - LocaleList.setDefault(data.config.getLocales()); + mResourcesManager.setDefaultLocalesLocked(data.config.getLocales()); /* * Update the system configuration since its preloaded and might not diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 368b8ef17cc9..3288cd91856d 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -48,13 +48,12 @@ interface INotificationManager boolean areNotificationsEnabledForPackage(String pkg, int uid); ParceledListSlice getTopics(String pkg, int uid); - void setTopicVisibilityOverride(String pkg, int uid, in Notification.Topic topic, int visibility); - int getTopicVisibilityOverride(String pkg, int uid, in Notification.Topic topic); - void setTopicPriority(String pkg, int uid, in Notification.Topic topic, int priority); - int getTopicPriority(String pkg, int uid, in Notification.Topic topic); - void setTopicImportance(String pkg, int uid, in Notification.Topic topic, int importance); - int getTopicImportance(String pkg, int uid, in Notification.Topic topic); - void setAppImportance(String pkg, int uid, int importance); + void setVisibilityOverride(String pkg, int uid, in Notification.Topic topic, int visibility); + int getVisibilityOverride(String pkg, int uid, in Notification.Topic topic); + void setPriority(String pkg, int uid, in Notification.Topic topic, int priority); + int getPriority(String pkg, int uid, in Notification.Topic topic); + void setImportance(String pkg, int uid, in Notification.Topic topic, int importance); + int getImportance(String pkg, int uid, in Notification.Topic topic); boolean doesAppUseTopics(String pkg, int uid); // TODO: Remove this when callers have been migrated to the equivalent diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 260216cfefb8..94e584eeaa98 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -35,6 +35,9 @@ import android.view.Display; import android.view.DisplayAdjustments; import java.lang.ref.WeakReference; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; /** @hide */ public class ResourcesManager { @@ -42,11 +45,15 @@ public class ResourcesManager { private static final boolean DEBUG = false; private static ResourcesManager sResourcesManager; - private final ArrayMap<ResourcesKey, WeakReference<Resources> > mActiveResources = + private final ArrayMap<ResourcesKey, WeakReference<Resources>> mActiveResources = new ArrayMap<>(); private final ArrayMap<Pair<Integer, DisplayAdjustments>, WeakReference<Display>> mDisplays = new ArrayMap<>(); + private String[] mSystemLocales = {}; + private final HashSet<String> mNonSystemLocales = new HashSet<String>(); + private boolean mHasNonSystemLocales = false; + CompatibilityInfo mResCompatibilityInfo; Configuration mResConfiguration; @@ -165,6 +172,8 @@ public class ResourcesManager { ? new Configuration(overrideConfiguration) : null; ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfigCopy, scale); Resources r; + final boolean findSystemLocales; + final boolean hasNonSystemLocales; synchronized (this) { // Resources is app scale dependent. if (DEBUG) Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale); @@ -178,6 +187,8 @@ public class ResourcesManager { + " key=" + key + " overrideConfig=" + overrideConfiguration); return r; } + findSystemLocales = (mSystemLocales.length == 0); + hasNonSystemLocales = mHasNonSystemLocales; } //if (r != null) { @@ -243,6 +254,18 @@ public class ResourcesManager { if (DEBUG) Slog.i(TAG, "Created app resources " + resDir + " " + r + ": " + r.getConfiguration() + " appScale=" + r.getCompatibilityInfo().applicationScale); + final String[] systemLocales = ( + findSystemLocales ? + AssetManager.getSystem().getLocales() : + null); + final String[] nonSystemLocales = assets.getNonSystemLocales(); + // Avoid checking for non-pseudo-locales if we already know there were some from a previous + // Resources. The default value (for when hasNonSystemLocales is true) doesn't matter, + // since mHasNonSystemLocales will also be true, and thus isPseudoLocalesOnly would not be + // able to affect mHasNonSystemLocales. + final boolean isPseudoLocalesOnly = hasNonSystemLocales || + LocaleList.isPseudoLocalesOnly(nonSystemLocales); + synchronized (this) { WeakReference<Resources> wr = mActiveResources.get(key); Resources existing = wr != null ? wr.get() : null; @@ -255,11 +278,30 @@ public class ResourcesManager { // XXX need to remove entries when weak references go away mActiveResources.put(key, new WeakReference<>(r)); + if (mSystemLocales.length == 0) { + mSystemLocales = systemLocales; + } + mNonSystemLocales.addAll(Arrays.asList(nonSystemLocales)); + mHasNonSystemLocales = mHasNonSystemLocales || !isPseudoLocalesOnly; if (DEBUG) Slog.v(TAG, "mActiveResources.size()=" + mActiveResources.size()); return r; } } + /* package */ void setDefaultLocalesLocked(LocaleList locales) { + final int bestLocale; + if (mHasNonSystemLocales) { + bestLocale = locales.getFirstMatchIndexWithEnglishSupported(mNonSystemLocales); + } else { + // We fallback to system locales if there was no locale specifically supported by the + // assets. This is to properly support apps that only rely on the shared system assets + // and don't need assets of their own. + bestLocale = locales.getFirstMatchIndexWithEnglishSupported(mSystemLocales); + } + // set it for Java, this also affects newly created Resources + LocaleList.setDefault(locales, bestLocale); + } + final boolean applyConfigurationToResourcesLocked(Configuration config, CompatibilityInfo compat) { if (mResConfiguration == null) { @@ -283,13 +325,28 @@ public class ResourcesManager { | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; } - // set it for java, this also affects newly created Resources - final LocaleList localeList = config.getLocales(); - if (!localeList.isEmpty()) { - LocaleList.setDefault(localeList); + Configuration localeAdjustedConfig = config; + final LocaleList configLocales = config.getLocales(); + if (!configLocales.isEmpty()) { + setDefaultLocalesLocked(configLocales); + final LocaleList adjustedLocales = LocaleList.getAdjustedDefault(); + if (adjustedLocales != configLocales) { // has the same result as .equals() in this case + // The first locale in the list was not chosen. So we create a modified + // configuration with the adjusted locales (which moves the chosen locale to the + // front). + localeAdjustedConfig = new Configuration(); + localeAdjustedConfig.setTo(config); + localeAdjustedConfig.setLocales(adjustedLocales); + // Also adjust the locale list in mResConfiguration, so that the Resources created + // later would have the same locale list. + if (!mResConfiguration.getLocales().equals(adjustedLocales)) { + mResConfiguration.setLocales(adjustedLocales); + changes |= ActivityInfo.CONFIG_LOCALE; + } + } } - Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat); + Resources.updateSystemConfiguration(localeAdjustedConfig, defaultDisplayMetrics, compat); ApplicationPackageManager.configurationChanged(); //Slog.i(TAG, "Configuration changed in " + currentPackageName()); @@ -301,7 +358,7 @@ public class ResourcesManager { Resources r = mActiveResources.valueAt(i).get(); if (r != null) { if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources " - + r + " config to: " + config); + + r + " config to: " + localeAdjustedConfig); int displayId = key.mDisplayId; boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY); DisplayMetrics dm = defaultDisplayMetrics; @@ -310,7 +367,7 @@ public class ResourcesManager { if (tmpConfig == null) { tmpConfig = new Configuration(); } - tmpConfig.setTo(config); + tmpConfig.setTo(localeAdjustedConfig); if (!isDefaultDisplay) { dm = getDisplayMetricsLocked(displayId); applyNonDefaultDisplayMetricsToConfigurationLocked(dm, tmpConfig); @@ -320,7 +377,7 @@ public class ResourcesManager { } r.updateConfiguration(tmpConfig, dm, compat); } else { - r.updateConfiguration(config, dm, compat); + r.updateConfiguration(localeAdjustedConfig, dm, compat); } //Slog.i(TAG, "Updated app resources " + v.getKey() // + " " + r + ": " + r.getConfiguration()); diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index f3b1175e720e..8035c56de143 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -82,8 +82,6 @@ import android.net.wifi.nan.IWifiNanManager; import android.net.wifi.nan.WifiNanManager; import android.net.wifi.p2p.IWifiP2pManager; import android.net.wifi.p2p.WifiP2pManager; -import android.net.wifi.passpoint.IWifiPasspointManager; -import android.net.wifi.passpoint.WifiPasspointManager; import android.nfc.NfcManager; import android.os.BatteryManager; import android.os.DropBoxManager; @@ -483,15 +481,6 @@ final class SystemServiceRegistry { return new WifiManager(ctx.getOuterContext(), service); }}); - registerService(Context.WIFI_PASSPOINT_SERVICE, WifiPasspointManager.class, - new CachedServiceFetcher<WifiPasspointManager>() { - @Override - public WifiPasspointManager createService(ContextImpl ctx) { - IBinder b = ServiceManager.getService(Context.WIFI_PASSPOINT_SERVICE); - IWifiPasspointManager service = IWifiPasspointManager.Stub.asInterface(b); - return new WifiPasspointManager(ctx.getOuterContext(), service); - }}); - registerService(Context.WIFI_P2P_SERVICE, WifiP2pManager.class, new StaticServiceFetcher<WifiP2pManager>() { @Override diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 7007afee5890..879d6636bb7b 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -16,6 +16,7 @@ package android.app.admin; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; @@ -53,6 +54,8 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.net.InetSocketAddress; import java.net.Proxy; import java.security.KeyFactory; @@ -280,6 +283,21 @@ public class DevicePolicyManager { = "android.app.action.PROVISION_MANAGED_SHAREABLE_DEVICE"; /** + * Activity action: Finalizes management provisioning, should be used after user-setup + * has been completed and {@link #getUserProvisioningState()} returns one of: + * <ul> + * <li>{@link #STATE_USER_SETUP_INCOMPLETE}</li> + * <li>{@link #STATE_USER_SETUP_COMPLETE}</li> + * <li>{@link #STATE_USER_PROFILE_COMPLETE}</li> + * </ul> + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_PROVISION_FINALIZATION + = "android.app.action.PROVISION_FINALIZATION"; + + /** * A {@link android.os.Parcelable} extra of type {@link android.os.PersistableBundle} that * allows a mobile device management application or NFC programmer application which starts * managed provisioning to pass data to the management application instance after provisioning. @@ -861,6 +879,44 @@ public class DevicePolicyManager { public static final int PERMISSION_GRANT_STATE_DENIED = 2; /** + * No management for current user in-effect. This is the default. + * @hide + */ + public static final int STATE_USER_UNMANAGED = 0; + + /** + * Management partially setup, user setup needs to be completed. + * @hide + */ + public static final int STATE_USER_SETUP_INCOMPLETE = 1; + + /** + * Management partially setup, user setup completed. + * @hide + */ + public static final int STATE_USER_SETUP_COMPLETE = 2; + + /** + * Management setup and active on current user. + * @hide + */ + public static final int STATE_USER_SETUP_FINALIZED = 3; + + /** + * Management partially setup on a managed profile. + * @hide + */ + public static final int STATE_USER_PROFILE_COMPLETE = 4; + + /** + * @hide + */ + @IntDef({STATE_USER_UNMANAGED, STATE_USER_SETUP_INCOMPLETE, STATE_USER_SETUP_COMPLETE, + STATE_USER_SETUP_FINALIZED, STATE_USER_PROFILE_COMPLETE}) + @Retention(RetentionPolicy.SOURCE) + public @interface UserProvisioningState {} + + /** * Return true if the given administrator component is currently * active (enabled) in the system. */ @@ -3736,14 +3792,15 @@ public class DevicePolicyManager { /** * Start Quick Contact on the managed profile for the user, if the policy allows. + * * @hide */ public void startManagedQuickContact(String actualLookupKey, long actualContactId, - long directoryId, Intent originalIntent) { + boolean isContactIdIgnored, long directoryId, Intent originalIntent) { if (mService != null) { try { - mService.startManagedQuickContact( - actualLookupKey, actualContactId, directoryId, originalIntent); + mService.startManagedQuickContact(actualLookupKey, actualContactId, + isContactIdIgnored, directoryId, originalIntent); } catch (RemoteException e) { Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e); } @@ -3756,7 +3813,7 @@ public class DevicePolicyManager { */ public void startManagedQuickContact(String actualLookupKey, long actualContactId, Intent originalIntent) { - startManagedQuickContact(actualLookupKey, actualContactId, Directory.DEFAULT, + startManagedQuickContact(actualLookupKey, actualContactId, false, Directory.DEFAULT, originalIntent); } @@ -5247,6 +5304,40 @@ public class DevicePolicyManager { } /** + * @return the {@link UserProvisioningState} for the current user - for unmanaged users will + * return {@link #STATE_USER_UNMANAGED} + * @hide + */ + @UserProvisioningState + public int getUserProvisioningState() { + if (mService != null) { + try { + return mService.getUserProvisioningState(); + } catch (RemoteException e) { + Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e); + } + } + return STATE_USER_UNMANAGED; + } + + /** + * Set the {@link UserProvisioningState} for the supplied user, if they are managed. + * + * @param state to store + * @param userHandle for user + * @hide + */ + public void setUserProvisioningState(@UserProvisioningState int state, int userHandle) { + if (mService != null) { + try { + mService.setUserProvisioningState(state, userHandle); + } catch (RemoteException e) { + Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e); + } + } + } + + /** * @hide * Indicates the entity that controls the device or profile owner. A user/profile is considered * affiliated if it is managed by the same entity as the device. diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index e97bdf2bd4e9..b34e678c8a60 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -211,7 +211,7 @@ interface IDevicePolicyManager { void setCrossProfileContactsSearchDisabled(in ComponentName who, boolean disabled); boolean getCrossProfileContactsSearchDisabled(in ComponentName who); boolean getCrossProfileContactsSearchDisabledForUser(int userId); - void startManagedQuickContact(String lookupKey, long contactId, long directoryId, in Intent originalIntent); + void startManagedQuickContact(String lookupKey, long contactId, boolean isContactIdIgnored, long directoryId, in Intent originalIntent); void setBluetoothContactSharingDisabled(in ComponentName who, boolean disabled); boolean getBluetoothContactSharingDisabled(in ComponentName who); @@ -269,6 +269,9 @@ interface IDevicePolicyManager { int getOrganizationColor(in ComponentName admin); int getOrganizationColorForUser(int userHandle); + int getUserProvisioningState(); + void setUserProvisioningState(int state, int userHandle); + void setAffiliationIds(in ComponentName admin, in List<String> ids); boolean isAffiliatedUser(); } diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index 74cb0f6cd25c..9cd7d0585af5 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -104,8 +104,6 @@ interface IBluetooth void getActivityEnergyInfoFromController(); BluetoothActivityEnergyInfo reportActivityInfo(); - // For dumpsys support - void dump(in ParcelFileDescriptor fd); void onLeServiceUp(); void onBrEdrDown(); } diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java index 0b80f8a9fc18..6dfefacb2cb4 100644 --- a/core/java/android/content/ClipData.java +++ b/core/java/android/content/ClipData.java @@ -17,11 +17,14 @@ package android.content; import static android.content.ContentProvider.maybeAddUserId; + +import android.content.pm.PackageManager; import android.content.res.AssetFileDescriptor; import android.graphics.Bitmap; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; +import android.os.Process; import android.os.StrictMode; import android.text.Html; import android.text.Spannable; @@ -462,7 +465,12 @@ public class ClipData implements Parcelable { // Check to see what data representations the content // provider supports. We would like HTML text, but if that // is not possible we'll live with plan text. - String[] types = context.getContentResolver().getStreamTypes(mUri, "text/*"); + String[] types = null; + try { + types = context.getContentResolver().getStreamTypes(mUri, "text/*"); + } catch (SecurityException e) { + // No read permission for mUri, assume empty stream types list. + } boolean hasHtml = false; boolean hasText = false; if (types != null) { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 48d019687b21..9df7a28c5b48 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2561,7 +2561,6 @@ public abstract class Context { //@hide: NETWORK_POLICY_SERVICE, WIFI_SERVICE, WIFI_NAN_SERVICE, - WIFI_PASSPOINT_SERVICE, WIFI_P2P_SERVICE, WIFI_SCANNING_SERVICE, //@hide: WIFI_RTT_SERVICE, @@ -3003,17 +3002,6 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a {@link - * android.net.wifi.passpoint.WifiPasspointManager} for handling management of - * Wi-Fi passpoint access. - * - * @see #getSystemService - * @see android.net.wifi.passpoint.WifiPasspointManager - * @hide - */ - public static final String WIFI_PASSPOINT_SERVICE = "wifipasspoint"; - - /** - * Use with {@link #getSystemService} to retrieve a {@link * android.net.wifi.p2p.WifiP2pManager} for handling management of * Wi-Fi peer-to-peer connections. * diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index ed64eade0351..e4044296dac4 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -162,10 +162,6 @@ public class Resources { private final Configuration mConfiguration = new Configuration(); - // Invariant: mResolvedLocale is the resolved locale of mLocalesForResolved - private LocaleList mLocalesForResolved = null; - private Locale mResolvedLocale = null; - private PluralRules mPluralRule; private CompatibilityInfo mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; @@ -321,16 +317,6 @@ public class Resources { } /** - * Return the Locale resulting from locale negotiation between the Resources and the - * Configuration objects used to construct the Resources. The locale is used for retrieving - * resources as well as for determining plural rules. - */ - @NonNull - public Locale getResolvedLocale() { - return mResolvedLocale; - } - - /** * Return the string value associated with a particular resource ID. The * returned object will be a String if this is a plain string; it will be * some other type of CharSequence if it is styled. @@ -394,7 +380,7 @@ public class Resources { private PluralRules getPluralRule() { synchronized (sSync) { if (mPluralRule == null) { - mPluralRule = PluralRules.forLocale(mResolvedLocale); + mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().getPrimary()); } return mPluralRule; } @@ -457,7 +443,7 @@ public class Resources { @NonNull public String getString(@StringRes int id, Object... formatArgs) throws NotFoundException { final String raw = getString(id); - return String.format(mResolvedLocale, raw, formatArgs); + return String.format(mConfiguration.getLocales().getPrimary(), raw, formatArgs); } /** @@ -488,7 +474,7 @@ public class Resources { public String getQuantityString(@PluralsRes int id, int quantity, Object... formatArgs) throws NotFoundException { String raw = getQuantityText(id, quantity).toString(); - return String.format(mResolvedLocale, raw, formatArgs); + return String.format(mConfiguration.getLocales().getPrimary(), raw, formatArgs); } /** @@ -1955,7 +1941,7 @@ public class Resources { LocaleList locales = mConfiguration.getLocales(); if (locales.isEmpty()) { - locales = LocaleList.getDefault(); + locales = LocaleList.getAdjustedDefault(); mConfiguration.setLocales(locales); } if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) { @@ -1983,26 +1969,8 @@ public class Resources { keyboardHidden = mConfiguration.keyboardHidden; } - if (locales != mLocalesForResolved) { - if (locales.size() == 1) { - // This is an optimization to avoid the JNI call(s) when the result of - // getFirstMatchWithEnglishSupported() does not depend on the supported locales. - mResolvedLocale = locales.getPrimary(); - } else { - String[] supportedLocales = mAssets.getNonSystemLocales(); - if (LocaleList.isPseudoLocalesOnly(supportedLocales)) { - // We fallback to all locales (including system locales) if there was no - // locale specifically supported by the assets. This is to properly support - // apps that only rely on the shared system assets and don't need assets of - // their own. - supportedLocales = mAssets.getLocales(); - } - mResolvedLocale = locales.getFirstMatchWithEnglishSupported(supportedLocales); - } - mLocalesForResolved = locales; - } mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc, - adjustLanguageTag(mResolvedLocale.toLanguageTag()), + adjustLanguageTag(locales.getPrimary().toLanguageTag()), mConfiguration.orientation, mConfiguration.touchscreen, mConfiguration.densityDpi, mConfiguration.keyboard, @@ -2027,7 +1995,7 @@ public class Resources { } synchronized (sSync) { if (mPluralRule != null) { - mPluralRule = PluralRules.forLocale(mResolvedLocale); + mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().getPrimary()); } } } diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 2695dfd6b652..2aa6af6f9e3c 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -2787,22 +2787,39 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri /** * <p>Generally classifies the overall set of the camera device functionality.</p> - * <p>Camera devices will come in three flavors: LEGACY, LIMITED and FULL.</p> - * <p>A FULL device will support below capabilities:</p> + * <p>The supported hardware level is a high-level description of the camera device's + * capabilities, summarizing several capabilities into one field. Each level adds additional + * features to the previous one, and is always a strict superset of the previous level. + * The ordering is <code>LEGACY < LIMITED < FULL < LEVEL_3</code>.</p> + * <p>Starting from <code>LEVEL_3</code>, the level enumerations are guaranteed to be in increasing + * numerical value as well. To check if a given device is at least at a given hardware level, + * the following code snippet can be used:</p> + * <pre><code>// Returns true if the device supports the required hardware level, or better. + * boolean isHardwareLevelSupported(CameraCharacteristics c, int requiredLevel) { + * int deviceLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL); + * if (deviceLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) { + * return requiredLevel == deviceLevel; + * } + * // deviceLevel is not LEGACY, can use numerical sort + * return requiredLevel <= deviceLevel; + * } + * </code></pre> + * <p>At a high level, the levels are:</p> * <ul> - * <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 - * MANUAL_POST_PROCESSING)</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 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> + * <li><code>LEGACY</code> devices operate in a backwards-compatibility mode for older + * Android devices, and have very limited capabilities.</li> + * <li><code>LIMITED</code> devices represent the + * baseline feature set, and may also include additional capabilities that are + * subsets of <code>FULL</code>.</li> + * <li><code>FULL</code> devices additionally support per-frame manual control of sensor, flash, lens and + * post-processing settings, and image capture at a high rate.</li> + * <li><code>LEVEL_3</code> devices additionally support YUV reprocessing and RAW image capture, along + * with additional output stream configurations.</li> * </ul> - * <p>A LIMITED device may have some or none of the above characteristics. - * To find out more refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}.</p> + * <p>See the individual level enums for full descriptions of the supported capabilities. The + * {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} entry describes the device's capabilities at a + * finer-grain level, if needed. In addition, many controls have their available settings or + * ranges defined in individual {@link android.hardware.camera2.CameraCharacteristics } entries.</p> * <p>Some features are not part of any particular hardware level or capability and must be * queried separately. These include:</p> * <ul> @@ -2813,19 +2830,12 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * ({@link CameraCharacteristics#LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION android.lens.info.availableOpticalStabilization}, * {@link CameraCharacteristics#CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES android.control.availableVideoStabilizationModes})</li> * </ul> - * <p>A LEGACY device does not support per-frame control, manual sensor control, manual - * 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>Note: - * Pre-API level 23, FULL devices also supported arbitrary cropping region - * ({@link CameraCharacteristics#SCALER_CROPPING_TYPE android.scaler.croppingType} <code>==</code> FREEFORM); this requirement was relaxed in API level 23, - * and FULL devices may only support CENTERED cropping.</p> * <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_3 3}</li> * </ul></p> * <p>This key is available on all devices.</p> * @@ -2833,16 +2843,12 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * @see CameraCharacteristics#LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION * @see CameraCharacteristics#LENS_INFO_FOCUS_DISTANCE_CALIBRATION * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES - * @see CameraCharacteristics#REQUEST_MAX_NUM_OUTPUT_PROC - * @see CameraCharacteristics#SCALER_CROPPING_TYPE - * @see CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE - * @see CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION * @see CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE * @see CameraCharacteristics#STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES - * @see CameraCharacteristics#SYNC_MAX_LATENCY * @see #INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED * @see #INFO_SUPPORTED_HARDWARE_LEVEL_FULL * @see #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY + * @see #INFO_SUPPORTED_HARDWARE_LEVEL_3 */ @PublicKey public static final Key<Integer> INFO_SUPPORTED_HARDWARE_LEVEL = diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 63bcb3187f73..28bb22acfe39 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -314,7 +314,7 @@ public abstract class CameraDevice implements AutoCloseable { * </table><br> * </p> * - * <p>Limited-capability ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} + * <p>Limited-level ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED}) devices * support at least the following stream combinations in addition to those for * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY} devices: @@ -332,13 +332,13 @@ public abstract class CameraDevice implements AutoCloseable { * </table><br> * </p> * - * <p>FULL-capability ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} + * <p>FULL-level ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}) devices * support at least the following stream combinations in addition to those for * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices: * * <table> - * <tr><th colspan="7">FULL-capability additional guaranteed configurations</th></tr> + * <tr><th colspan="7">FULL-level additional guaranteed configurations</th></tr> * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr> * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td colspan="2" id="rb"></td> <td>Maximum-resolution GPU processing with preview.</td> </tr> @@ -389,6 +389,22 @@ public abstract class CameraDevice implements AutoCloseable { * </table><br> * </p> * + * <p>LEVEL-3 ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} + * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_3 LEVEL_3}) + * support at least the following stream combinations in addition to the combinations for + * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL} and for + * RAW capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} includes + * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_RAW RAW}): + * + * <table> + * <tr><th colspan="11">LEVEL-3 additional guaranteed configurations</th></tr> + * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th rowspan="2">Sample use case(s)</th> </tr> + * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr> + * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code 640x480}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td> <td>In-app viewfinder analysis with dynamic selection of output format.</td> </tr> + * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code 640x480}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td> <td>In-app viewfinder analysis with dynamic selection of output format.</td> </tr> + * </table><br> + * </p> + * * <p>Since the capabilities of camera devices vary greatly, a given camera device may support * target combinations with sizes outside of these guarantees, but this can only be tested for * by attempting to create a session with such targets.</p> @@ -501,7 +517,7 @@ public abstract class CameraDevice implements AutoCloseable { * #rb { border-right-width: thick; } * </style> * - * <p>Limited-capability ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} + * <p>LIMITED-level ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED}) devices * support at least the following stream combinations for creating a reprocessable capture * session in addition to those listed in {@link #createCaptureSession createCaptureSession} for @@ -518,14 +534,14 @@ public abstract class CameraDevice implements AutoCloseable { * </table><br> * </p> * - * <p>FULL-capability ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} + * <p>FULL-level ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}) devices * support at least the following stream combinations for creating a reprocessable capture * session in addition to those for * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices: * * <table> - * <tr><th colspan="11">FULL-capability additional guaranteed configurations for creating a reprocessable capture session<br>({@code PRIV} input is guaranteed only if PRIVATE reprocessing is supported. {@code YUV} input is guaranteed only if YUV reprocessing is supported)</th></tr> + * <tr><th colspan="11">FULL-level additional guaranteed configurations for creating a reprocessable capture session<br>({@code PRIV} input is guaranteed only if PRIVATE reprocessing is supported. {@code YUV} input is guaranteed only if YUV reprocessing is supported)</th></tr> * <tr><th colspan="2" id="rb">Input</th><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th rowspan="2">Sample use case(s)</th> </tr> * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr> * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td></td><td id="rb"></td> <td></td><td id="rb"></td> <td>Maximum-resolution multi-frame image fusion in-app processing with regular preview.</td> </tr> @@ -555,6 +571,22 @@ public abstract class CameraDevice implements AutoCloseable { * </table><br> * </p> * + * <p>LEVEL-3 ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} + * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_3 LEVEL_3}) devices + * support at least the following stream combinations for creating a reprocessable capture + * session in addition to those for + * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL} devices. Note that targets in the "Reprocess-only target" column may only be + * used as an output target for a reprocess capture request, not as an output to a regular capture request. + * + * <table> + * <tr><th colspan="13">LEVEL-3 additional guaranteed configurations for creating a reprocessable capture session<br>({@code PRIV} input is guaranteed only if PRIVATE reprocessing is supported. {@code YUV} input is always guaranteed.</th></tr> + * <tr><th colspan="2" id="rb">Input</th><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th colspan="2" id="rb">Reprocess-only target</th><th rowspan="2">Sample use case(s)</th> </tr> + * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code 640x480}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td> <td></td><td id="rb"></td> <td>In-app viewfinder analysis with ZSL and RAW.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code 640x480}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td><td>In-app viewfinder analysis with ZSL, RAW, and JPEG reprocessing output.</td> </tr> + * </table><br> + * </p> + * * @param inputConfig The configuration for the input {@link Surface} * @param outputs The new set of Surfaces that should be made available as * targets for captured image data. diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index f61892ec6114..d58ad22396c6 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -964,23 +964,102 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>This camera device has only limited capabilities.</p> + * <p>This camera device does not have enough capabilities to qualify as a <code>FULL</code> device or + * better.</p> + * <p>Only the stream configurations listed in the <code>LEGACY</code> and <code>LIMITED</code> tables in the + * {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession} documentation are guaranteed to be supported.</p> + * <p>All <code>LIMITED</code> devices support the <code>BACKWARDS_COMPATIBLE</code> capability, indicating basic + * support for color image capture. The only exception is that the device may + * alternatively support only the <code>DEPTH_OUTPUT</code> capability, if it can only output depth + * measurements and not color images.</p> + * <p><code>LIMITED</code> devices and above require the use of {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger} + * to lock exposure metering (and calculate flash power, for cameras with flash) before + * capturing a high-quality still image.</p> + * <p>A <code>LIMITED</code> device that only lists the <code>BACKWARDS_COMPATIBLE</code> capability is only + * required to support full-automatic operation and post-processing (<code>OFF</code> is not + * supported for {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode}, {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode}, or + * {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode})</p> + * <p>Additional capabilities may optionally be supported by a <code>LIMITED</code>-level device, and + * can be checked for in {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}.</p> + * + * @see CaptureRequest#CONTROL_AE_MODE + * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER + * @see CaptureRequest#CONTROL_AF_MODE + * @see CaptureRequest#CONTROL_AWB_MODE + * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL */ public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; /** * <p>This camera device is capable of supporting advanced imaging applications.</p> + * <p>The stream configurations listed in the <code>FULL</code>, <code>LEGACY</code> and <code>LIMITED</code> tables in the + * {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession} documentation are guaranteed to be supported.</p> + * <p>A <code>FULL</code> device will support below capabilities:</p> + * <ul> + * <li><code>BURST_CAPTURE</code> capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains + * <code>BURST_CAPTURE</code>)</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 <code>MANUAL_SENSOR</code>)</li> + * <li>Manual post-processing control ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains + * <code>MANUAL_POST_PROCESSING</code>)</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> + * <p>Note: + * Pre-API level 23, FULL devices also supported arbitrary cropping region + * ({@link CameraCharacteristics#SCALER_CROPPING_TYPE android.scaler.croppingType} <code>== FREEFORM</code>); this requirement was relaxed in API level + * 23, and <code>FULL</code> devices may only support <code>CENTERED</code> cropping.</p> + * + * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES + * @see CameraCharacteristics#SCALER_CROPPING_TYPE + * @see CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE + * @see CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION + * @see CameraCharacteristics#SYNC_MAX_LATENCY * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL */ public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; /** * <p>This camera device is running in backward compatibility mode.</p> + * <p>Only the stream configurations listed in the <code>LEGACY</code> table in the {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession} + * documentation are supported.</p> + * <p>A <code>LEGACY</code> device does not support per-frame control, manual sensor control, manual + * post-processing, arbitrary cropping regions, and has relaxed performance constraints. + * No additional capabilities beyond <code>BACKWARD_COMPATIBLE</code> will ever be listed by a + * <code>LEGACY</code> device in {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}.</p> + * <p>In addition, the {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger} is not functional on <code>LEGACY</code> + * devices. Instead, every request that includes a JPEG-format output target is treated + * as triggering a still capture, internally executing a precapture trigger. This may + * fire the flash for flash power metering during precapture, and then fire the flash + * for the final capture, if a flash is available on the device and the AE mode is set to + * enable the flash.</p> + * + * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER + * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL */ public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; + /** + * <p>This camera device is capable of YUV reprocessing and RAW data capture, in addition to + * FULL-level capabilities.</p> + * <p>The stream configurations listed in the <code>LEVEL_3</code>, <code>RAW</code>, <code>FULL</code>, <code>LEGACY</code> and + * <code>LIMITED</code> tables in the {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession} + * documentation are guaranteed to be supported.</p> + * <p>The following additional capabilities are guaranteed to be supported:</p> + * <ul> + * <li><code>YUV_REPROCESSING</code> capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains + * <code>YUV_REPROCESSING</code>)</li> + * <li><code>RAW</code> capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains + * <code>RAW</code>)</li> + * </ul> + * + * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES + * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL + */ + public static final int INFO_SUPPORTED_HARDWARE_LEVEL_3 = 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 c3cae65d378e..79eff4ff431e 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -1599,13 +1599,13 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * <p>This key is optional. Applications can assume there is no boost applied * after RAW is captured if this key is not available. * When this key is available, the sensitivity boost value must be within - * {@link CaptureRequest#CONTROL_POST_RAW_SENSITIVITY_BOOST android.control.postRawSensitivityBoost}.</p> + * {@link CameraCharacteristics#CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE android.control.postRawSensitivityBoostRange}.</p> * <p>If the camera device cannot apply the exact boost requested, it will reduce the * boost to the nearest supported value. * The final boost value used will be available in the output capture result.</p> * <p>For devices that support post RAW sensitivity boost, the YUV/JPEG output images * of such device will have the total sensitivity of - * <code>{@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} * android.control.ispSensitivity / 100</code> + * <code>{@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} * {@link CaptureRequest#CONTROL_POST_RAW_SENSITIVITY_BOOST android.control.postRawSensitivityBoost} / 100</code> * The sensitivity of RAW format images will always be <code>{@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}</code></p> * <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to * OFF; otherwise the auto-exposure algorithm will override this value.</p> diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 7b9d1a3c832e..5748726b4cad 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -2106,13 +2106,13 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p>This key is optional. Applications can assume there is no boost applied * after RAW is captured if this key is not available. * When this key is available, the sensitivity boost value must be within - * {@link CaptureRequest#CONTROL_POST_RAW_SENSITIVITY_BOOST android.control.postRawSensitivityBoost}.</p> + * {@link CameraCharacteristics#CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE android.control.postRawSensitivityBoostRange}.</p> * <p>If the camera device cannot apply the exact boost requested, it will reduce the * boost to the nearest supported value. * The final boost value used will be available in the output capture result.</p> * <p>For devices that support post RAW sensitivity boost, the YUV/JPEG output images * of such device will have the total sensitivity of - * <code>{@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} * android.control.ispSensitivity / 100</code> + * <code>{@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} * {@link CaptureRequest#CONTROL_POST_RAW_SENSITIVITY_BOOST android.control.postRawSensitivityBoost} / 100</code> * The sensitivity of RAW format images will always be <code>{@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}</code></p> * <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to * OFF; otherwise the auto-exposure algorithm will override this value.</p> diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 04caa8fcda38..c96c9be02a3d 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -496,7 +496,10 @@ public class FingerprintManager { */ @RequiresPermission(MANAGE_FINGERPRINT) public void enroll(byte [] token, CancellationSignal cancel, int flags, - EnrollmentCallback callback) { + EnrollmentCallback callback, int userId) { + if (userId == UserHandle.USER_CURRENT) { + userId = getCurrentUserId(); + } if (callback == null) { throw new IllegalArgumentException("Must supply an enrollment callback"); } @@ -512,7 +515,7 @@ public class FingerprintManager { if (mService != null) try { mEnrollmentCallback = callback; - mService.enroll(mToken, token, getCurrentUserId(), mServiceReceiver, flags); + mService.enroll(mToken, token, userId, mServiceReceiver, flags); } catch (RemoteException e) { Log.w(TAG, "Remote exception in enroll: ", e); if (callback != null) { @@ -556,6 +559,21 @@ public class FingerprintManager { } /** + * Sets the active user. This is meant to be used to select the current profile for enrollment + * to allow separate enrolled fingers for a work profile + * @param userId + * @hide + */ + @RequiresPermission(MANAGE_FINGERPRINT) + public void setActiveUser(int userId) { + if (mService != null) try { + mService.setActiveUser(userId); + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in setActiveUser: ", e); + } + } + + /** * Remove given fingerprint template from fingerprint hardware and/or protected storage. * @param fp the fingerprint item to remove * @param callback an optional callback to verify that fingerprint templates have been diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index 690a7519a3b5..43d5577f3b71 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -75,4 +75,7 @@ interface IFingerprintService { // Add a callback which gets notified when the fingerprint lockout period expired. void addLockoutResetCallback(IFingerprintServiceLockoutResetCallback callback); + + // Explicitly set the active user (for enrolling work profile) + void setActiveUser(int uid); } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 7cb086fa717b..92c721bdb017 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -1611,7 +1611,7 @@ public class ConnectivityManager { if (b != null) { try { ITelephony it = ITelephony.Stub.asInterface(b); - int subId = SubscriptionManager.getDefaultDataSubId(); + int subId = SubscriptionManager.getDefaultDataSubscriptionId(); Log.d("ConnectivityManager", "getMobileDataEnabled()+ subId=" + subId); boolean retVal = it.getDataEnabled(subId); Log.d("ConnectivityManager", "getMobileDataEnabled()- subId=" + subId diff --git a/core/java/android/print/PrintJob.java b/core/java/android/print/PrintJob.java index 777baabd955a..66181e0f18fa 100644 --- a/core/java/android/print/PrintJob.java +++ b/core/java/android/print/PrintJob.java @@ -19,6 +19,8 @@ package android.print; import android.annotation.NonNull; import android.annotation.Nullable; +import java.util.Objects; + /** * This class represents a print job from the perspective of an * application. It contains behavior methods for performing operations @@ -30,11 +32,11 @@ import android.annotation.Nullable; */ public final class PrintJob { - private final PrintManager mPrintManager; + private final @NonNull PrintManager mPrintManager; - private PrintJobInfo mCachedInfo; + private @NonNull PrintJobInfo mCachedInfo; - PrintJob(PrintJobInfo info, PrintManager printManager) { + PrintJob(@NonNull PrintJobInfo info, @NonNull PrintManager printManager) { mCachedInfo = info; mPrintManager = printManager; } @@ -44,7 +46,7 @@ public final class PrintJob { * * @return The id. */ - public @NonNull PrintJobId getId() { + public @Nullable PrintJobId getId() { return mCachedInfo.getId(); } @@ -58,7 +60,7 @@ public final class PrintJob { * * @return The print job info. */ - public @Nullable PrintJobInfo getInfo() { + public @NonNull PrintJobInfo getInfo() { if (isInImmutableState()) { return mCachedInfo; } @@ -193,11 +195,17 @@ public final class PrintJob { return false; } PrintJob other = (PrintJob) obj; - return mCachedInfo.getId().equals(other.mCachedInfo.getId()); + return Objects.equals(mCachedInfo.getId(), other.mCachedInfo.getId()); } @Override public int hashCode() { - return mCachedInfo.getId().hashCode(); + PrintJobId printJobId = mCachedInfo.getId(); + + if (printJobId == null) { + return 0; + } else { + return printJobId.hashCode(); + } } } diff --git a/core/java/android/print/PrintJobId.java b/core/java/android/print/PrintJobId.java index a2ee02b7bc49..186ae9b28dae 100644 --- a/core/java/android/print/PrintJobId.java +++ b/core/java/android/print/PrintJobId.java @@ -19,7 +19,8 @@ package android.print; import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; -import android.text.TextUtils; + +import com.android.internal.util.Preconditions; import java.util.UUID; @@ -27,7 +28,7 @@ import java.util.UUID; * This class represents the id of a print job. */ public final class PrintJobId implements Parcelable { - private final String mValue; + private final @NonNull String mValue; /** * Creates a new instance. @@ -45,7 +46,7 @@ public final class PrintJobId implements Parcelable { * * @hide */ - public PrintJobId(String value) { + public PrintJobId(@NonNull String value) { mValue = value; } @@ -53,7 +54,7 @@ public final class PrintJobId implements Parcelable { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((mValue != null) ? mValue.hashCode() : 0); + result = prime * result + mValue.hashCode(); return result; } @@ -69,7 +70,7 @@ public final class PrintJobId implements Parcelable { return false; } PrintJobId other = (PrintJobId) obj; - if (!TextUtils.equals(mValue, other.mValue)) { + if (!mValue.equals(other.mValue)) { return false; } return true; @@ -104,7 +105,7 @@ public final class PrintJobId implements Parcelable { * * @hide */ - public static PrintJobId unflattenFromString(String string) { + public static @NonNull PrintJobId unflattenFromString(@NonNull String string) { return new PrintJobId(string); } @@ -112,7 +113,7 @@ public final class PrintJobId implements Parcelable { new Parcelable.Creator<PrintJobId>() { @Override public PrintJobId createFromParcel(Parcel parcel) { - return new PrintJobId(parcel.readString()); + return new PrintJobId(Preconditions.checkNotNull(parcel.readString())); } @Override diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java index 21836b3a6109..7e3a72feb2f0 100644 --- a/core/java/android/print/PrintJobInfo.java +++ b/core/java/android/print/PrintJobInfo.java @@ -244,7 +244,7 @@ public final class PrintJobInfo implements Parcelable { * * @return The id. */ - public @NonNull PrintJobId getId() { + public @Nullable PrintJobId getId() { return mId; } diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java index 3eb487461e5c..58f260cbe1cf 100644 --- a/core/java/android/print/PrintManager.java +++ b/core/java/android/print/PrintManager.java @@ -535,7 +535,10 @@ public final class PrintManager { return new PrinterDiscoverySession(mService, mContext, mUserId); } - private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub + /** + * @hide + */ + public static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub implements ActivityLifecycleCallbacks { private final Object mLock = new Object(); @@ -1061,7 +1064,10 @@ public final class PrintManager { } } - private static final class PrintJobStateChangeListenerWrapper extends + /** + * @hide + */ + public static final class PrintJobStateChangeListenerWrapper extends IPrintJobStateChangeListener.Stub { private final WeakReference<PrintJobStateChangeListener> mWeakListener; private final WeakReference<Handler> mWeakHandler; diff --git a/core/java/android/print/PrinterDiscoverySession.java b/core/java/android/print/PrinterDiscoverySession.java index abb441b79bc1..c587edde78f7 100644 --- a/core/java/android/print/PrinterDiscoverySession.java +++ b/core/java/android/print/PrinterDiscoverySession.java @@ -16,6 +16,8 @@ package android.print; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.content.pm.ParceledListSlice; import android.os.Handler; @@ -72,7 +74,7 @@ public final class PrinterDiscoverySession { } } - public final void startPrinterDiscovery(List<PrinterId> priorityList) { + public final void startPrinterDiscovery(@Nullable List<PrinterId> priorityList) { if (isDestroyed()) { Log.w(LOG_TAG, "Ignoring start printers discovery - session destroyed"); return; @@ -102,7 +104,7 @@ public final class PrinterDiscoverySession { } } - public final void startPrinterStateTracking(PrinterId printerId) { + public final void startPrinterStateTracking(@NonNull PrinterId printerId) { if (isDestroyed()) { Log.w(LOG_TAG, "Ignoring start printer state tracking - session destroyed"); return; @@ -114,7 +116,7 @@ public final class PrinterDiscoverySession { } } - public final void stopPrinterStateTracking(PrinterId printerId) { + public final void stopPrinterStateTracking(@NonNull PrinterId printerId) { if (isDestroyed()) { Log.w(LOG_TAG, "Ignoring stop printer state tracking - session destroyed"); return; @@ -285,7 +287,7 @@ public final class PrinterDiscoverySession { } } - private static final class PrinterDiscoveryObserver extends IPrinterDiscoveryObserver.Stub { + public static final class PrinterDiscoveryObserver extends IPrinterDiscoveryObserver.Stub { private final WeakReference<PrinterDiscoverySession> mWeakSession; diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java index 310449222a0b..62d214e9e43c 100644 --- a/core/java/android/printservice/PrintService.java +++ b/core/java/android/printservice/PrintService.java @@ -16,6 +16,7 @@ package android.printservice; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Service; import android.content.ComponentName; @@ -346,7 +347,7 @@ public abstract class PrintService extends Service { * @param localId A locally unique id in the context of your print service. * @return Global printer id. */ - public final PrinterId generatePrinterId(String localId) { + public @NonNull final PrinterId generatePrinterId(String localId) { throwIfNotCalledOnMainThread(); localId = Preconditions.checkNotNull(localId, "localId cannot be null"); return new PrinterId(new ComponentName(getPackageName(), diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index b5474329e3e5..330fcf6c4c74 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -8401,10 +8401,15 @@ public final class ContactsContract { * @hide */ public static Intent rebuildManagedQuickContactsIntent(String lookupKey, long contactId, - long directoryId, Intent originalIntent) { + boolean isContactIdIgnored, long directoryId, Intent originalIntent) { final Intent intent = new Intent(ACTION_QUICK_CONTACT); // Rebuild the URI from a lookup key and a contact ID. - Uri uri = Contacts.getLookupUri(contactId, lookupKey); + Uri uri = null; + if (!TextUtils.isEmpty(lookupKey)) { + uri = isContactIdIgnored + ? Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey) + : Contacts.getLookupUri(contactId, lookupKey); + } if (uri != null && directoryId != Directory.DEFAULT) { uri = uri.buildUpon().appendQueryParameter( ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId)).build(); @@ -8699,6 +8704,120 @@ public final class ContactsContract { "com.android.contacts.action.SHOW_OR_CREATE_CONTACT"; /** + * Activity Action: Initiate a message to someone by voice. The message could be text, + * audio, video or image(photo). This action supports messaging with a specific contact + * regardless of the underlying messaging protocol used. + * <p> + * The action could be originated from the Voice Assistant as a voice interaction. In such + * case, a receiving activity that supports {@link android.content.Intent#CATEGORY_VOICE} + * could check return value of {@link android.app.Activity#isVoiceInteractionRoot} before + * proceeding. By doing this check the activity verifies that the action indeed was + * initiated by Voice Assistant and could send a message right away, without any further + * input from the user. This allows for a smooth user experience when sending a message by + * voice. Note: this activity must also support the {@link + * android.content.Intent#CATEGORY_DEFAULT} so it can be found by {@link + * android.service.voice.VoiceInteractionSession#startVoiceActivity}. + * <p> + * When the action was not initiated by Voice Assistant or when the receiving activity does + * not support {@link android.content.Intent#CATEGORY_VOICE}, the activity should confirm + * with the user before sending the message (because in this case it is unknown which app + * sent the intent, it could be malicious). + * <p> + * To allow the Voice Assistant to help users with contacts disambiguation, the messaging + * app may choose to integrate with the Contacts Provider. The following convention should + * be met when creating Data table for such integration: + * <ul> + * <li>Column {@link DataColumns#DATA1} should store the unique contact ID as understood by + * the app. This value will be used in the {@link #EXTRA_RECIPIENT_CONTACT_CHAT_ID}.</li> + * <li>Optionally, column {@link DataColumns#DATA3} could store a human readable label for + * the ID. For example it could be phone number or human readable username/user_id like + * "a_super_cool_user_name". This label may be shown below the Contact Name by the Voice + * Assistant as the user completes the voice action. If DATA3 is empty, the ID in DATA1 may + * be shown instead.</li> + * <li><em>Note: Do not use DATA3 to store the Contact Name. The Voice Assistant will + * already get the Contact Name from the RawContact’s display_name.</em></li> + * <li><em>Note: Some apps may choose to use phone number as the unique contact ID in DATA1. + * If this applies to you and you’d like phone number to be shown below the Contact Name by + * the Voice Assistant, then you may choose to leave DATA3 empty.</em></li> + * </ul> + * <p> + * Input: {@link android.content.Intent#getType} is the MIME type of the data being sent. + * The intent sender will always put the concrete mime type in the intent type, like + * "text/plain" or "audio/wav" for example. If the MIME type is "text/plain", message to + * sent will be provided via {@link android.content.Intent#EXTRA_TEXT} as a styled + * CharSequence. Otherwise, the message content will be supplied through {@link + * android.content.Intent#setClipData(ClipData)} as a content provider URI(s). In the latter + * case, EXTRA_TEXT could still be supplied optionally; for example, for audio messages + * ClipData will contain URI of a recording and EXTRA_TEXT could contain the text + * transcription of this recording. + * <p> + * The message can have n recipients. The n-th recipient of the message will be provided as + * n-th elements of {@link #EXTRA_RECIPIENT_CONTACT_URI}, {@link + * #EXTRA_RECIPIENT_CONTACT_CHAT_ID} and {@link #EXTRA_RECIPIENT_CONTACT_NAME} (as a + * consequence, EXTRA_RECIPIENT_CONTACT_URI, EXTRA_RECIPIENT_CONTACT_CHAT_ID and + * EXTRA_RECIPIENT_CONTACT_NAME should all be of length n). If neither of these 3 elements + * is provided (e.g. all 3 are null) for the recipient or if the information provided is + * ambiguous then the activity should prompt the user for the recipient to send the message + * to. + * <p> + * Output: nothing + * + * @see #EXTRA_RECIPIENT_CONTACT_URI + * @see #EXTRA_RECIPIENT_CONTACT_CHAT_ID + * @see #EXTRA_RECIPIENT_CONTACT_NAME + */ + public static final String ACTION_VOICE_SEND_MESSAGE_TO_CONTACTS = + "android.provider.action.VOICE_SEND_MESSAGE_TO_CONTACTS"; + + /** + * This extra specifies a content provider uri(s) for the contact(s) (if the contacts were + * located in the Contacts Provider), used with {@link + * #ACTION_VOICE_SEND_MESSAGE_TO_CONTACTS} to supply the recipient(s). The value of this + * extra is a {@code String[]}. The number of elements in the array should be equal to + * number of recipients (and consistent with {@link #EXTRA_RECIPIENT_CONTACT_CHAT_ID} and + * {@link #EXTRA_RECIPIENT_CONTACT_NAME}). When the value of the element for the particular + * recipient is absent, it will be set to null. + * <p> + * <em>Note: one contact may have multiple accounts (e.g. Chat IDs) on a specific messaging + * platform, so this may be ambiguous. E.g., one contact “John Smith” could have two + * accounts on the same messaging app.</em> + * <p> + * <em>Example value: {"content://com.android.contacts/contacts/16"}</em> + */ + public static final String EXTRA_RECIPIENT_CONTACT_URI = + "android.provider.extra.RECIPIENT_CONTACT_URI"; + + /** + * This extra specifies a messaging app’s unique ID(s) for the contact(s), used with {@link + * #ACTION_VOICE_SEND_MESSAGE_TO_CONTACTS} to supply the recipient(s). The value of this + * extra is a {@code String[]}. The number of elements in the array should be equal to + * number of recipients (and consistent with {@link #EXTRA_RECIPIENT_CONTACT_URI} and {@link + * #EXTRA_RECIPIENT_CONTACT_NAME}). When the value of the element for the particular + * recipient is absent, it will be set to null. + * <p> + * The value of the elements comes from the {@link DataColumns#DATA1} column in Contacts + * Provider, and should be the unambiguous contact endpoint. This value is app-specific, it + * could be a phone number or some proprietary ID. + */ + public static final String EXTRA_RECIPIENT_CONTACT_CHAT_ID = + "android.provider.extra.RECIPIENT_CONTACT_CHAT_ID"; + + /** + * This extra specifies the contact name (full name from the Contacts Provider), used with + * {@link #ACTION_VOICE_SEND_MESSAGE_TO_CONTACTS} to supply the recipient. The value of this + * extra is a {@code String[]}. The number of elements in the array should be equal to + * number of recipients (and consistent with {@link #EXTRA_RECIPIENT_CONTACT_URI} and {@link + * #EXTRA_RECIPIENT_CONTACT_CHAT_ID}). When the value of the element for the particular + * recipient is absent, it will be set to null. + * <p> + * The value of the elements comes from RawContact's display_name column. + * <p> + * <em>Example value: {"Jane Doe"}</em> + */ + public static final String EXTRA_RECIPIENT_CONTACT_NAME = + "android.provider.extra.RECIPIENT_CONTACT_NAME"; + + /** * Starts an Activity that lets the user select the multiple phones from a * list of phone numbers which come from the contacts or * {@link #EXTRA_PHONE_URIS}. diff --git a/core/java/android/provider/ContactsInternal.java b/core/java/android/provider/ContactsInternal.java index 36ef52d6c39b..2cd1d48e7dc1 100644 --- a/core/java/android/provider/ContactsInternal.java +++ b/core/java/android/provider/ContactsInternal.java @@ -42,10 +42,12 @@ public class ContactsInternal { private static final UriMatcher sContactsUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); private static final int CONTACTS_URI_LOOKUP_ID = 1000; + private static final int CONTACTS_URI_LOOKUP = 1001; static { // Contacts URI matching table final UriMatcher matcher = sContactsUriMatcher; + matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_URI_LOOKUP); matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_URI_LOOKUP_ID); } @@ -57,6 +59,7 @@ public class ContactsInternal { final int match = sContactsUriMatcher.match(uri); switch (match) { + case CONTACTS_URI_LOOKUP: case CONTACTS_URI_LOOKUP_ID: { if (maybeStartManagedQuickContact(context, intent)) { return; // Request handled by DPM. Just return here. @@ -89,7 +92,10 @@ public class ContactsInternal { // Decompose into an ID and a lookup key. final List<String> pathSegments = uri.getPathSegments(); - final long contactId = ContentUris.parseId(uri); + final boolean isContactIdIgnored = pathSegments.size() < 4; + final long contactId = isContactIdIgnored + ? ContactsContract.Contacts.ENTERPRISE_CONTACT_ID_BASE //contact id will be ignored + : ContentUris.parseId(uri); final String lookupKey = pathSegments.get(2); final String directoryIdStr = uri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY); final long directoryId = (directoryIdStr == null) @@ -119,8 +125,8 @@ public class ContactsInternal { final long actualDirectoryId = (directoryId - ContactsContract.Directory.ENTERPRISE_DIRECTORY_ID_BASE); - dpm.startManagedQuickContact(actualLookupKey, actualContactId, actualDirectoryId, - originalIntent); + dpm.startManagedQuickContact(actualLookupKey, actualContactId, isContactIdIgnored, + actualDirectoryId, originalIntent); return true; } } diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 88cc8a2ae21a..cdd88f60244f 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -342,8 +342,8 @@ public final class DocumentsContract { * within the same document provider. * * @see #COLUMN_FLAGS - * @see DocumentsContract#moveDocument(ContentProviderClient, Uri, Uri) - * @see DocumentsProvider#moveDocument(String, String) + * @see DocumentsContract#moveDocument(ContentProviderClient, Uri, Uri, Uri) + * @see DocumentsProvider#moveDocument(String, String, String) */ public static final int FLAG_SUPPORTS_MOVE = 1 << 8; @@ -614,6 +614,8 @@ public final class DocumentsContract { public static final String METHOD_IS_CHILD_DOCUMENT = "android:isChildDocument"; /** {@hide} */ + public static final String EXTRA_PARENT_URI = "parentUri"; + /** {@hide} */ public static final String EXTRA_URI = "uri"; private static final String PATH_ROOT = "root"; @@ -1139,7 +1141,6 @@ public final class DocumentsContract { * @param targetParentDocumentUri document which will become a parent of the source * document's copy. * @return the copied document, or {@code null} if failed. - * @hide */ public static Uri copyDocument(ContentResolver resolver, Uri sourceDocumentUri, Uri targetParentDocumentUri) { @@ -1170,17 +1171,18 @@ public final class DocumentsContract { * Moves the given document under a new parent. * * @param sourceDocumentUri document with {@link Document#FLAG_SUPPORTS_MOVE} + * @param sourceParentDocumentUri parent document of the document to move. * @param targetParentDocumentUri document which will become a new parent of the source * document. * @return the moved document, or {@code null} if failed. - * @hide */ public static Uri moveDocument(ContentResolver resolver, Uri sourceDocumentUri, - Uri targetParentDocumentUri) { + Uri sourceParentDocumentUri, Uri targetParentDocumentUri) { final ContentProviderClient client = resolver.acquireUnstableContentProviderClient( sourceDocumentUri.getAuthority()); try { - return moveDocument(client, sourceDocumentUri, targetParentDocumentUri); + return moveDocument(client, sourceParentDocumentUri, sourceDocumentUri, + targetParentDocumentUri); } catch (Exception e) { Log.w(TAG, "Failed to move document", e); return null; @@ -1191,9 +1193,10 @@ public final class DocumentsContract { /** {@hide} */ public static Uri moveDocument(ContentProviderClient client, Uri sourceDocumentUri, - Uri targetParentDocumentUri) throws RemoteException { + Uri sourceParentDocumentUri, Uri targetParentDocumentUri) throws RemoteException { final Bundle in = new Bundle(); in.putParcelable(DocumentsContract.EXTRA_URI, sourceDocumentUri); + in.putParcelable(DocumentsContract.EXTRA_PARENT_URI, sourceParentDocumentUri); in.putParcelable(DocumentsContract.EXTRA_TARGET_URI, targetParentDocumentUri); final Bundle out = client.call(METHOD_MOVE_DOCUMENT, null, in); diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java index 94b41575d0a3..bae928d1755d 100644 --- a/core/java/android/provider/DocumentsProvider.java +++ b/core/java/android/provider/DocumentsProvider.java @@ -289,12 +289,14 @@ public abstract class DocumentsProvider extends ContentProvider { * be returned. * * @param sourceDocumentId the document to move. + * @param sourceParentDocumentId the parent of the document to move. * @param targetParentDocumentId the target document to be a new parent of the * source document. * @hide */ @SuppressWarnings("unused") - public String moveDocument(String sourceDocumentId, String targetParentDocumentId) + public String moveDocument(String sourceDocumentId, String sourceParentDocumentId, + String targetParentDocumentId) throws FileNotFoundException { throw new UnsupportedOperationException("Move not supported"); } @@ -759,7 +761,7 @@ public abstract class DocumentsProvider extends ContentProvider { out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri); - // Original document no longer exists, clean up any grants + // Original document no longer exists, clean up any grants. revokeDocumentPermission(documentId); } @@ -767,7 +769,7 @@ public abstract class DocumentsProvider extends ContentProvider { enforceWritePermissionInner(documentUri, getCallingPackage(), null); deleteDocument(documentId); - // Document no longer exists, clean up any grants + // Document no longer exists, clean up any grants. revokeDocumentPermission(documentId); } else if (METHOD_COPY_DOCUMENT.equals(method)) { @@ -793,13 +795,16 @@ public abstract class DocumentsProvider extends ContentProvider { } } else if (METHOD_MOVE_DOCUMENT.equals(method)) { + final Uri parentSourceUri = extras.getParcelable(DocumentsContract.EXTRA_PARENT_URI); + final String parentSourceId = DocumentsContract.getDocumentId(parentSourceUri); final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI); final String targetId = DocumentsContract.getDocumentId(targetUri); - enforceReadPermissionInner(documentUri, getCallingPackage(), null); + enforceWritePermissionInner(documentUri, getCallingPackage(), null); + enforceReadPermissionInner(parentSourceUri, getCallingPackage(), null); enforceWritePermissionInner(targetUri, getCallingPackage(), null); - final String newDocumentId = moveDocument(documentId, targetId); + final String newDocumentId = moveDocument(documentId, parentSourceId, targetId); if (newDocumentId != null) { final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri, @@ -814,7 +819,7 @@ public abstract class DocumentsProvider extends ContentProvider { out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri); } - // Original document no longer exists, clean up any grants + // Original document no longer exists, clean up any grants. revokeDocumentPermission(documentId); } else { diff --git a/core/java/android/security/net/config/CertificateSource.java b/core/java/android/security/net/config/CertificateSource.java index 7e3601e1fbea..f3272e478665 100644 --- a/core/java/android/security/net/config/CertificateSource.java +++ b/core/java/android/security/net/config/CertificateSource.java @@ -16,12 +16,13 @@ package android.security.net.config; -import java.util.Set; import java.security.cert.X509Certificate; +import java.util.Set; /** @hide */ public interface CertificateSource { Set<X509Certificate> getCertificates(); X509Certificate findBySubjectAndPublicKey(X509Certificate cert); X509Certificate findByIssuerAndSignature(X509Certificate cert); + Set<X509Certificate> findAllByIssuerAndSignature(X509Certificate cert); } diff --git a/core/java/android/security/net/config/CertificatesEntryRef.java b/core/java/android/security/net/config/CertificatesEntryRef.java index ff728efc2d93..742d430a4e78 100644 --- a/core/java/android/security/net/config/CertificatesEntryRef.java +++ b/core/java/android/security/net/config/CertificatesEntryRef.java @@ -17,8 +17,8 @@ package android.security.net.config; import android.util.ArraySet; -import java.util.Set; import java.security.cert.X509Certificate; +import java.util.Set; /** @hide */ public final class CertificatesEntryRef { @@ -60,4 +60,8 @@ public final class CertificatesEntryRef { return new TrustAnchor(foundCert, mOverridesPins); } + + public Set<X509Certificate> findAllCertificatesByIssuerAndSignature(X509Certificate cert) { + return mSource.findAllByIssuerAndSignature(cert); + } } diff --git a/core/java/android/security/net/config/DirectoryCertificateSource.java b/core/java/android/security/net/config/DirectoryCertificateSource.java index bf29efabb00a..b2c068cb4516 100644 --- a/core/java/android/security/net/config/DirectoryCertificateSource.java +++ b/core/java/android/security/net/config/DirectoryCertificateSource.java @@ -29,6 +29,7 @@ import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.util.Collections; import java.util.Set; import libcore.io.IoUtils; @@ -110,10 +111,50 @@ abstract class DirectoryCertificateSource implements CertificateSource { }); } + @Override + public Set<X509Certificate> findAllByIssuerAndSignature(final X509Certificate cert) { + return findCerts(cert.getIssuerX500Principal(), new CertSelector() { + @Override + public boolean match(X509Certificate ca) { + try { + cert.verify(ca.getPublicKey()); + return true; + } catch (Exception e) { + return false; + } + } + }); + } + private static interface CertSelector { boolean match(X509Certificate cert); } + private Set<X509Certificate> findCerts(X500Principal subj, CertSelector selector) { + String hash = getHash(subj); + Set<X509Certificate> certs = null; + for (int index = 0; index >= 0; index++) { + String fileName = hash + "." + index; + if (!new File(mDir, fileName).exists()) { + break; + } + if (isCertMarkedAsRemoved(fileName)) { + continue; + } + X509Certificate cert = readCertificate(fileName); + if (!subj.equals(cert.getSubjectX500Principal())) { + continue; + } + if (selector.match(cert)) { + if (certs == null) { + certs = new ArraySet<X509Certificate>(); + } + certs.add(cert); + } + } + return certs != null ? certs : Collections.<X509Certificate>emptySet(); + } + private X509Certificate findCert(X500Principal subj, CertSelector selector) { String hash = getHash(subj); for (int index = 0; index >= 0; index++) { diff --git a/core/java/android/security/net/config/KeyStoreCertificateSource.java b/core/java/android/security/net/config/KeyStoreCertificateSource.java index b6105cd5da4c..ba5dd8396024 100644 --- a/core/java/android/security/net/config/KeyStoreCertificateSource.java +++ b/core/java/android/security/net/config/KeyStoreCertificateSource.java @@ -21,6 +21,7 @@ import java.security.KeyStore; import java.security.KeyStoreException; import java.security.cert.Certificate; import java.security.cert.X509Certificate; +import java.util.Collections; import java.util.Enumeration; import java.util.Set; @@ -90,4 +91,18 @@ class KeyStoreCertificateSource implements CertificateSource { } return anchor.getTrustedCert(); } + + @Override + public Set<X509Certificate> findAllByIssuerAndSignature(X509Certificate cert) { + ensureInitialized(); + Set<java.security.cert.TrustAnchor> anchors = mIndex.findAllByIssuerAndSignature(cert); + if (anchors.isEmpty()) { + return Collections.<X509Certificate>emptySet(); + } + Set<X509Certificate> certs = new ArraySet<X509Certificate>(anchors.size()); + for (java.security.cert.TrustAnchor anchor : anchors) { + certs.add(anchor.getTrustedCert()); + } + return certs; + } } diff --git a/core/java/android/security/net/config/NetworkSecurityConfig.java b/core/java/android/security/net/config/NetworkSecurityConfig.java index 0a2edff2295f..ebe14691464c 100644 --- a/core/java/android/security/net/config/NetworkSecurityConfig.java +++ b/core/java/android/security/net/config/NetworkSecurityConfig.java @@ -145,6 +145,15 @@ public final class NetworkSecurityConfig { return null; } + /** @hide */ + public Set<X509Certificate> findAllCertificatesByIssuerAndSignature(X509Certificate cert) { + Set<X509Certificate> certs = new ArraySet<X509Certificate>(); + for (CertificatesEntryRef ref : mCertificatesEntryRefs) { + certs.addAll(ref.findAllCertificatesByIssuerAndSignature(cert)); + } + return certs; + } + /** * Return a {@link Builder} for the default {@code NetworkSecurityConfig}. * diff --git a/core/java/android/security/net/config/ResourceCertificateSource.java b/core/java/android/security/net/config/ResourceCertificateSource.java index e489c2c529eb..8803c4b2559c 100644 --- a/core/java/android/security/net/config/ResourceCertificateSource.java +++ b/core/java/android/security/net/config/ResourceCertificateSource.java @@ -25,6 +25,7 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.Collection; +import java.util.Collections; import java.util.Set; import com.android.org.conscrypt.TrustedCertificateIndex; @@ -100,4 +101,18 @@ public class ResourceCertificateSource implements CertificateSource { } return anchor.getTrustedCert(); } + + @Override + public Set<X509Certificate> findAllByIssuerAndSignature(X509Certificate cert) { + ensureInitialized(); + Set<java.security.cert.TrustAnchor> anchors = mIndex.findAllByIssuerAndSignature(cert); + if (anchors.isEmpty()) { + return Collections.<X509Certificate>emptySet(); + } + Set<X509Certificate> certs = new ArraySet<X509Certificate>(anchors.size()); + for (java.security.cert.TrustAnchor anchor : anchors) { + certs.add(anchor.getTrustedCert()); + } + return certs; + } } diff --git a/core/java/android/security/net/config/TrustedCertificateStoreAdapter.java b/core/java/android/security/net/config/TrustedCertificateStoreAdapter.java index 4a90f8223ad7..c2f29bed1b27 100644 --- a/core/java/android/security/net/config/TrustedCertificateStoreAdapter.java +++ b/core/java/android/security/net/config/TrustedCertificateStoreAdapter.java @@ -42,6 +42,11 @@ public class TrustedCertificateStoreAdapter extends TrustedCertificateStore { } @Override + public Set<X509Certificate> findAllIssuers(X509Certificate cert) { + return mConfig.findAllCertificatesByIssuerAndSignature(cert); + } + + @Override public X509Certificate getTrustAnchor(X509Certificate cert) { TrustAnchor anchor = mConfig.findTrustAnchorBySubjectAndPublicKey(cert); if (anchor == null) { diff --git a/core/java/android/util/LocaleList.java b/core/java/android/util/LocaleList.java index 8a2d015a6f7b..90a20bc2ab24 100644 --- a/core/java/android/util/LocaleList.java +++ b/core/java/android/util/LocaleList.java @@ -26,6 +26,8 @@ import android.os.Parcelable; import com.android.internal.annotations.GuardedBy; +import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.Locale; @@ -317,55 +319,72 @@ public final class LocaleList implements Parcelable { return supportedScr.equals(desiredScr) ? 1 : 0; } + private int findFirstMatchIndex(Locale supportedLocale) { + for (int idx = 0; idx < mList.length; idx++) { + final int score = matchScore(supportedLocale, mList[idx]); + if (score > 0) { + return idx; + } + } + return Integer.MAX_VALUE; + } + private static final Locale EN_LATN = Locale.forLanguageTag("en-Latn"); - private Locale computeFirstMatch(String[] supportedLocales, boolean assumeEnglishIsSupported) { + private int computeFirstMatchIndex(Collection<String> supportedLocales, + boolean assumeEnglishIsSupported) { if (mList.length == 1) { // just one locale, perhaps the most common scenario - return mList[0]; + return 0; } if (mList.length == 0) { // empty locale list - return null; + return -1; } + int bestIndex = Integer.MAX_VALUE; - final int numSupportedLocales = - supportedLocales.length + (assumeEnglishIsSupported ? 1 : 0); - for (int i = 0; i < numSupportedLocales; i++) { - final Locale supportedLocale; - if (assumeEnglishIsSupported) { - // Try English first, so we can return early if it's in the LocaleList - supportedLocale = (i == 0) ? EN_LATN : Locale.forLanguageTag(supportedLocales[i-1]); - } else { - supportedLocale = Locale.forLanguageTag(supportedLocales[i]); + // Try English first, so we can return early if it's in the LocaleList + if (assumeEnglishIsSupported) { + final int idx = findFirstMatchIndex(EN_LATN); + if (idx == 0) { // We have a match on the first locale, which is good enough + return 0; + } else if (idx < bestIndex) { + bestIndex = idx; } + } + for (String languageTag : supportedLocales) { + final Locale supportedLocale = Locale.forLanguageTag(languageTag); // We expect the average length of locale lists used for locale resolution to be // smaller than three, so it's OK to do this as an O(mn) algorithm. - for (int idx = 0; idx < mList.length; idx++) { - final int score = matchScore(supportedLocale, mList[idx]); - if (score > 0) { - if (idx == 0) { // We have a match on the first locale, which is good enough - return mList[0]; - } else if (idx < bestIndex) { - bestIndex = idx; - } - } + final int idx = findFirstMatchIndex(supportedLocale); + if (idx == 0) { // We have a match on the first locale, which is good enough + return 0; + } else if (idx < bestIndex) { + bestIndex = idx; } } - if (bestIndex == Integer.MAX_VALUE) { // no match was found - return mList[0]; + if (bestIndex == Integer.MAX_VALUE) { + // no match was found, so we fall back to the first locale in the locale list + return 0; } else { - return mList[bestIndex]; + return bestIndex; } } + private Locale computeFirstMatch(Collection<String> supportedLocales, + boolean assumeEnglishIsSupported) { + int bestIndex = computeFirstMatchIndex(supportedLocales, assumeEnglishIsSupported); + return bestIndex == -1 ? null : mList[bestIndex]; + } + /** * Returns the first match in the locale list given an unordered array of supported locales - * in BCP47 format. + * in BCP 47 format. * * If the locale list is empty, null would be returned. */ @Nullable public Locale getFirstMatch(String[] supportedLocales) { - return computeFirstMatch(supportedLocales, false /* assume English is not supported */); + return computeFirstMatch(Arrays.asList(supportedLocales), + false /* assume English is not supported */); } /** @@ -374,11 +393,26 @@ public final class LocaleList implements Parcelable { */ @Nullable public Locale getFirstMatchWithEnglishSupported(String[] supportedLocales) { - return computeFirstMatch(supportedLocales, true /* assume English is supported */); + return computeFirstMatch(Arrays.asList(supportedLocales), + true /* assume English is supported */); + } + + /** + * {@hide} + */ + public int getFirstMatchIndexWithEnglishSupported(Collection<String> supportedLocales) { + return computeFirstMatchIndex(supportedLocales, true /* assume English is supported */); } /** - * Returns true if the array of locale tags only contains empty locales and pseudolocales. + * {@hide} + */ + public int getFirstMatchIndexWithEnglishSupported(String[] supportedLocales) { + return getFirstMatchIndexWithEnglishSupported(Arrays.asList(supportedLocales)); + } + + /** + * Returns true if the collection of locale tags only contains empty locales and pseudolocales. * Assumes that there is no repetition in the input. * {@hide} */ @@ -386,7 +420,7 @@ public final class LocaleList implements Parcelable { if (supportedLocales.length > NUM_PSEUDO_LOCALES + 1) { // This is for optimization. Since there's no repetition in the input, if we have more // than the number of pseudo-locales plus one for the empty string, it's guaranteed - // that we have some meaninful locale in the list, so the list is not "practically + // that we have some meaninful locale in the collection, so the list is not "practically // empty". return false; } @@ -405,6 +439,8 @@ public final class LocaleList implements Parcelable { @GuardedBy("sLock") private static LocaleList sDefaultLocaleList = null; @GuardedBy("sLock") + private static LocaleList sDefaultAdjustedLocaleList = null; + @GuardedBy("sLock") private static Locale sLastDefaultLocale = null; /** @@ -415,8 +451,8 @@ public final class LocaleList implements Parcelable { * secondary preference is. * * Note that the default LocaleList would change if Locale.setDefault() is called. This method - * takes that into account by always checking the output of Locale.getDefault() and adjusting - * the default LocaleList if needed. + * takes that into account by always checking the output of Locale.getDefault() and + * recalculating the default LocaleList if needed. */ @NonNull @Size(min=1) public static LocaleList getDefault() { @@ -426,7 +462,7 @@ public final class LocaleList implements Parcelable { sLastDefaultLocale = defaultLocale; // It's either the first time someone has asked for the default locale list, or // someone has called Locale.setDefault() since we last set or adjusted the default - // locale list. So let's adjust the locale list. + // locale list. So let's recalculate the locale list. if (sDefaultLocaleList != null && defaultLocale.equals(sDefaultLocaleList.getPrimary())) { // The default Locale has changed, but it happens to be the first locale in the @@ -434,6 +470,7 @@ public final class LocaleList implements Parcelable { return sDefaultLocaleList; } sDefaultLocaleList = new LocaleList(defaultLocale, sLastExplicitlySetLocaleList); + sDefaultAdjustedLocaleList = sDefaultLocaleList; } // sDefaultLocaleList can't be null, since it can't be set to null by // LocaleList.setDefault(), and if getDefault() is called before a call to @@ -444,6 +481,20 @@ public final class LocaleList implements Parcelable { } /** + * Returns the default locale list, adjusted by moving the default locale to its first + * position. + * + * {@hide} + */ + @NonNull @Size(min=1) + public static LocaleList getAdjustedDefault() { + getDefault(); // to recalculate the default locale list, if necessary + synchronized (sLock) { + return sDefaultAdjustedLocaleList; + } + } + + /** * Also sets the default locale by calling Locale.setDefault() with the first locale in the * list. * @@ -474,6 +525,12 @@ public final class LocaleList implements Parcelable { Locale.setDefault(sLastDefaultLocale); sLastExplicitlySetLocaleList = locales; sDefaultLocaleList = locales; + if (localeIndex == 0) { + sDefaultAdjustedLocaleList = sDefaultLocaleList; + } else { + sDefaultAdjustedLocaleList = new LocaleList( + sLastDefaultLocale, sDefaultLocaleList); + } } } } diff --git a/core/java/android/view/DropPermissions.java b/core/java/android/view/DropPermissions.java index 8c948a9ab4ae..5411dad3284a 100644 --- a/core/java/android/view/DropPermissions.java +++ b/core/java/android/view/DropPermissions.java @@ -16,6 +16,7 @@ package android.view; +import android.app.ActivityManagerNative; import android.os.IBinder; import android.os.RemoteException; import com.android.internal.view.IDropPermissions; @@ -41,6 +42,8 @@ public final class DropPermissions { private final IDropPermissions mDropPermissions; + private IBinder mPermissionOwnerToken; + private final CloseGuard mCloseGuard = CloseGuard.get(); /** @@ -80,11 +83,29 @@ public final class DropPermissions { } /** + * Take the permissions. Must call {@link #release} explicitly. + * @return True if permissions are successfully taken. + * @hide + */ + public boolean takeTransient() { + try { + mPermissionOwnerToken = ActivityManagerNative.getDefault(). + newUriPermissionOwner("drop"); + mDropPermissions.takeTransient(mPermissionOwnerToken); + } catch (RemoteException e) { + return false; + } + mCloseGuard.open("release"); + return true; + } + + /** * Revoke permissions explicitly. */ public void release() { try { mDropPermissions.release(); + mPermissionOwnerToken = null; } catch (RemoteException e) { } mCloseGuard.close(); diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 67473c6a4521..617d3dd67306 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -77,6 +77,7 @@ import android.view.ActionMode.Callback; import android.view.ContextMenu; import android.view.DisplayListCanvas; import android.view.DragEvent; +import android.view.DropPermissions; import android.view.Gravity; import android.view.InputDevice; import android.view.LayoutInflater; @@ -2298,11 +2299,24 @@ public class Editor { void onDrop(DragEvent event) { StringBuilder content = new StringBuilder(""); - ClipData clipData = event.getClipData(); - final int itemCount = clipData.getItemCount(); - for (int i=0; i < itemCount; i++) { - Item item = clipData.getItemAt(i); - content.append(item.coerceToStyledText(mTextView.getContext())); + + final DropPermissions dropPermissions = DropPermissions.obtain(event); + if (dropPermissions != null) { + dropPermissions.takeTransient(); + } + + try { + ClipData clipData = event.getClipData(); + final int itemCount = clipData.getItemCount(); + for (int i=0; i < itemCount; i++) { + Item item = clipData.getItemAt(i); + content.append(item.coerceToStyledText(mTextView.getContext())); + } + } + finally { + if (dropPermissions != null) { + dropPermissions.release(); + } } final int offset = mTextView.getOffsetForPosition(event.getX(), event.getY()); diff --git a/core/java/com/android/internal/app/LocaleHelper.java b/core/java/com/android/internal/app/LocaleHelper.java index 5ac786a971dd..71c2c21df48b 100644 --- a/core/java/com/android/internal/app/LocaleHelper.java +++ b/core/java/com/android/internal/app/LocaleHelper.java @@ -24,7 +24,7 @@ import java.util.Comparator; import java.util.Locale; /** - * This class implements some handy methods to proces with locales. + * This class implements some handy methods to process with locales. */ public class LocaleHelper { @@ -55,7 +55,7 @@ public class LocaleHelper { * source, breakIterator, UCharacter.TITLECASE_NO_LOWERCASE); * }} * - * <p>That also means creating BreakIteratos for each locale. Expensive...</p> + * <p>That also means creating a BreakIterator for each locale. Expensive...</p> * * @param str the string to sentence-case. * @param locale the locale used for the case conversion. @@ -180,7 +180,7 @@ public class LocaleHelper { * * <p>Gives priority to suggested locales (to sort them at the top).</p> */ - static final class LocaleInfoComparator implements Comparator<LocaleStore.LocaleInfo> { + public static final class LocaleInfoComparator implements Comparator<LocaleStore.LocaleInfo> { private final Collator mCollator; /** diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java index 2191c58a82e7..210adce4d0af 100644 --- a/core/java/com/android/internal/app/LocaleStore.java +++ b/core/java/com/android/internal/app/LocaleStore.java @@ -179,6 +179,34 @@ public class LocaleStore { return result; } + /* + * This method is added for SetupWizard, to force an update of the suggested locales + * when the SIM is initialized. + * + * <p>When the device is freshly started, it sometimes gets to the language selection + * before the SIM is properly initialized. + * So at the time the cache is filled, the info from the SIM might not be available. + * The SetupWizard has a SimLocaleMonitor class to detect onSubscriptionsChanged events. + * SetupWizard will call this function when that happens.</p> + * + * <p>TODO: decide if it is worth moving such kind of monitoring in this shared code. + * The user might change the SIM or might cross border and connect to a network + * in a different country, without restarting the Settings application or the phone.</p> + */ + public static void updateSimCountries(Context context) { + Set<String> simCountries = getSimCountries(context); + + for (LocaleInfo li : sLocaleCache.values()) { + // This method sets the suggestion flags for the (new) SIM locales, but it does not + // try to clean up the old flags. After all, if the user replaces a German SIM + // with a French one, it is still possible that they are speaking German. + // So both French and German are reasonable suggestions. + if (simCountries.contains(li.getLocale().getCountry())) { + li.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_SIM; + } + } + } + public static void fillCache(Context context) { if (sFullyInitialized) { return; @@ -242,11 +270,11 @@ public class LocaleStore { /** * Returns a list of locales for language or region selection. * If the parent is null, then it is the language list. - * If it is not null, then the list will contain all the locales that belong to that perent. + * If it is not null, then the list will contain all the locales that belong to that parent. * Example: if the parent is "ar", then the region list will contain all Arabic locales. * (this is not language based, but language-script, so that it works for zh-Hant and so on. */ - /* package */ static Set<LocaleInfo> getLevelLocales(Context context, Set<String> ignorables, + public static Set<LocaleInfo> getLevelLocales(Context context, Set<String> ignorables, LocaleInfo parent, boolean translatedOnly) { fillCache(context); String parentId = parent == null ? null : parent.getId(); diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java index 2f855c6cb42f..c4ec714302aa 100644 --- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java +++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java @@ -45,7 +45,7 @@ import java.util.Set; * countries for all the other German locales, but not Switzerland * (Austria, Belgium, Germany, Liechtenstein, Luxembourg)</p> */ -class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { +public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { private static final int TYPE_HEADER_SUGGESTED = 0; private static final int TYPE_HEADER_ALL_OTHERS = 1; private static final int TYPE_LOCALE = 2; @@ -56,7 +56,7 @@ class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { private final boolean mCountryMode; private LayoutInflater mInflater; - SuggestedLocaleAdapter(Set<LocaleStore.LocaleInfo> localeOptions, boolean countryMode) { + public SuggestedLocaleAdapter(Set<LocaleStore.LocaleInfo> localeOptions, boolean countryMode) { mCountryMode = countryMode; mLocaleOptions = new ArrayList<>(localeOptions.size()); for (LocaleStore.LocaleInfo li : localeOptions) { @@ -169,11 +169,14 @@ class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { return convertView; } - private boolean showHeaders() { return mSuggestionCount != 0 && mSuggestionCount != mLocaleOptions.size(); } + /** + * Sorts the items in the adapter using a locale-aware comparator. + * @param comp The locale-aware comparator to use. + */ public void sort(LocaleHelper.LocaleInfoComparator comp) { Collections.sort(mLocaleOptions, comp); } @@ -222,7 +225,7 @@ class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { // TODO: decide if this is enough, or we want to use a BreakIterator... boolean wordMatches(String valueText, String prefixString) { - // First match against the whole, non-splitted value + // First match against the whole, non-split value if (valueText.startsWith(prefixString)) { return true; } @@ -239,7 +242,6 @@ class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { } @Override - @SuppressWarnings("unchecked") protected void publishResults(CharSequence constraint, FilterResults results) { mLocaleOptions = (ArrayList<LocaleStore.LocaleInfo>) results.values; diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java index 381e71f7f333..2f26e921d03b 100644 --- a/core/java/com/android/internal/util/Preconditions.java +++ b/core/java/com/android/internal/util/Preconditions.java @@ -42,6 +42,23 @@ public class Preconditions { * @return the string reference that was validated * @throws IllegalArgumentException if {@code string} is empty */ + public static @NonNull String checkStringNotEmpty(final String string) { + if (TextUtils.isEmpty(string)) { + throw new IllegalArgumentException(); + } + return string; + } + + /** + * Ensures that an string reference passed as a parameter to the calling + * method is not empty. + * + * @param string an string reference + * @param errorMessage the exception message to use if the check fails; will + * be converted to a string using {@link String#valueOf(Object)} + * @return the string reference that was validated + * @throws IllegalArgumentException if {@code string} is empty + */ public static @NonNull String checkStringNotEmpty(final String string, final Object errorMessage) { if (TextUtils.isEmpty(string)) { @@ -301,8 +318,8 @@ public class Preconditions { * * @throws NullPointerException if the {@code value} or any of its elements were {@code null} */ - public static <T> Collection<T> checkCollectionElementsNotNull(final Collection<T> value, - final String valueName) { + public static @NonNull <C extends Collection<T>, T> C checkCollectionElementsNotNull( + final C value, final String valueName) { if (value == null) { throw new NullPointerException(valueName + " must not be null"); } diff --git a/core/java/com/android/internal/view/IDropPermissions.aidl b/core/java/com/android/internal/view/IDropPermissions.aidl index 2438bdaed135..74ff4b42d97f 100644 --- a/core/java/com/android/internal/view/IDropPermissions.aidl +++ b/core/java/com/android/internal/view/IDropPermissions.aidl @@ -24,5 +24,6 @@ import android.os.IBinder; */ interface IDropPermissions { void take(IBinder activityToken); + void takeTransient(IBinder permissionOwnerToken); void release(); } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index a1b18fee1032..2354125ef4ff 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -1418,25 +1418,32 @@ public class LockPatternUtils { */ public static final int SOME_AUTH_REQUIRED_AFTER_WRONG_CREDENTIAL = 0x10; - public static final int DEFAULT = STRONG_AUTH_REQUIRED_AFTER_BOOT; - private static final int ALLOWING_FINGERPRINT = STRONG_AUTH_NOT_REQUIRED | SOME_AUTH_REQUIRED_AFTER_USER_REQUEST | SOME_AUTH_REQUIRED_AFTER_WRONG_CREDENTIAL; private final SparseIntArray mStrongAuthRequiredForUser = new SparseIntArray(); private final H mHandler; + private final int mDefaultStrongAuthFlags; - public StrongAuthTracker() { - this(Looper.myLooper()); + public StrongAuthTracker(Context context) { + this(context, Looper.myLooper()); } /** * @param looper the looper on whose thread calls to {@link #onStrongAuthRequiredChanged} * will be scheduled. + * @param context the current {@link Context} */ - public StrongAuthTracker(Looper looper) { + public StrongAuthTracker(Context context, Looper looper) { mHandler = new H(looper); + mDefaultStrongAuthFlags = getDefaultFlags(context); + } + + public static @StrongAuthFlags int getDefaultFlags(Context context) { + boolean strongAuthRequired = context.getResources().getBoolean( + com.android.internal.R.bool.config_strongAuthRequiredOnBoot); + return strongAuthRequired ? STRONG_AUTH_REQUIRED_AFTER_BOOT : STRONG_AUTH_NOT_REQUIRED; } /** @@ -1447,7 +1454,7 @@ public class LockPatternUtils { * @param userId the user for whom the state is queried. */ public @StrongAuthFlags int getStrongAuthForUser(int userId) { - return mStrongAuthRequiredForUser.get(userId, DEFAULT); + return mStrongAuthRequiredForUser.get(userId, mDefaultStrongAuthFlags); } /** @@ -1477,7 +1484,7 @@ public class LockPatternUtils { int oldValue = getStrongAuthForUser(userId); if (strongAuthFlags != oldValue) { - if (strongAuthFlags == DEFAULT) { + if (strongAuthFlags == mDefaultStrongAuthFlags) { mStrongAuthRequiredForUser.delete(userId); } else { mStrongAuthRequiredForUser.put(userId, strongAuthFlags); diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp index 61dc6e425ddf..2018e76e9bee 100644 --- a/core/jni/android/graphics/SurfaceTexture.cpp +++ b/core/jni/android/graphics/SurfaceTexture.cpp @@ -18,6 +18,8 @@ #include <stdio.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> @@ -34,6 +36,8 @@ // ---------------------------------------------------------------------------- +#define EGL_QCOM_PROTECTED_CONTENT 0x32E0 + namespace android { static const char* const OutOfResourcesException = @@ -55,6 +59,28 @@ static int32_t createProcessUniqueId() { return android_atomic_inc(&globalCounter); } +// Check whether the current EGL context is protected. +static bool isProtectedContext() { + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (dpy == EGL_NO_DISPLAY) { + ALOGE("isProtectedSurface: invalid current EGLDisplay"); + return false; + } + + if (ctx == EGL_NO_CONTEXT) { + ALOGE("isProtectedSurface: invalid current EGLContext"); + return false; + } + + EGLint isProtected = EGL_FALSE; + // TODO: Change the enum value below when an extension is ratified. + eglQueryContext(dpy, ctx, EGL_QCOM_PROTECTED_CONTENT, &isProtected); + + return isProtected; +} + // ---------------------------------------------------------------------------- static void SurfaceTexture_setSurfaceTexture(JNIEnv* env, jobject thiz, @@ -263,6 +289,11 @@ static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached, getpid(), createProcessUniqueId())); + // If the current context is protected, inform the producer. + if (isProtectedContext()) { + consumer->setConsumerUsageBits(GRALLOC_USAGE_PROTECTED); + } + SurfaceTexture_setSurfaceTexture(env, thiz, surfaceTexture); SurfaceTexture_setProducer(env, thiz, producer); diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp index 34877e054e55..35b5016e1b07 100644 --- a/core/jni/android_graphics_Canvas.cpp +++ b/core/jni/android_graphics_Canvas.cpp @@ -86,20 +86,20 @@ static jint getSaveCount(JNIEnv*, jobject, jlong canvasHandle) { } static jint save(JNIEnv*, jobject, jlong canvasHandle, jint flagsHandle) { - SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle); + SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle); return static_cast<jint>(get_canvas(canvasHandle)->save(flags)); } static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t, jfloat r, jfloat b, jlong paintHandle, jint flagsHandle) { Paint* paint = reinterpret_cast<Paint*>(paintHandle); - SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle); + SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle); return static_cast<jint>(get_canvas(canvasHandle)->saveLayer(l, t, r, b, paint, flags)); } static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t, jfloat r, jfloat b, jint alpha, jint flagsHandle) { - SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle); + SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle); return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha, flags)); } @@ -351,7 +351,7 @@ static void drawNinePatch(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmap if (CC_LIKELY(dstDensity == srcDensity || dstDensity == 0 || srcDensity == 0)) { canvas->drawNinePatch(skiaBitmap, *chunk, left, top, right, bottom, paint); } else { - canvas->save(SkCanvas::kMatrixClip_SaveFlag); + canvas->save(SaveFlags::MatrixClip); SkScalar scale = dstDensity / (float)srcDensity; canvas->translate(left, top); @@ -390,7 +390,7 @@ static void drawBitmap(JNIEnv* env, jobject jcanvas, jlong canvasHandle, jobject canvas->drawBitmap(bitmap, left, top, paint); } } else { - canvas->save(SkCanvas::kMatrixClip_SaveFlag); + canvas->save(SaveFlags::MatrixClip); SkScalar scale = canvasDensity / (float)bitmapDensity; canvas->translate(left, top); canvas->scale(scale, scale); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 3d4b8b4e9184..56c3fc88cf23 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -383,6 +383,7 @@ <protected-broadcast android:name="android.security.STORAGE_CHANGED" /> <protected-broadcast android:name="android.telecom.action.PHONE_ACCOUNT_REGISTERED" /> <protected-broadcast android:name="android.telecom.action.PHONE_ACCOUNT_UNREGISTERED" /> + <protected-broadcast android:name="android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION" /> <protected-broadcast android:name="android.telephony.action.CARRIER_CONFIG_CHANGED" /> <protected-broadcast android:name="com.android.bluetooth.btservice.action.ALARM_WAKEUP" /> @@ -1207,7 +1208,8 @@ <eat-comment /> <!-- Allows access to the list of accounts in the Accounts Service. - <p>Protection level: normal + <p>Protection level: dangerous + @deprecated Not operative for apps apps with targetSdkVersion >= 24. --> <permission android:name="android.permission.GET_ACCOUNTS" android:permissionGroup="android.permission-group.CONTACTS" diff --git a/core/res/res/layout/app_error_dialog.xml b/core/res/res/layout/app_error_dialog.xml new file mode 100644 index 000000000000..aaa2dbc389dc --- /dev/null +++ b/core/res/res/layout/app_error_dialog.xml @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 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. +*/ +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingTop="@dimen/dialog_list_padding_vertical_material" + android:paddingBottom="@dimen/dialog_list_padding_vertical_material" +> + + + <TextView + android:id="@+id/aerr_restart" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/aerr_restart" + style="@style/aerr_list_item" + /> + + <TextView + android:id="@+id/aerr_reset" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/aerr_reset" + style="@style/aerr_list_item" + /> + + <TextView + android:id="@+id/aerr_report" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/aerr_report" + style="@style/aerr_list_item" + /> + + <TextView + android:id="@+id/aerr_close" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/aerr_close" + style="@style/aerr_list_item" + /> + + <TextView + android:id="@+id/aerr_mute" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/aerr_mute" + style="@style/aerr_list_item" + /> + + +</LinearLayout> diff --git a/core/res/res/layout/app_error_dialog_dont_show_again.xml b/core/res/res/layout/app_error_dialog_dont_show_again.xml deleted file mode 100644 index ba79ecd1fa8c..000000000000 --- a/core/res/res/layout/app_error_dialog_dont_show_again.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 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. -*/ ---> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingStart="14dp" - android:paddingEnd="10dp" - android:gravity="center_vertical" - > - <CheckBox - android:id="@+id/checkbox" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - /> - <TextView - android:id="@+id/text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:layout_marginBottom="8dp" - /> - -</LinearLayout> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 655a5fac923a..507925b70336 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2462,4 +2462,11 @@ <!-- If true, all guest users created on the device will be ephemeral. --> <bool name="config_guestUserEphemeral">false</bool> + + <!-- Enforce strong auth on boot. Setting this to false represents a security risk and should + not be ordinarily done. The only case in which this might be permissible is in a car head + unit where there are hardware mechanisms to protect the device (physical keys) and not + much in the way of user data. + --> + <bool name="config_strongAuthRequiredOnBoot">true</bool> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 28c76bb2d38d..a276854ad59d 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2588,16 +2588,27 @@ <!-- Text to display when there are no activities found to display in the activity chooser. See the "Select an action" title. --> <string name="noApplications">No apps can perform this action.</string> - <!-- Title of the alert when an application has crashed. --> - <string name="aerr_title"></string> <!-- Text of the alert that is displayed when an application has crashed. --> - <string name="aerr_application">Unfortunately, <xliff:g id="application">%1$s</xliff:g> has stopped.</string> - <!-- Text of the alert that is displayed when an application has crashed. --> - <string name="aerr_process">Unfortunately, the process <xliff:g id="process">%1$s</xliff:g> has - stopped.</string> - <!-- Text of the alert that is displayed when an application has crashed. --> - <string name="aerr_process_silence">Silence crashes from <xliff:g id="process">%1$s</xliff:g> - until reboot.</string> + <string name="aerr_application"><xliff:g id="application">%1$s</xliff:g> has stopped</string> + <!-- Text of the alert that is displayed when a process has crashed. --> + <string name="aerr_process"><xliff:g id="process">%1$s</xliff:g> has + stopped</string> + <!-- Text of the alert that is displayed when an application has crashed repeatedly. --> + <string name="aerr_application_repeated"><xliff:g id="application">%1$s</xliff:g> is repeatedly stopping</string> + <!-- Text of the alert that is displayed when a process has crashed repeatedly. --> + <string name="aerr_process_repeated"><xliff:g id="process">%1$s</xliff:g> is + repeatedly stopping</string> + <!-- Button that restarts a crashed application --> + <string name="aerr_restart">Restart app</string> + <!-- Button that clears cache and restarts a crashed application --> + <string name="aerr_reset">Reset and restart app</string> + <!-- Button that sends feedback about a crashed application --> + <string name="aerr_report">Send feedback</string> + <!-- Button that closes a crashed application --> + <string name="aerr_close">Close</string> + <!-- Button that mutes further crashes of the crashed application--> + <string name="aerr_mute">Mute</string> + <!-- Title of the alert when an application is not responding. --> <string name="anr_title"></string> <!-- Text of the alert that is displayed when an application is not responding. --> @@ -4174,4 +4185,9 @@ <string name="suspended_package_title">%1$s disabled</string> <!-- Message for dialog displayed when a packge is suspended by device admin. [CHAR LIMIT=NONE] --> <string name="suspended_package_message">Disabled by %1$s administrator. Contact them to learn more.</string> + + <!-- Notification title shown when new SMS/MMS is received while the device is locked [CHAR LIMIT=NONE] --> + <string name="new_sms_notification_title">You have new messages</string> + <!-- Notification content shown when new SMS/MMS is received while the device is locked [CHAR LIMIT=NONE] --> + <string name="new_sms_notification_content">Open SMS app to view</string> </resources> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index d5349b2ee9f8..b660277847c4 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1397,6 +1397,16 @@ please see styles_device_defaults.xml. <item name="pointerIconGrabbing">@drawable/pointer_grabbing_large_icon</item> </style> + <!-- @hide --> + <style name="aerr_list_item" parent="Widget.Material.Light.TextView"> + <item name="minHeight">?attr/listPreferredItemHeightSmall</item> + <item name="textAppearance">?attr/textAppearanceListItemSmall</item> + <item name="textColor">?attr/textColorAlertDialogListItem</item> + <item name="gravity">center_vertical</item> + <item name="paddingStart">?attr/listPreferredItemPaddingStart</item> + <item name="paddingEnd">?attr/listPreferredItemPaddingEnd</item> + </style> + <!-- Wifi dialog styles --> <!-- @hide --> <style name="wifi_item"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 57ff243eeab0..81705b420fce 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1763,7 +1763,7 @@ <java-symbol type="layout" name="launch_warning" /> <java-symbol type="layout" name="safe_mode" /> <java-symbol type="layout" name="simple_list_item_2_single_choice" /> - <java-symbol type="layout" name="app_error_dialog_dont_show_again" /> + <java-symbol type="layout" name="app_error_dialog" /> <java-symbol type="plurals" name="wifi_available" /> <java-symbol type="plurals" name="wifi_available_detailed" /> <java-symbol type="string" name="accessibility_binding_label" /> @@ -1777,8 +1777,8 @@ <java-symbol type="string" name="remote_bugreport_progress_notification_message_can_cancel" /> <java-symbol type="string" name="aerr_application" /> <java-symbol type="string" name="aerr_process" /> - <java-symbol type="string" name="aerr_process_silence" /> - <java-symbol type="string" name="aerr_title" /> + <java-symbol type="string" name="aerr_application_repeated" /> + <java-symbol type="string" name="aerr_process_repeated" /> <java-symbol type="string" name="android_upgrading_fstrim" /> <java-symbol type="string" name="android_upgrading_apk" /> <java-symbol type="string" name="android_upgrading_complete" /> @@ -2429,6 +2429,12 @@ <java-symbol type="id" name="work_widget_app_icon" /> <java-symbol type="drawable" name="work_widget_mask_view_background" /> + <java-symbol type="id" name="aerr_report" /> + <java-symbol type="id" name="aerr_reset" /> + <java-symbol type="id" name="aerr_restart" /> + <java-symbol type="id" name="aerr_close" /> + <java-symbol type="id" name="aerr_mute" /> + <!-- Framework-private Material.DayNight styles. --> <java-symbol type="style" name="Theme.Material.DayNight" /> <java-symbol type="style" name="Theme.Material.DayNight.DarkActionBar" /> @@ -2493,4 +2499,10 @@ <java-symbol type="string" name="work_mode_turn_on" /> <java-symbol type="string" name="suspended_package_title" /> <java-symbol type="string" name="suspended_package_message" /> + + <!-- New SMS notification while phone is locked. --> + <java-symbol type="string" name="new_sms_notification_title" /> + <java-symbol type="string" name="new_sms_notification_content" /> + + <java-symbol type="bool" name="config_strongAuthRequiredOnBoot" /> </resources> diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk index ee8921ef8228..eb055de01c63 100644 --- a/core/tests/coretests/Android.mk +++ b/core/tests/coretests/Android.mk @@ -32,7 +32,8 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ littlemock \ android-support-test \ mockito-target \ - espresso-core + espresso-core \ + ub-uiautomator LOCAL_JAVA_LIBRARIES := android.test.runner conscrypt telephony-common org.apache.http.legacy LOCAL_PACKAGE_NAME := FrameworksCoreTests diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index eb0075b6b0b7..bfa2b10fdf4f 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -1295,6 +1295,26 @@ </intent-filter> </activity> + <activity android:name="android.print.PrintTestActivity"/> + + <service + android:name="android.print.mockservice.MockPrintService" + android:permission="android.permission.BIND_PRINT_SERVICE"> + <intent-filter> + <action android:name="android.printservice.PrintService" /> + </intent-filter> + <meta-data + android:name="android.printservice" + android:resource="@xml/printservice"> + </meta-data> + </service> + + <activity + android:name="android.print.mockservice.SettingsActivity" + android:permission="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY" + android:exported="true"> + </activity> + </application> <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" diff --git a/core/tests/coretests/res/xml/printservice.xml b/core/tests/coretests/res/xml/printservice.xml new file mode 100644 index 000000000000..abbebdae0d57 --- /dev/null +++ b/core/tests/coretests/res/xml/printservice.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<print-service xmlns:android="http://schemas.android.com/apk/res/android" + android:settingsActivity="android.print.mockservice.SettingsActivity"/> diff --git a/core/tests/coretests/src/android/content/res/ResourcesLocaleResolutionTest.java b/core/tests/coretests/src/android/content/res/ResourcesLocaleResolutionTest.java deleted file mode 100644 index 55c00314ee63..000000000000 --- a/core/tests/coretests/src/android/content/res/ResourcesLocaleResolutionTest.java +++ /dev/null @@ -1,53 +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.content.res; - -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; -import android.util.DisplayMetrics; -import android.util.LocaleList; - -import java.util.Arrays; -import java.util.Locale; - -public class ResourcesLocaleResolutionTest extends AndroidTestCase { - @SmallTest - public void testGetResolvedLocale_englishIsAlwaysConsideredSupported() { - // First make sure English has no explicit assets other than the default assets - final AssetManager assets = getContext().getAssets(); - final String supportedLocales[] = assets.getNonSystemLocales(); - for (String languageTag : supportedLocales) { - if ("en-XA".equals(languageTag)) { - continue; - } - assertFalse( - "supported locales: " + Arrays.toString(supportedLocales), - "en".equals(Locale.forLanguageTag(languageTag).getLanguage())); - } - - final DisplayMetrics dm = new DisplayMetrics(); - dm.setToDefaults(); - final Configuration cfg = new Configuration(); - cfg.setToDefaults(); - // Avestan and English have no assets, but Persian does. - cfg.setLocales(LocaleList.forLanguageTags("ae,en,fa")); - Resources res = new Resources(assets, dm, cfg); - // We should get English, because it is always considered supported. - assertEquals("en", res.getResolvedLocale().toLanguageTag()); - } -} - diff --git a/core/tests/coretests/src/android/print/BasePrintTest.java b/core/tests/coretests/src/android/print/BasePrintTest.java new file mode 100644 index 000000000000..19ce44a90815 --- /dev/null +++ b/core/tests/coretests/src/android/print/BasePrintTest.java @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.print; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doCallRealMethod; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.annotation.NonNull; +import android.app.Instrumentation; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.os.ParcelFileDescriptor; +import android.os.SystemClock; +import android.print.PrintAttributes; +import android.print.PrintDocumentAdapter; +import android.print.PrintManager; +import android.print.PrinterId; +import android.print.mockservice.PrintServiceCallbacks; +import android.print.mockservice.PrinterDiscoverySessionCallbacks; +import android.print.mockservice.StubbablePrinterDiscoverySession; +import android.printservice.CustomPrinterIconCallback; +import android.printservice.PrintJob; +import android.printservice.PrintService; +import android.test.InstrumentationTestCase; +import android.util.DisplayMetrics; +import android.util.LocaleList; + +import org.mockito.stubbing.Answer; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.TimeoutException; + +/** + * This is the base class for print tests. + */ +public abstract class BasePrintTest extends InstrumentationTestCase { + + private static final long OPERATION_TIMEOUT = 30000; + private static final String PRINT_SPOOLER_PACKAGE_NAME = "com.android.printspooler"; + private static final String PM_CLEAR_SUCCESS_OUTPUT = "Success"; + private static final String COMMAND_LIST_ENABLED_IME_COMPONENTS = "ime list -s"; + private static final String COMMAND_PREFIX_ENABLE_IME = "ime enable "; + private static final String COMMAND_PREFIX_DISABLE_IME = "ime disable "; + private static final int CURRENT_USER_ID = -2; // Mirrors UserHandle.USER_CURRENT + + private PrintTestActivity mActivity; + private android.print.PrintJob mPrintJob; + + private LocaleList mOldLocale; + + private CallCounter mStartCallCounter; + private CallCounter mStartSessionCallCounter; + + private String[] mEnabledImes; + + private String[] getEnabledImes() throws IOException { + List<String> imeList = new ArrayList<>(); + + ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation() + .executeShellCommand(COMMAND_LIST_ENABLED_IME_COMPONENTS); + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(new FileInputStream(pfd.getFileDescriptor())))) { + + String line; + while ((line = reader.readLine()) != null) { + imeList.add(line); + } + } + + String[] imeArray = new String[imeList.size()]; + imeList.toArray(imeArray); + + return imeArray; + } + + private void disableImes() throws Exception { + mEnabledImes = getEnabledImes(); + for (String ime : mEnabledImes) { + String disableImeCommand = COMMAND_PREFIX_DISABLE_IME + ime; + runShellCommand(getInstrumentation(), disableImeCommand); + } + } + + private void enableImes() throws Exception { + for (String ime : mEnabledImes) { + String enableImeCommand = COMMAND_PREFIX_ENABLE_IME + ime; + runShellCommand(getInstrumentation(), enableImeCommand); + } + mEnabledImes = null; + } + + @Override + protected void runTest() throws Throwable { + // Do nothing if the device does not support printing. + if (supportsPrinting()) { + super.runTest(); + } + } + + @Override + public void setUp() throws Exception { + super.setUp(); + if (!supportsPrinting()) { + return; + } + + // Make sure we start with a clean slate. + clearPrintSpoolerData(); + disableImes(); + + // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2 + // Dexmaker is used by mockito. + System.setProperty("dexmaker.dexcache", getInstrumentation() + .getTargetContext().getCacheDir().getPath()); + + // Set to US locale. + Resources resources = getInstrumentation().getTargetContext().getResources(); + Configuration oldConfiguration = resources.getConfiguration(); + if (!oldConfiguration.getLocales().getPrimary().equals(Locale.US)) { + mOldLocale = oldConfiguration.getLocales(); + DisplayMetrics displayMetrics = resources.getDisplayMetrics(); + Configuration newConfiguration = new Configuration(oldConfiguration); + newConfiguration.setLocale(Locale.US); + resources.updateConfiguration(newConfiguration, displayMetrics); + } + + // Initialize the latches. + mStartCallCounter = new CallCounter(); + mStartSessionCallCounter = new CallCounter(); + + // Create the activity for the right locale. + createActivity(); + } + + @Override + public void tearDown() throws Exception { + if (!supportsPrinting()) { + return; + } + + // Done with the activity. + getActivity().finish(); + enableImes(); + + // Restore the locale if needed. + if (mOldLocale != null) { + Resources resources = getInstrumentation().getTargetContext().getResources(); + DisplayMetrics displayMetrics = resources.getDisplayMetrics(); + Configuration newConfiguration = new Configuration(resources.getConfiguration()); + newConfiguration.setLocales(mOldLocale); + mOldLocale = null; + resources.updateConfiguration(newConfiguration, displayMetrics); + } + + // Make sure the spooler is cleaned, this also un-approves all services + clearPrintSpoolerData(); + + super.tearDown(); + } + + protected android.print.PrintJob print(@NonNull final PrintDocumentAdapter adapter, + final PrintAttributes attributes) { + // Initiate printing as if coming from the app. + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + PrintManager printManager = (PrintManager) getActivity() + .getSystemService(Context.PRINT_SERVICE); + mPrintJob = printManager.print("Print job", adapter, attributes); + } + }); + + return mPrintJob; + } + + protected void onStartCalled() { + mStartCallCounter.call(); + } + + protected void onPrinterDiscoverySessionStartCalled() { + mStartSessionCallCounter.call(); + } + + protected void waitForPrinterDiscoverySessionStartCallbackCalled() { + waitForCallbackCallCount(mStartSessionCallCounter, 1, + "Did not get expected call to onStartPrinterDiscoverySession."); + } + + protected void waitForStartAdapterCallbackCalled() { + waitForCallbackCallCount(mStartCallCounter, 1, "Did not get expected call to start."); + } + + private void waitForCallbackCallCount(CallCounter counter, int count, String message) { + try { + counter.waitForCount(count, OPERATION_TIMEOUT); + } catch (TimeoutException te) { + fail(message); + } + } + + protected PrintTestActivity getActivity() { + return mActivity; + } + + protected void createActivity() { + mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(), + PrintTestActivity.class, null); + } + + public static String runShellCommand(Instrumentation instrumentation, String cmd) + throws IOException { + ParcelFileDescriptor pfd = instrumentation.getUiAutomation().executeShellCommand(cmd); + byte[] buf = new byte[512]; + int bytesRead; + FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd); + StringBuffer stdout = new StringBuffer(); + while ((bytesRead = fis.read(buf)) != -1) { + stdout.append(new String(buf, 0, bytesRead)); + } + fis.close(); + return stdout.toString(); + } + + protected void clearPrintSpoolerData() throws Exception { + assertTrue("failed to clear print spooler data", + runShellCommand(getInstrumentation(), String.format( + "pm clear --user %d %s", CURRENT_USER_ID, PRINT_SPOOLER_PACKAGE_NAME)) + .contains(PM_CLEAR_SUCCESS_OUTPUT)); + } + + @SuppressWarnings("unchecked") + protected PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks( + Answer<Void> onStartPrinterDiscovery, Answer<Void> onStopPrinterDiscovery, + Answer<Void> onValidatePrinters, Answer<Void> onStartPrinterStateTracking, + Answer<Void> onRequestCustomPrinterIcon, Answer<Void> onStopPrinterStateTracking, + Answer<Void> onDestroy) { + PrinterDiscoverySessionCallbacks callbacks = mock(PrinterDiscoverySessionCallbacks.class); + + doCallRealMethod().when(callbacks).setSession(any(StubbablePrinterDiscoverySession.class)); + when(callbacks.getSession()).thenCallRealMethod(); + + if (onStartPrinterDiscovery != null) { + doAnswer(onStartPrinterDiscovery).when(callbacks).onStartPrinterDiscovery( + any(List.class)); + } + if (onStopPrinterDiscovery != null) { + doAnswer(onStopPrinterDiscovery).when(callbacks).onStopPrinterDiscovery(); + } + if (onValidatePrinters != null) { + doAnswer(onValidatePrinters).when(callbacks).onValidatePrinters( + any(List.class)); + } + if (onStartPrinterStateTracking != null) { + doAnswer(onStartPrinterStateTracking).when(callbacks).onStartPrinterStateTracking( + any(PrinterId.class)); + } + if (onRequestCustomPrinterIcon != null) { + doAnswer(onRequestCustomPrinterIcon).when(callbacks).onRequestCustomPrinterIcon( + any(PrinterId.class), any(CustomPrinterIconCallback.class)); + } + if (onStopPrinterStateTracking != null) { + doAnswer(onStopPrinterStateTracking).when(callbacks).onStopPrinterStateTracking( + any(PrinterId.class)); + } + if (onDestroy != null) { + doAnswer(onDestroy).when(callbacks).onDestroy(); + } + + return callbacks; + } + + protected PrintServiceCallbacks createMockPrintServiceCallbacks( + Answer<PrinterDiscoverySessionCallbacks> onCreatePrinterDiscoverySessionCallbacks, + Answer<Void> onPrintJobQueued, Answer<Void> onRequestCancelPrintJob) { + final PrintServiceCallbacks service = mock(PrintServiceCallbacks.class); + + doCallRealMethod().when(service).setService(any(PrintService.class)); + when(service.getService()).thenCallRealMethod(); + + if (onCreatePrinterDiscoverySessionCallbacks != null) { + doAnswer(onCreatePrinterDiscoverySessionCallbacks).when(service) + .onCreatePrinterDiscoverySessionCallbacks(); + } + if (onPrintJobQueued != null) { + doAnswer(onPrintJobQueued).when(service).onPrintJobQueued(any(PrintJob.class)); + } + if (onRequestCancelPrintJob != null) { + doAnswer(onRequestCancelPrintJob).when(service).onRequestCancelPrintJob( + any(PrintJob.class)); + } + + return service; + } + + protected final class CallCounter { + private final Object mLock = new Object(); + + private int mCallCount; + + public void call() { + synchronized (mLock) { + mCallCount++; + mLock.notifyAll(); + } + } + + public int getCallCount() { + synchronized (mLock) { + return mCallCount; + } + } + + public void waitForCount(int count, long timeoutMillis) throws TimeoutException { + synchronized (mLock) { + final long startTimeMillis = SystemClock.uptimeMillis(); + while (mCallCount < count) { + try { + final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; + final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis; + if (remainingTimeMillis <= 0) { + throw new TimeoutException(); + } + mLock.wait(timeoutMillis); + } catch (InterruptedException ie) { + /* ignore */ + } + } + } + } + } + + protected boolean supportsPrinting() { + return getInstrumentation().getContext().getPackageManager() + .hasSystemFeature(PackageManager.FEATURE_PRINTING); + } +} diff --git a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java new file mode 100644 index 000000000000..5179b42fd650 --- /dev/null +++ b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java @@ -0,0 +1,669 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.print; + +import android.annotation.NonNull; +import android.content.ComponentName; +import android.content.Context; +import android.os.Bundle; +import android.os.CancellationSignal; +import android.os.Handler; +import android.os.Looper; +import android.os.ParcelFileDescriptor; +import android.os.Process; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.print.IPrintDocumentAdapter; +import android.print.IPrintJobStateChangeListener; +import android.print.IPrintManager; +import android.print.IPrinterDiscoveryObserver; +import android.print.PageRange; +import android.print.PrintAttributes; +import android.print.PrintAttributes.Margins; +import android.print.PrintAttributes.MediaSize; +import android.print.PrintAttributes.Resolution; +import android.print.PrintDocumentAdapter; +import android.print.PrintJob; +import android.print.PrintJobId; +import android.print.PrintJobInfo; +import android.print.PrintManager; +import android.print.PrinterCapabilitiesInfo; +import android.print.PrinterDiscoverySession; +import android.print.PrinterId; +import android.print.PrinterInfo; +import android.printservice.PrintServiceInfo; + +import android.print.mockservice.MockPrintService; +import android.print.mockservice.PrintServiceCallbacks; +import android.print.mockservice.PrinterDiscoverySessionCallbacks; +import android.print.mockservice.StubbablePrinterDiscoverySession; + +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.util.ArrayList; +import java.util.List; + +/** + * tests feeding all possible parameters to the IPrintManager Binder. + */ +public class IPrintManagerParametersTest extends BasePrintTest { + + private final int BAD_APP_ID = 0xffffffff; + + private final int mAppId; + private final int mUserId; + private final PrintJobId mBadPrintJobId; + + private PrintJob mGoodPrintJob; + private PrinterId mBadPrinterId; + private PrinterId mGoodPrinterId; + private ComponentName mGoodComponentName; + private ComponentName mBadComponentName; + + private IPrintManager mIPrintManager; + + /** + * Create a new IPrintManagerParametersTest and setup basic fields. + */ + public IPrintManagerParametersTest() { + super(); + + mAppId = UserHandle.getAppId(Process.myUid()); + mUserId = UserHandle.myUserId(); + mBadPrintJobId = new PrintJobId(); + mBadComponentName = new ComponentName("bad", "bad"); + } + + /** + * Create a mock PrintDocumentAdapter. + * + * @return The adapter + */ + private @NonNull PrintDocumentAdapter createMockAdapter() { + return new PrintDocumentAdapter() { + @Override + public void onStart() { + onStartCalled(); + } + + @Override + public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, + CancellationSignal cancellationSignal, LayoutResultCallback callback, + Bundle extras) { + } + + @Override + public void onWrite(PageRange[] pages, ParcelFileDescriptor destination, + CancellationSignal cancellationSignal, WriteResultCallback callback) { + } + }; + } + + /** + * Create mock print service callbacks. + * + * @return the callbacks + */ + private PrintServiceCallbacks createMockCallbacks() { + return createMockPrintServiceCallbacks( + new Answer<PrinterDiscoverySessionCallbacks>() { + @Override + public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) { + return createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() { + @Override + public Void answer(InvocationOnMock invocation) { + // Get the session. + StubbablePrinterDiscoverySession session = + ((PrinterDiscoverySessionCallbacks) invocation + .getMock()).getSession(); + + if (session.getPrinters().isEmpty()) { + final String PRINTER_NAME = "good printer"; + List<PrinterInfo> printers = new ArrayList<PrinterInfo>(); + + // Add the printer. + mGoodPrinterId = session.getService() + .generatePrinterId(PRINTER_NAME); + + PrinterCapabilitiesInfo capabilities = + new PrinterCapabilitiesInfo.Builder(mGoodPrinterId) + .setMinMargins( + new Margins(200, 200, 200, 200)) + .addMediaSize(MediaSize.ISO_A4, true) + .addResolution(new Resolution("300x300", + "300x300", 300, 300), + true) + .setColorModes( + PrintAttributes.COLOR_MODE_COLOR, + PrintAttributes.COLOR_MODE_COLOR) + .build(); + + PrinterInfo printer = new PrinterInfo.Builder( + mGoodPrinterId, + PRINTER_NAME, + PrinterInfo.STATUS_IDLE) + .setCapabilities(capabilities) + .build(); + printers.add(printer); + + session.addPrinters(printers); + } + onPrinterDiscoverySessionStartCalled(); + return null; + } + }, null, null, null, null, null, null); + } + }, + null, null); + } + + /** + * Create a IPrintJobStateChangeListener object. + * + * @return the object + * @throws Exception if the object could not be created. + */ + private IPrintJobStateChangeListener createMockIPrintJobStateChangeListener() throws Exception { + return new PrintManager.PrintJobStateChangeListenerWrapper(null, + new Handler(Looper.getMainLooper())); + } + + /** + * Create a IPrinterDiscoveryObserver object. + * + * @return the object + * @throws Exception if the object could not be created. + */ + private IPrinterDiscoveryObserver createMockIPrinterDiscoveryObserver() throws Exception { + return new PrinterDiscoverySession.PrinterDiscoveryObserver(null); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + + MockPrintService.setCallbacks(createMockCallbacks()); + + mGoodComponentName = getActivity().getComponentName(); + + mGoodPrintJob = print(createMockAdapter(), null); + + mIPrintManager = IPrintManager.Stub + .asInterface(ServiceManager.getService(Context.PRINT_SERVICE)); + + // Generate dummy printerId which is a valid PrinterId object, but does not correspond to a + // printer + mBadPrinterId = new PrinterId(mGoodComponentName, "dummy printer"); + + // Wait for PrintActivity to be ready + waitForStartAdapterCallbackCalled(); + + // Wait for printer discovery session to be ready + waitForPrinterDiscoverySessionStartCallbackCalled(); + } + + /** + * {@link Runnable} that can throw and {@link Exception} + */ + private interface Invokable { + /** + * Execute the {@link Invokable} + * + * @throws Exception + */ + public void run() throws Exception; + } + + /** + * Assert that the invokable throws an expectedException + * + * @param invokable The {@link Invokable} to run + * @param expectedClass The {@link Exception} that is supposed to be thrown + */ + public void assertException(Invokable invokable, Class<? extends Exception> expectedClass) + throws Exception { + try { + invokable.run(); + } catch (Exception e) { + if (e.getClass().isAssignableFrom(expectedClass)) { + return; + } else { + throw new AssertionError("Expected: " + expectedClass.getName() + ", got: " + + e.getClass().getName()); + } + } + + throw new AssertionError("No exception thrown"); + } + + /** + * test IPrintManager.getPrintJobInfo + */ + public void testGetPrintJobInfo() throws Exception { + assertEquals(mGoodPrintJob.getId(), mIPrintManager.getPrintJobInfo(mGoodPrintJob.getId(), + mAppId, mUserId).getId()); + assertEquals(null, mIPrintManager.getPrintJobInfo(mBadPrintJobId, mAppId, mUserId)); + assertEquals(null, mIPrintManager.getPrintJobInfo(null, mAppId, mUserId)); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.getPrintJobInfo(mGoodPrintJob.getId(), BAD_APP_ID, mUserId); + } + }, SecurityException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.getPrintJobInfos + */ + public void testGetPrintJobInfos() throws Exception { + List<PrintJobInfo> infos = mIPrintManager.getPrintJobInfos(mAppId, mUserId); + + boolean foundPrintJob = false; + for (PrintJobInfo info : infos) { + if (info.getId().equals(mGoodPrintJob.getId())) { + assertEquals(PrintJobInfo.STATE_CREATED, info.getState()); + foundPrintJob = true; + } + } + assertTrue(foundPrintJob); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.getPrintJobInfos(BAD_APP_ID, mUserId); + } + }, SecurityException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.print + */ + public void testPrint() throws Exception { + final String name = "dummy print job"; + + final IPrintDocumentAdapter adapter = new PrintManager + .PrintDocumentAdapterDelegate(getActivity(), createMockAdapter()); + + // Valid parameters are tested in setUp() + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.print(null, adapter, null, mGoodComponentName.getPackageName(), + mAppId, mUserId); + } + }, IllegalArgumentException.class); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.print(name, null, null, mGoodComponentName.getPackageName(), + mAppId, mUserId); + } + }, NullPointerException.class); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.print(name, adapter, null, null, mAppId, mUserId); + } + }, IllegalArgumentException.class); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.print(name, adapter, null, mBadComponentName.getPackageName(), + mAppId, mUserId); + } + }, IllegalArgumentException.class); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.print(name, adapter, null, mGoodComponentName.getPackageName(), + BAD_APP_ID, mUserId); + } + }, SecurityException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.cancelPrintJob + */ + public void testCancelPrintJob() throws Exception { + // Invalid print jobs IDs do not produce an exception + mIPrintManager.cancelPrintJob(mBadPrintJobId, mAppId, mUserId); + mIPrintManager.cancelPrintJob(null, mAppId, mUserId); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.cancelPrintJob(mGoodPrintJob.getId(), BAD_APP_ID, mUserId); + } + }, SecurityException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + + // Must be last as otherwise mGoodPrintJob will not be good anymore + mIPrintManager.cancelPrintJob(mGoodPrintJob.getId(), mAppId, mUserId); + } + + /** + * test IPrintManager.restartPrintJob + */ + public void testRestartPrintJob() throws Exception { + mIPrintManager.restartPrintJob(mGoodPrintJob.getId(), mAppId, mUserId); + + // Invalid print jobs IDs do not produce an exception + mIPrintManager.restartPrintJob(mBadPrintJobId, mAppId, mUserId); + mIPrintManager.restartPrintJob(null, mAppId, mUserId); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.restartPrintJob(mGoodPrintJob.getId(), BAD_APP_ID, mUserId); + } + }, SecurityException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.addPrintJobStateChangeListener + */ + public void testAddPrintJobStateChangeListener() throws Exception { + final IPrintJobStateChangeListener listener = createMockIPrintJobStateChangeListener(); + + mIPrintManager.addPrintJobStateChangeListener(listener, mAppId, mUserId); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.addPrintJobStateChangeListener(null, mAppId, mUserId); + } + }, NullPointerException.class); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.addPrintJobStateChangeListener(listener, BAD_APP_ID, mUserId); + } + }, SecurityException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.removePrintJobStateChangeListener + */ + public void testRemovePrintJobStateChangeListener() throws Exception { + final IPrintJobStateChangeListener listener = createMockIPrintJobStateChangeListener(); + + mIPrintManager.addPrintJobStateChangeListener(listener, mAppId, mUserId); + mIPrintManager.removePrintJobStateChangeListener(listener, mUserId); + + // Removing unknown listeners is a no-op + mIPrintManager.removePrintJobStateChangeListener(listener, mUserId); + + mIPrintManager.addPrintJobStateChangeListener(listener, mAppId, mUserId); + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.removePrintJobStateChangeListener(null, mUserId); + } + }, NullPointerException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.getInstalledPrintServices + */ + public void testGetInstalledPrintServices() throws Exception { + List<PrintServiceInfo> printServices = mIPrintManager.getInstalledPrintServices(mUserId); + assertTrue(printServices.size() >= 2); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.getEnabledPrintServices + */ + public void testGetEnabledPrintServices() throws Exception { + List<PrintServiceInfo> printServices = mIPrintManager.getEnabledPrintServices(mUserId); + assertTrue(printServices.size() >= 2); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.createPrinterDiscoverySession + */ + public void testCreatePrinterDiscoverySession() throws Exception { + final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver(); + + mIPrintManager.createPrinterDiscoverySession(listener, mUserId); + + try { + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.createPrinterDiscoverySession(null, mUserId); + } + }, NullPointerException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } finally { + // Remove discovery session so that the next test create a new one. Usually a leaked + // session is removed on the next call from the print service. But in this case we want + // to force a new call to onPrinterDiscoverySessionStart in the next test. + mIPrintManager.destroyPrinterDiscoverySession(listener, mUserId); + } + } + + /** + * test IPrintManager.startPrinterDiscovery + */ + public void testStartPrinterDiscovery() throws Exception { + final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver(); + final List<PrinterId> goodPrinters = new ArrayList<>(); + goodPrinters.add(mGoodPrinterId); + + final List<PrinterId> badPrinters = new ArrayList<>(); + badPrinters.add(mBadPrinterId); + + final List<PrinterId> emptyPrinters = new ArrayList<>(); + + final List<PrinterId> nullPrinters = new ArrayList<>(); + nullPrinters.add(null); + + mIPrintManager.startPrinterDiscovery(listener, goodPrinters, mUserId); + + // Bad or no printers do no cause exceptions + mIPrintManager.startPrinterDiscovery(listener, badPrinters, mUserId); + mIPrintManager.startPrinterDiscovery(listener, emptyPrinters, mUserId); + mIPrintManager.startPrinterDiscovery(listener, null, mUserId); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.startPrinterDiscovery(listener, nullPrinters, mUserId); + } + }, NullPointerException.class); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.startPrinterDiscovery(null, goodPrinters, mUserId); + } + }, NullPointerException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.stopPrinterDiscovery + */ + public void testStopPrinterDiscovery() throws Exception { + final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver(); + + mIPrintManager.startPrinterDiscovery(listener, null, mUserId); + mIPrintManager.stopPrinterDiscovery(listener, mUserId); + + // Stopping an already stopped session is a no-op + mIPrintManager.stopPrinterDiscovery(listener, mUserId); + + mIPrintManager.startPrinterDiscovery(listener, null, mUserId); + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.stopPrinterDiscovery(null, mUserId); + } + }, NullPointerException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.validatePrinters + */ + public void testValidatePrinters() throws Exception { + final List<PrinterId> goodPrinters = new ArrayList<>(); + goodPrinters.add(mGoodPrinterId); + + final List<PrinterId> badPrinters = new ArrayList<>(); + badPrinters.add(mBadPrinterId); + + final List<PrinterId> emptyPrinters = new ArrayList<>(); + + final List<PrinterId> nullPrinters = new ArrayList<>(); + nullPrinters.add(null); + + mIPrintManager.validatePrinters(goodPrinters, mUserId); + + // Bad or empty list of printers do no cause exceptions + mIPrintManager.validatePrinters(badPrinters, mUserId); + mIPrintManager.validatePrinters(emptyPrinters, mUserId); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.validatePrinters(null, mUserId); + } + }, NullPointerException.class); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.validatePrinters(nullPrinters, mUserId); + } + }, NullPointerException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.startPrinterStateTracking + */ + public void testStartPrinterStateTracking() throws Exception { + mIPrintManager.startPrinterStateTracking(mGoodPrinterId, mUserId); + + // Bad printers do no cause exceptions + mIPrintManager.startPrinterStateTracking(mBadPrinterId, mUserId); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.startPrinterStateTracking(null, mUserId); + } + }, NullPointerException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.getCustomPrinterIcon + */ + public void testGetCustomPrinterIcon() throws Exception { + mIPrintManager.getCustomPrinterIcon(mGoodPrinterId, mUserId); + + // Bad printers do no cause exceptions + mIPrintManager.getCustomPrinterIcon(mBadPrinterId, mUserId); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.getCustomPrinterIcon(null, mUserId); + } + }, NullPointerException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.stopPrinterStateTracking + */ + public void testStopPrinterStateTracking() throws Exception { + mIPrintManager.startPrinterStateTracking(mGoodPrinterId, mUserId); + mIPrintManager.stopPrinterStateTracking(mGoodPrinterId, mUserId); + + // Stop to track a non-tracked printer is a no-op + mIPrintManager.stopPrinterStateTracking(mGoodPrinterId, mUserId); + + // Bad printers do no cause exceptions + mIPrintManager.startPrinterStateTracking(mBadPrinterId, mUserId); + mIPrintManager.stopPrinterStateTracking(mBadPrinterId, mUserId); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.stopPrinterStateTracking(null, mUserId); + } + }, NullPointerException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } + + /** + * test IPrintManager.destroyPrinterDiscoverySession + */ + public void testDestroyPrinterDiscoverySession() throws Exception { + final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver(); + + mIPrintManager.createPrinterDiscoverySession(listener, mUserId); + mIPrintManager.destroyPrinterDiscoverySession(listener, mUserId); + + // Destroying already destroyed session is a no-op + mIPrintManager.destroyPrinterDiscoverySession(listener, mUserId); + + assertException(new Invokable() { + @Override + public void run() throws Exception { + mIPrintManager.destroyPrinterDiscoverySession(null, mUserId); + } + }, NullPointerException.class); + + // Cannot test bad user Id as these tests are allowed to call across users + } +} diff --git a/core/tests/coretests/src/android/print/PrintTestActivity.java b/core/tests/coretests/src/android/print/PrintTestActivity.java new file mode 100644 index 000000000000..86074a62eab6 --- /dev/null +++ b/core/tests/coretests/src/android/print/PrintTestActivity.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.print; + +import android.app.Activity; +import android.os.Bundle; +import android.view.WindowManager; + +public class PrintTestActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON + | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON + | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); + } +} diff --git a/core/tests/coretests/src/android/print/mockservice/MockPrintService.java b/core/tests/coretests/src/android/print/mockservice/MockPrintService.java new file mode 100644 index 000000000000..9c11c22282d7 --- /dev/null +++ b/core/tests/coretests/src/android/print/mockservice/MockPrintService.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.print.mockservice; + +public class MockPrintService extends StubbablePrintService { + + private static final Object sLock = new Object(); + + private static PrintServiceCallbacks sCallbacks; + + public static void setCallbacks(PrintServiceCallbacks callbacks) { + synchronized (sLock) { + sCallbacks = callbacks; + } + } + + @Override + protected PrintServiceCallbacks getCallbacks() { + synchronized (sLock) { + if (sCallbacks != null) { + sCallbacks.setService(this); + } + return sCallbacks; + } + } +} diff --git a/core/tests/coretests/src/android/print/mockservice/PrintServiceCallbacks.java b/core/tests/coretests/src/android/print/mockservice/PrintServiceCallbacks.java new file mode 100644 index 000000000000..4e892072f0cb --- /dev/null +++ b/core/tests/coretests/src/android/print/mockservice/PrintServiceCallbacks.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.print.mockservice; + +import android.printservice.PrintJob; +import android.printservice.PrintService; + +public abstract class PrintServiceCallbacks { + + private PrintService mService; + + public PrintService getService() { + return mService; + } + + public void setService(PrintService service) { + mService = service; + } + + public abstract PrinterDiscoverySessionCallbacks onCreatePrinterDiscoverySessionCallbacks(); + + public abstract void onRequestCancelPrintJob(PrintJob printJob); + + public abstract void onPrintJobQueued(PrintJob printJob); +} diff --git a/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java b/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java new file mode 100644 index 000000000000..26b7caeba15f --- /dev/null +++ b/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.print.mockservice; + +import android.print.PrinterId; +import android.printservice.CustomPrinterIconCallback; + +import java.util.List; + +public abstract class PrinterDiscoverySessionCallbacks { + + private StubbablePrinterDiscoverySession mSession; + + public void setSession(StubbablePrinterDiscoverySession session) { + mSession = session; + } + + public StubbablePrinterDiscoverySession getSession() { + return mSession; + } + + public abstract void onStartPrinterDiscovery(List<PrinterId> priorityList); + + public abstract void onStopPrinterDiscovery(); + + public abstract void onValidatePrinters(List<PrinterId> printerIds); + + public abstract void onStartPrinterStateTracking(PrinterId printerId); + + public abstract void onRequestCustomPrinterIcon(PrinterId printerId, + CustomPrinterIconCallback callback); + + public abstract void onStopPrinterStateTracking(PrinterId printerId); + + public abstract void onDestroy(); +} diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.aidl b/core/tests/coretests/src/android/print/mockservice/SettingsActivity.java index 6a88b2e2c1ee..fb76e6780374 100644 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.aidl +++ b/core/tests/coretests/src/android/print/mockservice/SettingsActivity.java @@ -1,11 +1,11 @@ -/** - * Copyright (c) 2014, The Android Open Source Project +/* + * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -14,6 +14,15 @@ * limitations under the License. */ -package android.net.wifi.passpoint; +package android.print.mockservice; -parcelable WifiPasspointDmTree; +import android.app.Activity; +import android.os.Bundle; + +public class SettingsActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } +} diff --git a/core/tests/coretests/src/android/print/mockservice/StubbablePrintService.java b/core/tests/coretests/src/android/print/mockservice/StubbablePrintService.java new file mode 100644 index 000000000000..b58b27350c28 --- /dev/null +++ b/core/tests/coretests/src/android/print/mockservice/StubbablePrintService.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.print.mockservice; + +import android.printservice.PrintJob; +import android.printservice.PrintService; +import android.printservice.PrinterDiscoverySession; + +public abstract class StubbablePrintService extends PrintService { + + @Override + public PrinterDiscoverySession onCreatePrinterDiscoverySession() { + PrintServiceCallbacks callbacks = getCallbacks(); + if (callbacks != null) { + return new StubbablePrinterDiscoverySession(this, + getCallbacks().onCreatePrinterDiscoverySessionCallbacks()); + } + return null; + } + + @Override + public void onRequestCancelPrintJob(PrintJob printJob) { + PrintServiceCallbacks callbacks = getCallbacks(); + if (callbacks != null) { + callbacks.onRequestCancelPrintJob(printJob); + } + } + + @Override + public void onPrintJobQueued(PrintJob printJob) { + PrintServiceCallbacks callbacks = getCallbacks(); + if (callbacks != null) { + callbacks.onPrintJobQueued(printJob); + } + } + + protected abstract PrintServiceCallbacks getCallbacks(); +} diff --git a/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java b/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java new file mode 100644 index 000000000000..04683f267aab --- /dev/null +++ b/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.print.mockservice; + +import android.print.PrinterId; +import android.printservice.CustomPrinterIconCallback; +import android.printservice.PrintService; +import android.printservice.PrinterDiscoverySession; + +import java.util.List; + +public class StubbablePrinterDiscoverySession extends PrinterDiscoverySession { + private final PrintService mService; + private final PrinterDiscoverySessionCallbacks mCallbacks; + + public StubbablePrinterDiscoverySession(PrintService service, + PrinterDiscoverySessionCallbacks callbacks) { + mService = service; + mCallbacks = callbacks; + if (mCallbacks != null) { + mCallbacks.setSession(this); + } + } + + public PrintService getService() { + return mService; + } + + @Override + public void onStartPrinterDiscovery(List<PrinterId> priorityList) { + if (mCallbacks != null) { + mCallbacks.onStartPrinterDiscovery(priorityList); + } + } + + @Override + public void onStopPrinterDiscovery() { + if (mCallbacks != null) { + mCallbacks.onStopPrinterDiscovery(); + } + } + + @Override + public void onValidatePrinters(List<PrinterId> printerIds) { + if (mCallbacks != null) { + mCallbacks.onValidatePrinters(printerIds); + } + } + + @Override + public void onStartPrinterStateTracking(PrinterId printerId) { + if (mCallbacks != null) { + mCallbacks.onStartPrinterStateTracking(printerId); + } + } + + @Override + public void onRequestCustomPrinterIcon(PrinterId printerId, + CustomPrinterIconCallback callback) { + if (mCallbacks != null) { + mCallbacks.onRequestCustomPrinterIcon(printerId, callback); + } + } + + @Override + public void onStopPrinterStateTracking(PrinterId printerId) { + if (mCallbacks != null) { + mCallbacks.onStopPrinterStateTracking(printerId); + } + } + + @Override + public void onDestroy() { + if (mCallbacks != null) { + mCallbacks.onDestroy(); + } + } +} diff --git a/docs/html/preview/support.jd b/docs/html/preview/support.jd index cfd94671604f..8c392aac2545 100644 --- a/docs/html/preview/support.jd +++ b/docs/html/preview/support.jd @@ -160,20 +160,21 @@ page.image=images/cards/card-support_16-9_2x.png still perform BTLE and WiFi scans, but only when they are in the foreground. While in the background, those apps will get no results from BTLE and WiFi scans.</li> </ul> </li> - <li>Permission changes + <li>Accessing accounts <ul> - <li>Updated the user interface for permissions and enhanced some of the permissions - behaviors.</li> - <li>The {@link android.Manifest.permission#GET_ACCOUNTS} permission is now a member of the - {@link android.Manifest.permission_group#CONTACTS} permission group and it has a - {@code android:protectionLevel} of {@code dangerous}. This change means that when - targeting Android 6.0 (API level 23), you must check for and request this permission if - your app requires it. + <li>Updated the behavior of {@link android.accounts.AccountManager} account + discovery methods. </li> - - <li>The {@code android.permission.READ_PROFILE} and {@code android.permission.WRITE_PROFILE} - permissions have been removed from the {@link android.Manifest.permission_group#CONTACTS} - permission group. + <li>The GET_ACCOUNTS permission has been deprecated. + </li> + <li>Apps targeting API level 24 should start the intent returned by + newChooseAccountIntent(...) and await the result to acquire a reference + to the user's selected account. AccountManager methods like getAccounts and + related methods will only return those accounts managed by + authenticators that match the signatures of the calling app. + </li> + <li>Apps targeting API level 23 or earlier will continue to behave as + before. </li> </ul> </li> diff --git a/docs/html/training/id-auth/identify.jd b/docs/html/training/id-auth/identify.jd index db9ab3a671e5..4c399f9abf39 100644 --- a/docs/html/training/id-auth/identify.jd +++ b/docs/html/training/id-auth/identify.jd @@ -15,8 +15,7 @@ next.link=authenticate.html <ol> <li><a href="#ForYou">Determine if AccountManager is for You</a></li> <li><a href="#TaskTwo">Decide What Type of Account to Use</a></li> - <li><a href="#GetPermission">Request GET_ACCOUNT permission</a></li> - <li><a href="#TaskFive">Query AccountManager for a List of Accounts</a></li> + <li><a href="#QueryAccounts">Query the user for an Account</a></li> <li><a href="#IdentifyUser">Use the Account Object to Personalize Your App</a></li> <li><a href="#IdIsEnough">Decide Whether an Account Name is Enough</a></li> </ol> @@ -71,48 +70,46 @@ UI.</p> <h2 id="TaskTwo">Decide What Type of Account to Use</h2> <p>Android devices can store multiple accounts from many different providers. -When you query {@link android.accounts.AccountManager} for account names, you can choose to filter -by -account type. The account type is a string that uniquely identifies the entity -that issued the account. For instance, Google accounts have type "com.google," -while Twitter uses "com.twitter.android.auth.login."</p> +When you query {@link android.accounts.AccountManager} for account names, you +can choose to filter by account type. The account type is a string that +uniquely identifies the entity that issued the account. For instance, Google +accounts have type "com.google," while Twitter uses +"com.twitter.android.auth.login."</p> +<h2 id="QueryAccounts">Query the user for an Account</h2> -<h2 id="GetPermission">Request GET_ACCOUNT permission</h2> - -<p>In order to get a list of accounts on the device, your app needs the {@link -android.Manifest.permission#GET_ACCOUNTS} -permission. Add a <a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">{@code -<uses-permission>}</a> tag in your manifest file to request -this permission:</p> +<p>Once an account type has been determined, you can prompt the user with an +account chooser as follows: <pre> -<manifest ... > - <uses-permission android:name="android.permission.GET_ACCOUNTS" /> - ... -</manifest> +AccountManager am = AccountManager.get(this); // "this" reference the current Context +Intent chooserIntent = am.newChooseAccountIntent( + null, // currently select account + null, // list of accounts that are allowed to be shown + new String[] { "com.google" }, // Only allow the user to select Google accounts + false, + null, // description text + null, // add account auth token type + null, // required features for added accounts + null); // options for adding an account +this.startActivityForResult(chooserIntent, MY_REQUEST_CODE); </pre> - -<h2 id="TaskFive">Query AccountManager for a List of Accounts</h2> - -<p>Once you decide what account type you're interested in, you need to query for accounts of that -type. Get an instance of {@link android.accounts.AccountManager} by calling {@link -android.accounts.AccountManager#get(android.content.Context) AccountManager.get()}. Then use that -instance to call {@link android.accounts.AccountManager#getAccountsByType(java.lang.String) -getAccountsByType()}.</p> +<p>Once the chooser intent is started, the user will be presented with a list of +appropriately typed accounts. From this list they will select one which will be +returned to your app upon onActivityResult as follows: <pre> -AccountManager am = AccountManager.get(this); // "this" references the current Context - -Account[] accounts = am.getAccountsByType("com.google"); +protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == MY_REQUEST_CODE && resultCode == RESULT_OK) { + String name = data.getStringExtra(AccountManage.KEY_ACCOUNT_NAME); + String type = data.getStringExtra(AccountManage.KEY_ACCOUNT_TYPE); + Account selectedAccount = new Account(name, type); + doSomethingWithSelectedAccount(selectedAccount); + } +} </pre> -<p>This returns an array of {@link android.accounts.Account} objects. If there's more than one -{@link android.accounts.Account} in -the array, you should present a dialog asking the user to select one.</p> - - <h2 id="IdentifyUser">Use the Account Object to Personalize Your App</h2> <p>The {@link android.accounts.Account} object contains an account name, which for Google accounts diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java index 84ca5464f5cb..1857345968fd 100644 --- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java @@ -19,7 +19,6 @@ import android.animation.AnimatorInflater; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.Animator.AnimatorListener; -import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.res.ColorStateList; @@ -141,15 +140,6 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { /** Local, mutable animator set. */ private final AnimatorSet mAnimatorSet = new AnimatorSet(); - - private final ValueAnimator.AnimatorUpdateListener mUpdateListener = - new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - invalidateSelf(); - } - }; - /** * The resources against which this drawable was created. Used to attempt * to inflate animators if applyTheme() doesn't get called. @@ -211,6 +201,9 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { @Override public void draw(Canvas canvas) { mAnimatedVectorState.mVectorDrawable.draw(canvas); + if (isStarted()) { + invalidateSelf(); + } } @Override @@ -493,7 +486,6 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { * animators, or {@code null} if not available */ public void prepareLocalAnimators(@NonNull AnimatorSet animatorSet, - @NonNull ValueAnimator.AnimatorUpdateListener updateListener, @Nullable Resources res) { // Check for uninflated animators. We can remove this after we add // support for Animator.applyTheme(). See comments in inflate(). @@ -519,17 +511,6 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { final Animator nextAnim = prepareLocalAnimator(i); builder.with(nextAnim); } - - // Setup a value animator to get animation update callbacks. - long totalDuration = animatorSet.getTotalDuration(); - ValueAnimator updateAnim = ValueAnimator.ofFloat(0f, 1f); - if (totalDuration == ValueAnimator.DURATION_INFINITE) { - updateAnim.setRepeatCount(ValueAnimator.INFINITE); - } else { - updateAnim.setDuration(totalDuration); - } - updateAnim.addUpdateListener(updateListener); - builder.with(updateAnim); } } @@ -622,7 +603,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { @NonNull private void ensureAnimatorSet() { if (!mHasAnimatorSet) { - mAnimatedVectorState.prepareLocalAnimators(mAnimatorSet, mUpdateListener, mRes); + mAnimatedVectorState.prepareLocalAnimators(mAnimatorSet, mRes); mHasAnimatorSet = true; mRes = null; } diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 483ccf714b51..fc405541484f 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -4,6 +4,11 @@ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk HWUI_NEW_OPS := true +# Enables fine-grained GLES error checking +# If set to true, every GLES call is wrapped & error checked +# Has moderate overhead +HWUI_ENABLE_OPENGL_VALIDATION := false + hwui_src_files := \ font/CacheTexture.cpp \ font/Font.cpp \ @@ -157,6 +162,13 @@ ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT)) frameworks/rs endif +ifeq (true, $(HWUI_ENABLE_OPENGL_VALIDATION)) + hwui_cflags += -include debug/wrap_gles.h + hwui_src_files += debug/wrap_gles.cpp + hwui_c_includes += frameworks/native/opengl/libs/GLES2 + hwui_cflags += -DDEBUG_OPENGL=3 +endif + # ------------------------ # static library @@ -188,8 +200,8 @@ LOCAL_CFLAGS := \ -DHWUI_NULL_GPU LOCAL_SRC_FILES := \ $(hwui_src_files) \ - tests/common/nullegl.cpp \ - tests/common/nullgles.cpp + debug/nullegl.cpp \ + debug/nullgles.cpp LOCAL_C_INCLUDES := $(hwui_c_includes) $(call hwui_proto_include) LOCAL_EXPORT_C_INCLUDE_DIRS := $(hwui_c_includes) $(call hwui_proto_include) diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp index 00381eea4147..7ecc743b700c 100644 --- a/libs/hwui/BakedOpDispatcher.cpp +++ b/libs/hwui/BakedOpDispatcher.cpp @@ -784,9 +784,7 @@ void BakedOpDispatcher::onCopyFromLayerOp(BakedOpRenderer& renderer, const CopyF .build(); renderer.renderGlop(state, glop); } - GL_CHECKPOINT(); renderer.renderState().layerPool().putOrDelete(*op.layerHandle); - GL_CHECKPOINT(); } } // namespace uirenderer diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp index a808b886a90d..5736c700fe7a 100644 --- a/libs/hwui/BakedOpRenderer.cpp +++ b/libs/hwui/BakedOpRenderer.cpp @@ -49,7 +49,8 @@ void BakedOpRenderer::startRepaintLayer(OffscreenBuffer* offscreenBuffer, const // attach the texture to the FBO glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, offscreenBuffer->texture.id(), 0); - LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "startLayer FAILED"); + GL_CHECKPOINT(LOW); + LOG_ALWAYS_FATAL_IF(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE, "framebuffer incomplete!"); @@ -63,7 +64,7 @@ void BakedOpRenderer::endLayer() { if (mRenderTarget.stencil) { // if stencil was used for clipping, detach it and return it to pool glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0); - LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "glfbrb endlayer failed"); + GL_CHECKPOINT(MODERATE); mCaches.renderBufferCache.put(mRenderTarget.stencil); mRenderTarget.stencil = nullptr; } @@ -74,8 +75,7 @@ void BakedOpRenderer::endLayer() { // Detach the texture from the FBO glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "endLayer FAILED, bound fbo = %u", - mRenderState.getFramebuffer()); + GL_CHECKPOINT(LOW); mRenderState.deleteFramebuffer(mRenderTarget.frameBufferId); mRenderTarget.frameBufferId = 0; } @@ -139,8 +139,6 @@ void BakedOpRenderer::endFrame(const Rect& repaintRect) { mCaches.pathCache.trim(); mCaches.tessellationCache.trim(); - GL_CHECKPOINT(); - #if DEBUG_MEMORY_USAGE mCaches.dumpMemoryUsage(); #else diff --git a/libs/hwui/BufferPool.h b/libs/hwui/BufferPool.h index 9bda2333329d..005b399f603b 100644 --- a/libs/hwui/BufferPool.h +++ b/libs/hwui/BufferPool.h @@ -16,8 +16,9 @@ #pragma once -#include <utils/RefBase.h> -#include <utils/Log.h> +#include "utils/RefBase.h" +#include "utils/Log.h" +#include "utils/Macros.h" #include <atomic> #include <stdint.h> @@ -37,6 +38,7 @@ namespace uirenderer { class BufferPool : public VirtualLightRefBase { public: class Buffer { + PREVENT_COPY_AND_ASSIGN(Buffer); public: int64_t* getBuffer() { return mBuffer.get(); } size_t getSize() { return mSize; } @@ -57,14 +59,17 @@ public: return refs - 1; } + bool isUniqueRef() { + return mRefs.load() == 1; + } + private: friend class BufferPool; - Buffer(BufferPool* pool, size_t size) { + Buffer(BufferPool* pool, size_t size) : mRefs(1) { mSize = size; mBuffer.reset(new int64_t[size]); mPool = pool; - mRefs++; } void setPool(BufferPool* pool) { diff --git a/libs/hwui/Canvas.h b/libs/hwui/Canvas.h index 0643a54c88c5..9dfe454c7bc1 100644 --- a/libs/hwui/Canvas.h +++ b/libs/hwui/Canvas.h @@ -27,6 +27,22 @@ namespace android { +namespace SaveFlags { + +// These must match the corresponding Canvas API constants. +enum { + Matrix = 0x01, + Clip = 0x02, + HasAlphaLayer = 0x04, + ClipToLayer = 0x10, + + // Helper constant + MatrixClip = Matrix | Clip, +}; +typedef uint32_t Flags; + +} // namespace SaveFlags + class ANDROID_API Canvas { public: virtual ~Canvas() {}; @@ -70,16 +86,17 @@ public: // ---------------------------------------------------------------------------- // Canvas state operations // ---------------------------------------------------------------------------- + // Save (layer) virtual int getSaveCount() const = 0; - virtual int save(SkCanvas::SaveFlags flags) = 0; + virtual int save(SaveFlags::Flags flags) = 0; virtual void restore() = 0; virtual void restoreToCount(int saveCount) = 0; virtual int saveLayer(float left, float top, float right, float bottom, - const SkPaint* paint, SkCanvas::SaveFlags flags) = 0; + const SkPaint* paint, SaveFlags::Flags flags) = 0; virtual int saveLayerAlpha(float left, float top, float right, float bottom, - int alpha, SkCanvas::SaveFlags flags) = 0; + int alpha, SaveFlags::Flags flags) = 0; // Matrix virtual void getMatrix(SkMatrix* outMatrix) const = 0; diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp index cf2726b5f530..43ff33f2b997 100644 --- a/libs/hwui/CanvasState.cpp +++ b/libs/hwui/CanvasState.cpp @@ -14,8 +14,7 @@ * limitations under the License. */ -#include <SkCanvas.h> - +#include "Canvas.h" #include "CanvasState.h" #include "utils/MathUtils.h" @@ -54,8 +53,7 @@ void CanvasState::initializeRecordingSaveStack(int viewportWidth, int viewportHe } freeAllSnapshots(); - mSnapshot = allocSnapshot(&mFirstSnapshot, - SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + mSnapshot = allocSnapshot(&mFirstSnapshot, SaveFlags::MatrixClip); mSnapshot->setRelativeLightCenter(Vector3()); mSaveCount = 1; } @@ -72,8 +70,7 @@ void CanvasState::initializeSaveStack( } freeAllSnapshots(); - mSnapshot = allocSnapshot(&mFirstSnapshot, - SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + mSnapshot = allocSnapshot(&mFirstSnapshot, SaveFlags::MatrixClip); mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom); mSnapshot->fbo = mCanvas.getTargetFbo(); mSnapshot->setRelativeLightCenter(lightCenter); diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h index e98fa0440591..748edef730b7 100644 --- a/libs/hwui/Debug.h +++ b/libs/hwui/Debug.h @@ -17,8 +17,18 @@ #ifndef ANDROID_HWUI_DEBUG_H #define ANDROID_HWUI_DEBUG_H +#define DEBUG_LEVEL_HIGH 3 +#define DEBUG_LEVEL_MODERATE 2 +#define DEBUG_LEVEL_LOW 1 +#define DEBUG_LEVEL_NONE 0 + // Turn on to check for OpenGL errors on each frame -#define DEBUG_OPENGL 1 +// Note DEBUG_LEVEL_HIGH for DEBUG_OPENGL is only setable by enabling +// HWUI_ENABLE_OPENGL_VALIDATION when building HWUI. Similarly if +// HWUI_ENABLE_OPENGL_VALIDATION is set then this is always DEBUG_LEVEL_HIGH +#ifndef DEBUG_OPENGL +#define DEBUG_OPENGL DEBUG_LEVEL_LOW +#endif // Turn on to enable initialization information #define DEBUG_INIT 0 diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp index a1825c5bc4c1..1b0f42466bff 100644 --- a/libs/hwui/DeferredDisplayList.cpp +++ b/libs/hwui/DeferredDisplayList.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#include <SkCanvas.h> - #include <utils/Trace.h> #include <ui/Rect.h> #include <ui/Region.h> @@ -419,7 +417,7 @@ void DeferredDisplayList::addClip(OpenGLRenderer& renderer, ClipOp* op) { * beginning of the frame. This would avoid targetting and removing an FBO in the middle of a frame. * * saveLayer operations should be pulled to the beginning of the frame if the canvas doesn't have a - * complex clip, and if the flags (kClip_SaveFlag & kClipToLayer_SaveFlag) are set. + * complex clip, and if the flags (SaveFlags::Clip & SaveFlags::ClipToLayer) are set. */ void DeferredDisplayList::addSaveLayer(OpenGLRenderer& renderer, SaveLayerOp* op, int newSaveCount) { @@ -438,7 +436,7 @@ void DeferredDisplayList::addSave(OpenGLRenderer& renderer, SaveOp* op, int newS int saveFlags = op->getFlags(); DEFER_LOGD("%p adding saveOp %p, flags %x, new count %d", this, op, saveFlags, newSaveCount); - if (recordingComplexClip() && (saveFlags & SkCanvas::kClip_SaveFlag)) { + if (recordingComplexClip() && (saveFlags & SaveFlags::Clip)) { // store and replay the save operation, as it may be needed to correctly playback the clip DEFER_LOGD(" adding save barrier with new save count %d", newSaveCount); storeStateOpBarrier(renderer, op); @@ -621,7 +619,7 @@ void DeferredDisplayList::storeRestoreToCountBarrier(OpenGLRenderer& renderer, S this, newSaveCount, mBatches.size()); // store displayState for the restore operation, as it may be associated with a saveLayer that - // doesn't have kClip_SaveFlag set + // doesn't have SaveFlags::Clip set DeferredDisplayState* state = createState(); renderer.storeDisplayState(*state, getStateOpDeferFlags()); mBatches.push_back(new RestoreToCountBatch(op, state, newSaveCount)); @@ -654,7 +652,7 @@ void DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) { renderer.eventMark("Flush"); // save and restore so that reordering doesn't affect final state - renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + renderer.save(SaveFlags::MatrixClip); if (CC_LIKELY(avoidOverdraw())) { for (unsigned int i = 1; i < mBatches.size(); i++) { diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp index 759c12a3f214..384e64d7700f 100644 --- a/libs/hwui/DisplayListCanvas.cpp +++ b/libs/hwui/DisplayListCanvas.cpp @@ -102,7 +102,7 @@ SkCanvas* DisplayListCanvas::asSkCanvas() { return mSkiaCanvasProxy.get(); } -int DisplayListCanvas::save(SkCanvas::SaveFlags flags) { +int DisplayListCanvas::save(SaveFlags::Flags flags) { addStateOp(new (alloc()) SaveOp((int) flags)); return mState.save((int) flags); } @@ -125,9 +125,9 @@ void DisplayListCanvas::restoreToCount(int saveCount) { } int DisplayListCanvas::saveLayer(float left, float top, float right, float bottom, - const SkPaint* paint, SkCanvas::SaveFlags flags) { + const SkPaint* paint, SaveFlags::Flags flags) { // force matrix/clip isolation for layer - flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag; + flags |= SaveFlags::MatrixClip; paint = refPaint(paint); addStateOp(new (alloc()) SaveLayerOp(left, top, right, bottom, paint, (int) flags)); @@ -232,7 +232,7 @@ void DisplayListCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) void DisplayListCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) { - save(SkCanvas::kMatrix_SaveFlag); + save(SaveFlags::Matrix); translate(left, top); drawBitmap(&bitmap, paint); restore(); @@ -253,7 +253,7 @@ void DisplayListCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matri drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint); } else { - save(SkCanvas::kMatrix_SaveFlag); + save(SaveFlags::Matrix); concat(matrix); drawBitmap(&bitmap, paint); restore(); @@ -269,7 +269,7 @@ void DisplayListCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float && (srcBottom - srcTop == dstBottom - dstTop) && (srcRight - srcLeft == dstRight - dstLeft)) { // transform simple rect to rect drawing case into position bitmap ops, since they merge - save(SkCanvas::kMatrix_SaveFlag); + save(SaveFlags::Matrix); translate(dstLeft, dstTop); drawBitmap(&bitmap, paint); restore(); @@ -283,7 +283,7 @@ void DisplayListCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float // Apply the scale transform on the canvas, so that the shader // effectively calculates positions relative to src rect space - save(SkCanvas::kMatrix_SaveFlag); + save(SaveFlags::Matrix); translate(dstLeft, dstTop); scale(scaleX, scaleY); diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h index 72fc100ebd1d..f1cfa08b3ac2 100644 --- a/libs/hwui/DisplayListCanvas.h +++ b/libs/hwui/DisplayListCanvas.h @@ -128,14 +128,14 @@ public: // ---------------------------------------------------------------------------- // Save (layer) virtual int getSaveCount() const override { return mState.getSaveCount(); } - virtual int save(SkCanvas::SaveFlags flags) override; + virtual int save(SaveFlags::Flags flags) override; virtual void restore() override; virtual void restoreToCount(int saveCount) override; virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint, - SkCanvas::SaveFlags flags) override; + SaveFlags::Flags flags) override; virtual int saveLayerAlpha(float left, float top, float right, float bottom, - int alpha, SkCanvas::SaveFlags flags) override { + int alpha, SaveFlags::Flags flags) override { SkPaint paint; paint.setAlpha(alpha); return saveLayer(left, top, right, bottom, &paint, flags); diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp index c4c655b75d22..a45721249542 100644 --- a/libs/hwui/FrameBuilder.cpp +++ b/libs/hwui/FrameBuilder.cpp @@ -16,6 +16,7 @@ #include "FrameBuilder.h" +#include "Canvas.h" #include "LayerUpdateQueue.h" #include "RenderNode.h" #include "renderstate/OffscreenBufferPool.h" @@ -23,7 +24,6 @@ #include "utils/PaintUtils.h" #include "utils/TraceUtils.h" -#include <SkCanvas.h> #include <SkPathOps.h> #include <utils/TypeHelpers.h> @@ -33,6 +33,15 @@ namespace uirenderer { FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight, const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter) + : FrameBuilder(layers, clip, viewportWidth, viewportHeight, nodes, lightCenter, + Rect(0, 0, 0, 0)) { +} + + +FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip, + uint32_t viewportWidth, uint32_t viewportHeight, + const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter, + const Rect &contentDrawBounds) : mCanvasState(*this) { ATRACE_NAME("prepare drawing commands"); @@ -72,14 +81,56 @@ FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip, } } - // Defer Fbo0 + // It there are multiple render nodes, they are laid out as follows: + // #0 - backdrop (content + caption) + // #1 - content (positioned at (0,0) and clipped to - its bounds mContentDrawBounds) + // #2 - additional overlay nodes + // Usually the backdrop cannot be seen since it will be entirely covered by the content. While + // resizing however it might become partially visible. The following render loop will crop the + // backdrop against the content and draw the remaining part of it. It will then draw the content + // cropped to the backdrop (since that indicates a shrinking of the window). + // + // Additional nodes will be drawn on top with no particular clipping semantics. + + // The bounds of the backdrop against which the content should be clipped. + Rect backdropBounds = contentDrawBounds; + // Usually the contents bounds should be mContentDrawBounds - however - we will + // move it towards the fixed edge to give it a more stable appearance (for the moment). + // If there is no content bounds we ignore the layering as stated above and start with 2. + int layer = (contentDrawBounds.isEmpty() || nodes.size() == 1) ? 2 : 0; + for (const sp<RenderNode>& node : nodes) { if (node->nothingToDraw()) continue; node->computeOrdering(); + int count = mCanvasState.save(SaveFlags::MatrixClip); + + if (layer == 0) { + const RenderProperties& properties = node->properties(); + Rect targetBounds(properties.getLeft(), properties.getTop(), + properties.getRight(), properties.getBottom()); + // Move the content bounds towards the fixed corner of the backdrop. + const int x = targetBounds.left; + const int y = targetBounds.top; + // Remember the intersection of the target bounds and the intersection bounds against + // which we have to crop the content. + backdropBounds.set(x, y, x + backdropBounds.getWidth(), y + backdropBounds.getHeight()); + backdropBounds.doIntersect(targetBounds); + } else if (layer == 1) { + // We shift and clip the content to match its final location in the window. + const float left = contentDrawBounds.left; + const float top = contentDrawBounds.top; + const float dx = backdropBounds.left - left; + const float dy = backdropBounds.top - top; + const float width = backdropBounds.getWidth(); + const float height = backdropBounds.getHeight(); + mCanvasState.translate(dx, dy); + // It gets cropped against the bounds of the backdrop to stay inside. + mCanvasState.clipRect(left, top, left + width, top + height, SkRegion::kIntersect_Op); + } - int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag); deferNodePropsAndOps(*node); mCanvasState.restoreToCount(count); + layer++; } } @@ -327,7 +378,7 @@ void FrameBuilder::deferShadow(const RenderNodeOp& casterNodeOp) { void FrameBuilder::deferProjectedChildren(const RenderNode& renderNode) { const SkPath* projectionReceiverOutline = renderNode.properties().getOutline().getPath(); - int count = mCanvasState.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + int count = mCanvasState.save(SaveFlags::MatrixClip); // can't be null, since DL=null node rejection happens before deferNodePropsAndOps const DisplayList& displayList = *(renderNode.getDisplayList()); @@ -348,7 +399,7 @@ void FrameBuilder::deferProjectedChildren(const RenderNode& renderNode) { for (size_t i = 0; i < renderNode.mProjectedNodes.size(); i++) { RenderNodeOp* childOp = renderNode.mProjectedNodes[i]; - int restoreTo = mCanvasState.save(SkCanvas::kMatrix_SaveFlag); + int restoreTo = mCanvasState.save(SaveFlags::Matrix); mCanvasState.concatMatrix(childOp->transformFromCompositingAncestor); deferRenderNodeOpImpl(*childOp); mCanvasState.restoreToCount(restoreTo); @@ -392,7 +443,7 @@ void FrameBuilder::deferNodeOps(const RenderNode& renderNode) { void FrameBuilder::deferRenderNodeOpImpl(const RenderNodeOp& op) { if (op.renderNode->nothingToDraw()) return; - int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag); + int count = mCanvasState.save(SaveFlags::MatrixClip); // apply state from RecordedOp (clip first, since op's clip is transformed by current matrix) mCanvasState.writableSnapshot()->mutateClipArea().applyClip(op.localClip, @@ -450,6 +501,10 @@ void FrameBuilder::deferBitmapOp(const BitmapOp& op) { BakedOpState* bakedState = tryBakeOpState(op); if (!bakedState) return; // quick rejected + currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap); + + // TODO: Fix this ( b/26569206 ) +/* // Don't merge non-simply transformed or neg scale ops, SET_TEXTURE doesn't handle rotation // Don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in // MergingDrawBatch::canMergeWith() @@ -464,6 +519,7 @@ void FrameBuilder::deferBitmapOp(const BitmapOp& op) { } else { currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap); } +*/ } void FrameBuilder::deferBitmapMeshOp(const BitmapMeshOp& op) { @@ -597,7 +653,7 @@ void FrameBuilder::saveForLayer(uint32_t layerWidth, uint32_t layerHeight, const Rect& repaintRect, const Vector3& lightCenter, const BeginLayerOp* beginLayerOp, RenderNode* renderNode) { - mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag); + mCanvasState.save(SaveFlags::MatrixClip); mCanvasState.writableSnapshot()->initializeViewport(layerWidth, layerHeight); mCanvasState.writableSnapshot()->roundRectClipState = nullptr; mCanvasState.writableSnapshot()->setRelativeLightCenter(lightCenter); diff --git a/libs/hwui/FrameBuilder.h b/libs/hwui/FrameBuilder.h index bd0185017f52..dea9934e9a1b 100644 --- a/libs/hwui/FrameBuilder.h +++ b/libs/hwui/FrameBuilder.h @@ -59,6 +59,11 @@ public: uint32_t viewportWidth, uint32_t viewportHeight, const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter); + FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip, + uint32_t viewportWidth, uint32_t viewportHeight, + const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter, + const Rect &contentDrawBounds); + virtual ~FrameBuilder() {} /** @@ -100,30 +105,30 @@ public: // Relay through layers in reverse order, since layers // later in the list will be drawn by earlier ones for (int i = mLayerBuilders.size() - 1; i >= 1; i--) { - GL_CHECKPOINT(); + GL_CHECKPOINT(MODERATE); LayerBuilder& layer = *(mLayerBuilders[i]); if (layer.renderNode) { // cached HW layer - can't skip layer if empty renderer.startRepaintLayer(layer.offscreenBuffer, layer.repaintRect); - GL_CHECKPOINT(); + GL_CHECKPOINT(MODERATE); layer.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers); - GL_CHECKPOINT(); + GL_CHECKPOINT(MODERATE); renderer.endLayer(); } else if (!layer.empty()) { // save layer - skip entire layer if empty layer.offscreenBuffer = renderer.startTemporaryLayer(layer.width, layer.height); - GL_CHECKPOINT(); + GL_CHECKPOINT(MODERATE); layer.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers); - GL_CHECKPOINT(); + GL_CHECKPOINT(MODERATE); renderer.endLayer(); } } - GL_CHECKPOINT(); + GL_CHECKPOINT(MODERATE); const LayerBuilder& fbo0 = *(mLayerBuilders[0]); renderer.startFrame(fbo0.width, fbo0.height, fbo0.repaintRect); - GL_CHECKPOINT(); + GL_CHECKPOINT(MODERATE); fbo0.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers); - GL_CHECKPOINT(); + GL_CHECKPOINT(MODERATE); renderer.endFrame(fbo0.repaintRect); } diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index 0f219e4792f6..3123e8e06ae7 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -407,7 +407,6 @@ bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* renderState.bindFramebuffer(fbo); glGenTextures(1, &texture); - GL_CHECKPOINT(); caches.textureState().activateTexture(0); caches.textureState().bindTexture(texture); @@ -422,11 +421,9 @@ bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* glTexImage2D(GL_TEXTURE_2D, 0, format, bitmap->width(), bitmap->height(), 0, format, type, nullptr); - GL_CHECKPOINT(); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); - GL_CHECKPOINT(); { LayerRenderer renderer(renderState, layer); @@ -437,8 +434,6 @@ bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* renderer.translate(0.0f, bitmap->height()); renderer.scale(1.0f, -1.0f); - GL_CHECKPOINT(); - { Rect bounds; bounds.set(0.0f, 0.0f, bitmap->width(), bitmap->height()); @@ -447,7 +442,6 @@ bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* glReadPixels(0, 0, bitmap->width(), bitmap->height(), format, type, bitmap->getPixels()); - GL_CHECKPOINT(); } status = true; @@ -460,6 +454,8 @@ bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* renderState.deleteFramebuffer(fbo); renderState.setViewport(previousViewportWidth, previousViewportHeight); + GL_CHECKPOINT(MODERATE); + return status; } return false; diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index db017feff577..6c2e244c5f6c 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -17,6 +17,7 @@ #include <GpuMemoryTracker.h> #include "OpenGLRenderer.h" +#include "Canvas.h" #include "DeferredDisplayList.h" #include "GammaFontRenderer.h" #include "Glop.h" @@ -39,7 +40,6 @@ #include <stdint.h> #include <sys/types.h> -#include <SkCanvas.h> #include <SkColor.h> #include <SkPaintDefaults.h> #include <SkPathOps.h> @@ -195,7 +195,7 @@ bool OpenGLRenderer::finish() { } if (!suppressErrorChecks()) { - GL_CHECKPOINT(); + GL_CHECKPOINT(MODERATE); #if DEBUG_MEMORY_USAGE mCaches.dumpMemoryUsage(); @@ -472,7 +472,7 @@ void OpenGLRenderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom, const SkPaint* paint, int flags, const SkPath* convexMask) { // force matrix/clip isolation for layer - flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag; + flags |= SaveFlags::MatrixClip; const int count = mState.saveSnapshot(flags); @@ -531,7 +531,7 @@ int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float const SkPaint* paint, int flags) { const int count = mState.saveSnapshot(flags); - if (!mState.currentlyIgnored() && (flags & SkCanvas::kClipToLayer_SaveFlag)) { + if (!mState.currentlyIgnored() && (flags & SaveFlags::ClipToLayer)) { // initialize the snapshot as though it almost represents an FBO layer so deferred draw // operations will be able to store and restore the current clip and transform info, and // quick rejection will be correct (for display lists) @@ -558,7 +558,7 @@ int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float * and the frame buffer still receive every drawing command. For instance, if a * layer is created and a shape intersecting the bounds of the layers and the * framebuffer is draw, the shape will be drawn on both (unless the layer was - * created with the SkCanvas::kClipToLayer_SaveFlag flag.) + * created with the SaveFlags::ClipToLayer flag.) * * A way to implement layers is to create an FBO for each layer, backed by an RGBA * texture. Unfortunately, this is inefficient as it requires every primitive to @@ -608,7 +608,7 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto LAYER_LOGD("Requesting layer %.2fx%.2f", right - left, bottom - top); LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize()); - const bool fboLayer = flags & SkCanvas::kClipToLayer_SaveFlag; + const bool fboLayer = flags & SaveFlags::ClipToLayer; // Window coordinates of the layer Rect clip; @@ -890,7 +890,7 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { if (CC_UNLIKELY(layer->region.isEmpty())) return; // nothing to draw if (layer->getConvexMask()) { - save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag); + save(SaveFlags::MatrixClip); // clip to the area of the layer the mask can be larger clipRect(rect.left, rect.top, rect.right, rect.bottom, SkRegion::kIntersect_Op); @@ -2233,7 +2233,7 @@ void OpenGLRenderer::drawLayer(Layer* layer) { if (layer->isTextureLayer()) { transform = &layer->getTransform(); if (!transform->isIdentity()) { - save(SkCanvas::kMatrix_SaveFlag); + save(SaveFlags::Matrix); concatMatrix(*transform); } } diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 78855e5d6a0a..328e291a60af 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -92,7 +92,7 @@ void RecordingCanvas::onSnapshotRestored(const Snapshot& removed, const Snapshot // android/graphics/Canvas state operations // ---------------------------------------------------------------------------- // Save (layer) -int RecordingCanvas::save(SkCanvas::SaveFlags flags) { +int RecordingCanvas::save(SaveFlags::Flags flags) { return mState.save((int) flags); } @@ -105,10 +105,10 @@ void RecordingCanvas::restoreToCount(int saveCount) { } int RecordingCanvas::saveLayer(float left, float top, float right, float bottom, - const SkPaint* paint, SkCanvas::SaveFlags flags) { + const SkPaint* paint, SaveFlags::Flags flags) { // force matrix/clip isolation for layer - flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag; - bool clippedLayer = flags & SkCanvas::kClipToLayer_SaveFlag; + flags |= SaveFlags::MatrixClip; + bool clippedLayer = flags & SaveFlags::ClipToLayer; const Snapshot& previous = *mState.currentSnapshot(); @@ -128,7 +128,7 @@ int RecordingCanvas::saveLayer(float left, float top, float right, float bottom, // unlikely case where an unclipped savelayer is recorded with a clip it can use, // as none of its unaffected/unclipped area is visible clippedLayer = true; - flags |= SkCanvas::kClipToLayer_SaveFlag; + flags |= SaveFlags::ClipToLayer; } visibleBounds.doIntersect(previous.getRenderTargetClip()); @@ -424,7 +424,7 @@ void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) { // Bitmap-based void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) { - save(SkCanvas::kMatrix_SaveFlag); + save(SaveFlags::Matrix); translate(left, top); drawBitmap(&bitmap, paint); restore(); @@ -445,7 +445,7 @@ void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint); } else { - save(SkCanvas::kMatrix_SaveFlag); + save(SaveFlags::Matrix); concat(matrix); drawBitmap(&bitmap, paint); restore(); @@ -461,7 +461,7 @@ void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float sr && (srcBottom - srcTop == dstBottom - dstTop) && (srcRight - srcLeft == dstRight - dstLeft)) { // transform simple rect to rect drawing case into position bitmap ops, since they merge - save(SkCanvas::kMatrix_SaveFlag); + save(SaveFlags::Matrix); translate(dstLeft, dstTop); drawBitmap(&bitmap, paint); restore(); diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 8aa750602972..786f96e852ec 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -108,14 +108,14 @@ public: // ---------------------------------------------------------------------------- // Save (layer) virtual int getSaveCount() const override { return mState.getSaveCount(); } - virtual int save(SkCanvas::SaveFlags flags) override; + virtual int save(SaveFlags::Flags flags) override; virtual void restore() override; virtual void restoreToCount(int saveCount) override; virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint, - SkCanvas::SaveFlags flags) override; + SaveFlags::Flags flags) override; virtual int saveLayerAlpha(float left, float top, float right, float bottom, - int alpha, SkCanvas::SaveFlags flags) override { + int alpha, SaveFlags::Flags flags) override { SkPaint paint; paint.setAlpha(alpha); return saveLayer(left, top, right, bottom, &paint, flags); diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index ae690fdef4c7..d4588edea207 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -33,8 +33,6 @@ #include "protos/hwui.pb.h" #include "protos/ProtoHelpers.h" -#include <SkCanvas.h> - #include <algorithm> #include <sstream> #include <string> @@ -105,8 +103,7 @@ void RenderNode::output(uint32_t level) { (isRenderable() ? "" : ", empty"), (properties().getProjectBackwards() ? ", projected" : ""), (mLayer != nullptr ? ", on HW Layer" : "")); - ALOGD("%*s%s %d", level * 2, "", "Save", - SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + ALOGD("%*s%s %d", level * 2, "", "Save", SaveFlags::MatrixClip); properties().debugOutputProperties(level); @@ -574,7 +571,7 @@ void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) { layerBounds.left, layerBounds.top, layerBounds.right, layerBounds.bottom, (int) (properties().getAlpha() * 255), - SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kClipToLayer_SaveFlag); + SaveFlags::HasAlphaLayer | SaveFlags::ClipToLayer); handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds()); } @@ -875,7 +872,7 @@ void RenderNode::issueOperationsOf3dChildren(ChildrenSelectMode mode, // Apply the base transform of the parent of the 3d children. This isolates // 3d children of the current chunk from transformations made in previous chunks. - int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag); + int rootRestoreTo = renderer.save(SaveFlags::Matrix); renderer.setGlobalMatrix(initialTransform); /** @@ -919,7 +916,7 @@ void RenderNode::issueOperationsOf3dChildren(ChildrenSelectMode mode, // only the actual child DL draw needs to be in save/restore, // since it modifies the renderer's matrix - int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag); + int restoreTo = renderer.save(SaveFlags::Matrix); DrawRenderNodeOp* childOp = zTranslatedNodes[drawIndex].value; @@ -941,7 +938,7 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& int restoreTo = renderer.getSaveCount(); LinearAllocator& alloc = handler.allocator(); - handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag), + handler(new (alloc) SaveOp(SaveFlags::MatrixClip), PROPERTY_SAVECOUNT, properties().getClipToBounds()); // Transform renderer to match background we're projecting onto @@ -966,7 +963,7 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& renderNodeOp_t* childOp = mProjectedNodes[i]; // matrix save, concat, and restore can be done safely without allocating operations - int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag); + int restoreTo = renderer.save(SaveFlags::Matrix); renderer.concatMatrix(childOp->transformFromCompositingAncestor); childOp->skipInOrderDraw = false; // this is horrible, I'm so sorry everyone handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds()); @@ -1027,11 +1024,11 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { LinearAllocator& alloc = handler.allocator(); int restoreTo = renderer.getSaveCount(); - handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag), + handler(new (alloc) SaveOp(SaveFlags::MatrixClip), PROPERTY_SAVECOUNT, properties().getClipToBounds()); DISPLAY_LIST_LOGD("%*sSave %d %d", (handler.level() + 1) * 2, "", - SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo); + SaveFlags::MatrixClip, restoreTo); if (useViewProperties) { setViewProperties<T>(renderer, handler); diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp index ce1bd6ab8b03..b848af4e7b1e 100644 --- a/libs/hwui/RenderProperties.cpp +++ b/libs/hwui/RenderProperties.cpp @@ -18,12 +18,12 @@ #include <utils/Trace.h> -#include <SkCanvas.h> #include <SkColorFilter.h> #include <SkMatrix.h> #include <SkPath.h> #include <SkPathOps.h> +#include "Canvas.h" #include "Matrix.h" #include "OpenGLRenderer.h" #include "utils/MathUtils.h" @@ -144,7 +144,7 @@ void RenderProperties::debugOutputProperties(const int level) const { (int)layerBounds.left, (int)layerBounds.top, (int)layerBounds.right, (int)layerBounds.bottom, (int)(mPrimitiveFields.mAlpha * 255), - SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kClipToLayer_SaveFlag); + SaveFlags::HasAlphaLayer | SaveFlags::ClipToLayer); } diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 96c1a7c18db9..20e7c711a9ce 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -24,6 +24,7 @@ #include <SkGraphics.h> #include <SkShader.h> #include <SkTArray.h> +#include <SkTLazy.h> #include <SkTemplates.h> #include <memory> @@ -63,14 +64,14 @@ public: virtual bool isHighContrastText() override { return mHighContrastText; } virtual int getSaveCount() const override; - virtual int save(SkCanvas::SaveFlags flags) override; + virtual int save(SaveFlags::Flags flags) override; virtual void restore() override; virtual void restoreToCount(int saveCount) override; virtual int saveLayer(float left, float top, float right, float bottom, - const SkPaint* paint, SkCanvas::SaveFlags flags) override; + const SkPaint* paint, SaveFlags::Flags flags) override; virtual int saveLayerAlpha(float left, float top, float right, float bottom, - int alpha, SkCanvas::SaveFlags flags) override; + int alpha, SaveFlags::Flags flags) override; virtual void getMatrix(SkMatrix* outMatrix) const override; virtual void setMatrix(const SkMatrix& matrix) override; @@ -138,13 +139,13 @@ public: private: struct SaveRec { - int saveCount; - SkCanvas::SaveFlags saveFlags; + int saveCount; + SaveFlags::Flags saveFlags; }; bool mHighContrastText = false; - void recordPartialSave(SkCanvas::SaveFlags flags); + void recordPartialSave(SaveFlags::Flags flags); void saveClipsForFrame(SkTArray<SkClipStack::Element>& clips, int frameSaveCount); void applyClips(const SkTArray<SkClipStack::Element>& clips); @@ -231,7 +232,7 @@ int SkiaCanvas::getSaveCount() const { return mCanvas->getSaveCount(); } -int SkiaCanvas::save(SkCanvas::SaveFlags flags) { +int SkiaCanvas::save(SaveFlags::Flags flags) { int count = mCanvas->save(); recordPartialSave(flags); return count; @@ -254,8 +255,8 @@ void SkiaCanvas::restore() { return; } - bool preserveMatrix = !(rec->saveFlags & SkCanvas::kMatrix_SaveFlag); - bool preserveClip = !(rec->saveFlags & SkCanvas::kClip_SaveFlag); + bool preserveMatrix = !(rec->saveFlags & SaveFlags::Matrix); + bool preserveClip = !(rec->saveFlags & SaveFlags::Clip); SkMatrix savedMatrix; if (preserveMatrix) { @@ -291,34 +292,53 @@ void SkiaCanvas::restoreToCount(int restoreCount) { } } +static inline SkCanvas::SaveLayerFlags layerFlags(SaveFlags::Flags flags) { + SkCanvas::SaveLayerFlags layerFlags = 0; + + if (!(flags & SaveFlags::HasAlphaLayer)) { + layerFlags |= SkCanvas::kIsOpaque_SaveLayerFlag; + } + + if (!(flags & SaveFlags::ClipToLayer)) { + layerFlags |= SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag; + } + + return layerFlags; +} + int SkiaCanvas::saveLayer(float left, float top, float right, float bottom, - const SkPaint* paint, SkCanvas::SaveFlags flags) { - SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom); - int count = mCanvas->saveLayer(&bounds, paint, flags | SkCanvas::kMatrixClip_SaveFlag); + const SkPaint* paint, SaveFlags::Flags flags) { + const SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom); + const SkCanvas::SaveLayerRec rec(&bounds, paint, layerFlags(flags)); + + int count = mCanvas->saveLayer(rec); recordPartialSave(flags); return count; } int SkiaCanvas::saveLayerAlpha(float left, float top, float right, float bottom, - int alpha, SkCanvas::SaveFlags flags) { - SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom); - int count = mCanvas->saveLayerAlpha(&bounds, alpha, flags | SkCanvas::kMatrixClip_SaveFlag); - recordPartialSave(flags); - return count; + int alpha, SaveFlags::Flags flags) { + SkTLazy<SkPaint> alphaPaint; + if (static_cast<unsigned>(alpha) < 0xFF) { + alphaPaint.init()->setAlpha(alpha); + } + + return this->saveLayer(left, top, right, bottom, alphaPaint.getMaybeNull(), + flags); } // ---------------------------------------------------------------------------- // functions to emulate legacy SaveFlags (i.e. independent matrix/clip flags) // ---------------------------------------------------------------------------- -void SkiaCanvas::recordPartialSave(SkCanvas::SaveFlags flags) { +void SkiaCanvas::recordPartialSave(SaveFlags::Flags flags) { // A partial save is a save operation which doesn't capture the full canvas state. - // (either kMatrix_SaveFlags or kClip_SaveFlag is missing). + // (either SaveFlags::Matrix or SaveFlags::Clip is missing). // Mask-out non canvas state bits. - flags = static_cast<SkCanvas::SaveFlags>(flags & SkCanvas::kMatrixClip_SaveFlag); + flags &= SaveFlags::MatrixClip; - if (SkCanvas::kMatrixClip_SaveFlag == flags) { + if (flags == SaveFlags::MatrixClip) { // not a partial save. return; } diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp index 976f77518337..6530d4ed8d50 100644 --- a/libs/hwui/SkiaCanvasProxy.cpp +++ b/libs/hwui/SkiaCanvasProxy.cpp @@ -159,7 +159,21 @@ SkSurface* SkiaCanvasProxy::onNewSurface(const SkImageInfo&, const SkSurfaceProp } void SkiaCanvasProxy::willSave() { - mCanvas->save(SkCanvas::kMatrixClip_SaveFlag); + mCanvas->save(android::SaveFlags::MatrixClip); +} + +static inline SaveFlags::Flags saveFlags(SkCanvas::SaveLayerFlags layerFlags) { + SaveFlags::Flags saveFlags = 0; + + if (!(layerFlags & SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag)) { + saveFlags |= SaveFlags::ClipToLayer; + } + + if (!(layerFlags & SkCanvas::kIsOpaque_SaveLayerFlag)) { + saveFlags |= SaveFlags::HasAlphaLayer; + } + + return saveFlags; } SkCanvas::SaveLayerStrategy SkiaCanvasProxy::getSaveLayerStrategy(const SaveLayerRec& saveLayerRec) { @@ -170,7 +184,7 @@ SkCanvas::SaveLayerStrategy SkiaCanvasProxy::getSaveLayerStrategy(const SaveLaye rect = SkRect::MakeEmpty(); } mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, saveLayerRec.fPaint, - (SkCanvas::SaveFlags) SaveLayerFlagsToSaveFlags(saveLayerRec.fSaveLayerFlags)); + saveFlags(saveLayerRec.fSaveLayerFlags)); return SkCanvas::kNoLayer_SaveLayerStrategy; } diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp index c6d89775390b..27fea1ff59f5 100644 --- a/libs/hwui/Snapshot.cpp +++ b/libs/hwui/Snapshot.cpp @@ -16,7 +16,7 @@ #include "Snapshot.h" -#include <SkCanvas.h> +#include "Canvas.h" namespace android { namespace uirenderer { @@ -57,14 +57,14 @@ Snapshot::Snapshot(Snapshot* s, int saveFlags) , mClipArea(nullptr) , mViewportData(s->mViewportData) , mRelativeLightCenter(s->mRelativeLightCenter) { - if (saveFlags & SkCanvas::kMatrix_SaveFlag) { + if (saveFlags & SaveFlags::Matrix) { mTransformRoot = *s->transform; transform = &mTransformRoot; } else { transform = s->transform; } - if (saveFlags & SkCanvas::kClip_SaveFlag) { + if (saveFlags & SaveFlags::Clip) { mClipAreaRoot = s->getClipArea(); mClipArea = &mClipAreaRoot; } else { diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp index 5046d37150fe..c09b6dd89e4e 100644 --- a/libs/hwui/Texture.cpp +++ b/libs/hwui/Texture.cpp @@ -97,7 +97,7 @@ void Texture::resetCachedParams() { void Texture::upload(GLint internalformat, uint32_t width, uint32_t height, GLenum format, GLenum type, const void* pixels) { - GL_CHECKPOINT(); + GL_CHECKPOINT(MODERATE); bool needsAlloc = updateSize(width, height, internalformat); if (!mId) { glGenTextures(1, &mId); @@ -112,7 +112,7 @@ void Texture::upload(GLint internalformat, uint32_t width, uint32_t height, glTexSubImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0, format, type, pixels); } - GL_CHECKPOINT(); + GL_CHECKPOINT(MODERATE); } static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei stride, GLsizei bpp, diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index 3e20608dc4cc..1d31c9e08a9e 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -420,7 +420,7 @@ void Tree::draw(Canvas* outCanvas, SkColorFilter* colorFilter, return; } - int saveCount = outCanvas->save(SkCanvas::SaveFlags::kMatrixClip_SaveFlag); + int saveCount = outCanvas->save(SaveFlags::MatrixClip); outCanvas->translate(mBounds.fLeft, mBounds.fTop); // Handle RTL mirroring. diff --git a/libs/hwui/tests/common/nullegl.cpp b/libs/hwui/debug/nullegl.cpp index b6cc2f247627..b6cc2f247627 100644 --- a/libs/hwui/tests/common/nullegl.cpp +++ b/libs/hwui/debug/nullegl.cpp diff --git a/libs/hwui/tests/common/nullgles.cpp b/libs/hwui/debug/nullgles.cpp index f8e8c98c20ba..ffb064942911 100644 --- a/libs/hwui/tests/common/nullgles.cpp +++ b/libs/hwui/debug/nullgles.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "unwrap_gles.h" + #include <GLES3/gl3.h> #include <GLES2/gl2ext.h> diff --git a/libs/hwui/debug/unwrap_gles.h b/libs/hwui/debug/unwrap_gles.h new file mode 100644 index 000000000000..7716a735a63b --- /dev/null +++ b/libs/hwui/debug/unwrap_gles.h @@ -0,0 +1,918 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HWUI_GLES_WRAP_ENABLED +#undef HWUI_GLES_WRAP_ENABLED + +#undef glActiveShaderProgram +#undef glActiveShaderProgramEXT +#undef glActiveTexture +#undef glAlphaFunc +#undef glAlphaFuncQCOM +#undef glAlphaFuncx +#undef glAlphaFuncxOES +#undef glApplyFramebufferAttachmentCMAAINTEL +#undef glAttachShader +#undef glBeginConditionalRenderNV +#undef glBeginPerfMonitorAMD +#undef glBeginPerfQueryINTEL +#undef glBeginQuery +#undef glBeginQueryEXT +#undef glBeginTransformFeedback +#undef glBindAttribLocation +#undef glBindBuffer +#undef glBindBufferBase +#undef glBindBufferRange +#undef glBindFragDataLocationEXT +#undef glBindFragDataLocationIndexedEXT +#undef glBindFramebuffer +#undef glBindFramebufferOES +#undef glBindImageTexture +#undef glBindProgramPipeline +#undef glBindProgramPipelineEXT +#undef glBindRenderbuffer +#undef glBindRenderbufferOES +#undef glBindSampler +#undef glBindTexture +#undef glBindTransformFeedback +#undef glBindVertexArray +#undef glBindVertexArrayOES +#undef glBindVertexBuffer +#undef glBlendBarrier +#undef glBlendBarrierKHR +#undef glBlendBarrierNV +#undef glBlendColor +#undef glBlendEquation +#undef glBlendEquationOES +#undef glBlendEquationSeparate +#undef glBlendEquationSeparateOES +#undef glBlendEquationSeparatei +#undef glBlendEquationSeparateiEXT +#undef glBlendEquationSeparateiOES +#undef glBlendEquationi +#undef glBlendEquationiEXT +#undef glBlendEquationiOES +#undef glBlendFunc +#undef glBlendFuncSeparate +#undef glBlendFuncSeparateOES +#undef glBlendFuncSeparatei +#undef glBlendFuncSeparateiEXT +#undef glBlendFuncSeparateiOES +#undef glBlendFunci +#undef glBlendFunciEXT +#undef glBlendFunciOES +#undef glBlendParameteriNV +#undef glBlitFramebuffer +#undef glBlitFramebufferANGLE +#undef glBlitFramebufferNV +#undef glBufferData +#undef glBufferStorageEXT +#undef glBufferSubData +#undef glCheckFramebufferStatus +#undef glCheckFramebufferStatusOES +#undef glClear +#undef glClearBufferfi +#undef glClearBufferfv +#undef glClearBufferiv +#undef glClearBufferuiv +#undef glClearColor +#undef glClearColorx +#undef glClearColorxOES +#undef glClearDepthf +#undef glClearDepthfOES +#undef glClearDepthx +#undef glClearDepthxOES +#undef glClearStencil +#undef glClientActiveTexture +#undef glClientWaitSync +#undef glClientWaitSyncAPPLE +#undef glClipPlanef +#undef glClipPlanefIMG +#undef glClipPlanefOES +#undef glClipPlanex +#undef glClipPlanexIMG +#undef glClipPlanexOES +#undef glColor4f +#undef glColor4ub +#undef glColor4x +#undef glColor4xOES +#undef glColorMask +#undef glColorMaski +#undef glColorMaskiEXT +#undef glColorMaskiOES +#undef glColorPointer +#undef glCompileShader +#undef glCompressedTexImage2D +#undef glCompressedTexImage3D +#undef glCompressedTexImage3DOES +#undef glCompressedTexSubImage2D +#undef glCompressedTexSubImage3D +#undef glCompressedTexSubImage3DOES +#undef glCopyBufferSubData +#undef glCopyBufferSubDataNV +#undef glCopyImageSubData +#undef glCopyImageSubDataEXT +#undef glCopyImageSubDataOES +#undef glCopyPathNV +#undef glCopyTexImage2D +#undef glCopyTexSubImage2D +#undef glCopyTexSubImage3D +#undef glCopyTexSubImage3DOES +#undef glCopyTextureLevelsAPPLE +#undef glCoverFillPathInstancedNV +#undef glCoverFillPathNV +#undef glCoverStrokePathInstancedNV +#undef glCoverStrokePathNV +#undef glCoverageMaskNV +#undef glCoverageModulationNV +#undef glCoverageModulationTableNV +#undef glCoverageOperationNV +#undef glCreatePerfQueryINTEL +#undef glCreateProgram +#undef glCreateShader +#undef glCreateShaderProgramv +#undef glCreateShaderProgramvEXT +#undef glCullFace +#undef glCurrentPaletteMatrixOES +#undef glDebugMessageCallback +#undef glDebugMessageCallbackKHR +#undef glDebugMessageControl +#undef glDebugMessageControlKHR +#undef glDebugMessageInsert +#undef glDebugMessageInsertKHR +#undef glDeleteBuffers +#undef glDeleteFencesNV +#undef glDeleteFramebuffers +#undef glDeleteFramebuffersOES +#undef glDeletePathsNV +#undef glDeletePerfMonitorsAMD +#undef glDeletePerfQueryINTEL +#undef glDeleteProgram +#undef glDeleteProgramPipelines +#undef glDeleteProgramPipelinesEXT +#undef glDeleteQueries +#undef glDeleteQueriesEXT +#undef glDeleteRenderbuffers +#undef glDeleteRenderbuffersOES +#undef glDeleteSamplers +#undef glDeleteShader +#undef glDeleteSync +#undef glDeleteSyncAPPLE +#undef glDeleteTextures +#undef glDeleteTransformFeedbacks +#undef glDeleteVertexArrays +#undef glDeleteVertexArraysOES +#undef glDepthFunc +#undef glDepthMask +#undef glDepthRangeArrayfvNV +#undef glDepthRangeIndexedfNV +#undef glDepthRangef +#undef glDepthRangefOES +#undef glDepthRangex +#undef glDepthRangexOES +#undef glDetachShader +#undef glDisable +#undef glDisableClientState +#undef glDisableDriverControlQCOM +#undef glDisableVertexAttribArray +#undef glDisablei +#undef glDisableiEXT +#undef glDisableiNV +#undef glDisableiOES +#undef glDiscardFramebufferEXT +#undef glDispatchCompute +#undef glDispatchComputeIndirect +#undef glDrawArrays +#undef glDrawArraysIndirect +#undef glDrawArraysInstanced +#undef glDrawArraysInstancedANGLE +#undef glDrawArraysInstancedBaseInstanceEXT +#undef glDrawArraysInstancedEXT +#undef glDrawArraysInstancedNV +#undef glDrawBuffers +#undef glDrawBuffersEXT +#undef glDrawBuffersIndexedEXT +#undef glDrawBuffersNV +#undef glDrawElements +#undef glDrawElementsBaseVertex +#undef glDrawElementsBaseVertexEXT +#undef glDrawElementsBaseVertexOES +#undef glDrawElementsIndirect +#undef glDrawElementsInstanced +#undef glDrawElementsInstancedANGLE +#undef glDrawElementsInstancedBaseInstanceEXT +#undef glDrawElementsInstancedBaseVertex +#undef glDrawElementsInstancedBaseVertexBaseInstanceEXT +#undef glDrawElementsInstancedBaseVertexEXT +#undef glDrawElementsInstancedBaseVertexOES +#undef glDrawElementsInstancedEXT +#undef glDrawElementsInstancedNV +#undef glDrawRangeElements +#undef glDrawRangeElementsBaseVertex +#undef glDrawRangeElementsBaseVertexEXT +#undef glDrawRangeElementsBaseVertexOES +#undef glDrawTexfOES +#undef glDrawTexfvOES +#undef glDrawTexiOES +#undef glDrawTexivOES +#undef glDrawTexsOES +#undef glDrawTexsvOES +#undef glDrawTexxOES +#undef glDrawTexxvOES +#undef glEGLImageTargetRenderbufferStorageOES +#undef glEGLImageTargetTexture2DOES +#undef glEnable +#undef glEnableClientState +#undef glEnableDriverControlQCOM +#undef glEnableVertexAttribArray +#undef glEnablei +#undef glEnableiEXT +#undef glEnableiNV +#undef glEnableiOES +#undef glEndConditionalRenderNV +#undef glEndPerfMonitorAMD +#undef glEndPerfQueryINTEL +#undef glEndQuery +#undef glEndQueryEXT +#undef glEndTilingQCOM +#undef glEndTransformFeedback +#undef glExtGetBufferPointervQCOM +#undef glExtGetBuffersQCOM +#undef glExtGetFramebuffersQCOM +#undef glExtGetProgramBinarySourceQCOM +#undef glExtGetProgramsQCOM +#undef glExtGetRenderbuffersQCOM +#undef glExtGetShadersQCOM +#undef glExtGetTexLevelParameterivQCOM +#undef glExtGetTexSubImageQCOM +#undef glExtGetTexturesQCOM +#undef glExtIsProgramBinaryQCOM +#undef glExtTexObjectStateOverrideiQCOM +#undef glFenceSync +#undef glFenceSyncAPPLE +#undef glFinish +#undef glFinishFenceNV +#undef glFlush +#undef glFlushMappedBufferRange +#undef glFlushMappedBufferRangeEXT +#undef glFogf +#undef glFogfv +#undef glFogx +#undef glFogxOES +#undef glFogxv +#undef glFogxvOES +#undef glFragmentCoverageColorNV +#undef glFramebufferParameteri +#undef glFramebufferRenderbuffer +#undef glFramebufferRenderbufferOES +#undef glFramebufferSampleLocationsfvNV +#undef glFramebufferTexture +#undef glFramebufferTexture2D +#undef glFramebufferTexture2DMultisampleEXT +#undef glFramebufferTexture2DMultisampleIMG +#undef glFramebufferTexture2DOES +#undef glFramebufferTexture3DOES +#undef glFramebufferTextureEXT +#undef glFramebufferTextureLayer +#undef glFramebufferTextureMultisampleMultiviewOVR +#undef glFramebufferTextureMultiviewOVR +#undef glFramebufferTextureOES +#undef glFrontFace +#undef glFrustumf +#undef glFrustumfOES +#undef glFrustumx +#undef glFrustumxOES +#undef glGenBuffers +#undef glGenFencesNV +#undef glGenFramebuffers +#undef glGenFramebuffersOES +#undef glGenPathsNV +#undef glGenPerfMonitorsAMD +#undef glGenProgramPipelines +#undef glGenProgramPipelinesEXT +#undef glGenQueries +#undef glGenQueriesEXT +#undef glGenRenderbuffers +#undef glGenRenderbuffersOES +#undef glGenSamplers +#undef glGenTextures +#undef glGenTransformFeedbacks +#undef glGenVertexArrays +#undef glGenVertexArraysOES +#undef glGenerateMipmap +#undef glGenerateMipmapOES +#undef glGetActiveAttrib +#undef glGetActiveUniform +#undef glGetActiveUniformBlockName +#undef glGetActiveUniformBlockiv +#undef glGetActiveUniformsiv +#undef glGetAttachedShaders +#undef glGetAttribLocation +#undef glGetBooleani_v +#undef glGetBooleanv +#undef glGetBufferParameteri64v +#undef glGetBufferParameteriv +#undef glGetBufferPointerv +#undef glGetBufferPointervOES +#undef glGetClipPlanef +#undef glGetClipPlanefOES +#undef glGetClipPlanex +#undef glGetClipPlanexOES +#undef glGetCoverageModulationTableNV +#undef glGetDebugMessageLog +#undef glGetDebugMessageLogKHR +#undef glGetDriverControlStringQCOM +#undef glGetDriverControlsQCOM +#undef glGetError +#undef glGetFenceivNV +#undef glGetFirstPerfQueryIdINTEL +#undef glGetFixedv +#undef glGetFixedvOES +#undef glGetFloati_vNV +#undef glGetFloatv +#undef glGetFragDataIndexEXT +#undef glGetFragDataLocation +#undef glGetFramebufferAttachmentParameteriv +#undef glGetFramebufferAttachmentParameterivOES +#undef glGetFramebufferParameteriv +#undef glGetGraphicsResetStatus +#undef glGetGraphicsResetStatusEXT +#undef glGetGraphicsResetStatusKHR +#undef glGetImageHandleNV +#undef glGetInteger64i_v +#undef glGetInteger64v +#undef glGetInteger64vAPPLE +#undef glGetIntegeri_v +#undef glGetIntegeri_vEXT +#undef glGetIntegerv +#undef glGetInternalformatSampleivNV +#undef glGetInternalformativ +#undef glGetLightfv +#undef glGetLightxv +#undef glGetLightxvOES +#undef glGetMaterialfv +#undef glGetMaterialxv +#undef glGetMaterialxvOES +#undef glGetMultisamplefv +#undef glGetNextPerfQueryIdINTEL +#undef glGetObjectLabel +#undef glGetObjectLabelEXT +#undef glGetObjectLabelKHR +#undef glGetObjectPtrLabel +#undef glGetObjectPtrLabelKHR +#undef glGetPathCommandsNV +#undef glGetPathCoordsNV +#undef glGetPathDashArrayNV +#undef glGetPathLengthNV +#undef glGetPathMetricRangeNV +#undef glGetPathMetricsNV +#undef glGetPathParameterfvNV +#undef glGetPathParameterivNV +#undef glGetPathSpacingNV +#undef glGetPerfCounterInfoINTEL +#undef glGetPerfMonitorCounterDataAMD +#undef glGetPerfMonitorCounterInfoAMD +#undef glGetPerfMonitorCounterStringAMD +#undef glGetPerfMonitorCountersAMD +#undef glGetPerfMonitorGroupStringAMD +#undef glGetPerfMonitorGroupsAMD +#undef glGetPerfQueryDataINTEL +#undef glGetPerfQueryIdByNameINTEL +#undef glGetPerfQueryInfoINTEL +#undef glGetPointerv +#undef glGetPointervKHR +#undef glGetProgramBinary +#undef glGetProgramBinaryOES +#undef glGetProgramInfoLog +#undef glGetProgramInterfaceiv +#undef glGetProgramPipelineInfoLog +#undef glGetProgramPipelineInfoLogEXT +#undef glGetProgramPipelineiv +#undef glGetProgramPipelineivEXT +#undef glGetProgramResourceIndex +#undef glGetProgramResourceLocation +#undef glGetProgramResourceLocationIndexEXT +#undef glGetProgramResourceName +#undef glGetProgramResourcefvNV +#undef glGetProgramResourceiv +#undef glGetProgramiv +#undef glGetQueryObjecti64vEXT +#undef glGetQueryObjectivEXT +#undef glGetQueryObjectui64vEXT +#undef glGetQueryObjectuiv +#undef glGetQueryObjectuivEXT +#undef glGetQueryiv +#undef glGetQueryivEXT +#undef glGetRenderbufferParameteriv +#undef glGetRenderbufferParameterivOES +#undef glGetSamplerParameterIiv +#undef glGetSamplerParameterIivEXT +#undef glGetSamplerParameterIivOES +#undef glGetSamplerParameterIuiv +#undef glGetSamplerParameterIuivEXT +#undef glGetSamplerParameterIuivOES +#undef glGetSamplerParameterfv +#undef glGetSamplerParameteriv +#undef glGetShaderInfoLog +#undef glGetShaderPrecisionFormat +#undef glGetShaderSource +#undef glGetShaderiv +#undef glGetString +#undef glGetStringi +#undef glGetSynciv +#undef glGetSyncivAPPLE +#undef glGetTexEnvfv +#undef glGetTexEnviv +#undef glGetTexEnvxv +#undef glGetTexEnvxvOES +#undef glGetTexGenfvOES +#undef glGetTexGenivOES +#undef glGetTexGenxvOES +#undef glGetTexLevelParameterfv +#undef glGetTexLevelParameteriv +#undef glGetTexParameterIiv +#undef glGetTexParameterIivEXT +#undef glGetTexParameterIivOES +#undef glGetTexParameterIuiv +#undef glGetTexParameterIuivEXT +#undef glGetTexParameterIuivOES +#undef glGetTexParameterfv +#undef glGetTexParameteriv +#undef glGetTexParameterxv +#undef glGetTexParameterxvOES +#undef glGetTextureHandleNV +#undef glGetTextureSamplerHandleNV +#undef glGetTransformFeedbackVarying +#undef glGetTranslatedShaderSourceANGLE +#undef glGetUniformBlockIndex +#undef glGetUniformIndices +#undef glGetUniformLocation +#undef glGetUniformfv +#undef glGetUniformiv +#undef glGetUniformuiv +#undef glGetVertexAttribIiv +#undef glGetVertexAttribIuiv +#undef glGetVertexAttribPointerv +#undef glGetVertexAttribfv +#undef glGetVertexAttribiv +#undef glGetnUniformfv +#undef glGetnUniformfvEXT +#undef glGetnUniformfvKHR +#undef glGetnUniformiv +#undef glGetnUniformivEXT +#undef glGetnUniformivKHR +#undef glGetnUniformuiv +#undef glGetnUniformuivKHR +#undef glHint +#undef glInsertEventMarkerEXT +#undef glInterpolatePathsNV +#undef glInvalidateFramebuffer +#undef glInvalidateSubFramebuffer +#undef glIsBuffer +#undef glIsEnabled +#undef glIsEnabledi +#undef glIsEnablediEXT +#undef glIsEnablediNV +#undef glIsEnablediOES +#undef glIsFenceNV +#undef glIsFramebuffer +#undef glIsFramebufferOES +#undef glIsImageHandleResidentNV +#undef glIsPathNV +#undef glIsPointInFillPathNV +#undef glIsPointInStrokePathNV +#undef glIsProgram +#undef glIsProgramPipeline +#undef glIsProgramPipelineEXT +#undef glIsQuery +#undef glIsQueryEXT +#undef glIsRenderbuffer +#undef glIsRenderbufferOES +#undef glIsSampler +#undef glIsShader +#undef glIsSync +#undef glIsSyncAPPLE +#undef glIsTexture +#undef glIsTextureHandleResidentNV +#undef glIsTransformFeedback +#undef glIsVertexArray +#undef glIsVertexArrayOES +#undef glLabelObjectEXT +#undef glLightModelf +#undef glLightModelfv +#undef glLightModelx +#undef glLightModelxOES +#undef glLightModelxv +#undef glLightModelxvOES +#undef glLightf +#undef glLightfv +#undef glLightx +#undef glLightxOES +#undef glLightxv +#undef glLightxvOES +#undef glLineWidth +#undef glLineWidthx +#undef glLineWidthxOES +#undef glLinkProgram +#undef glLoadIdentity +#undef glLoadMatrixf +#undef glLoadMatrixx +#undef glLoadMatrixxOES +#undef glLoadPaletteFromModelViewMatrixOES +#undef glLogicOp +#undef glMakeImageHandleNonResidentNV +#undef glMakeImageHandleResidentNV +#undef glMakeTextureHandleNonResidentNV +#undef glMakeTextureHandleResidentNV +#undef glMapBufferOES +#undef glMapBufferRange +#undef glMapBufferRangeEXT +#undef glMaterialf +#undef glMaterialfv +#undef glMaterialx +#undef glMaterialxOES +#undef glMaterialxv +#undef glMaterialxvOES +#undef glMatrixIndexPointerOES +#undef glMatrixLoad3x2fNV +#undef glMatrixLoad3x3fNV +#undef glMatrixLoadTranspose3x3fNV +#undef glMatrixMode +#undef glMatrixMult3x2fNV +#undef glMatrixMult3x3fNV +#undef glMatrixMultTranspose3x3fNV +#undef glMemoryBarrier +#undef glMemoryBarrierByRegion +#undef glMinSampleShading +#undef glMinSampleShadingOES +#undef glMultMatrixf +#undef glMultMatrixx +#undef glMultMatrixxOES +#undef glMultiDrawArraysEXT +#undef glMultiDrawArraysIndirectEXT +#undef glMultiDrawElementsBaseVertexEXT +#undef glMultiDrawElementsBaseVertexOES +#undef glMultiDrawElementsEXT +#undef glMultiDrawElementsIndirectEXT +#undef glMultiTexCoord4f +#undef glMultiTexCoord4x +#undef glMultiTexCoord4xOES +#undef glNamedFramebufferSampleLocationsfvNV +#undef glNormal3f +#undef glNormal3x +#undef glNormal3xOES +#undef glNormalPointer +#undef glObjectLabel +#undef glObjectLabelKHR +#undef glObjectPtrLabel +#undef glObjectPtrLabelKHR +#undef glOrthof +#undef glOrthofOES +#undef glOrthox +#undef glOrthoxOES +#undef glPatchParameteri +#undef glPatchParameteriEXT +#undef glPatchParameteriOES +#undef glPathCommandsNV +#undef glPathCoordsNV +#undef glPathCoverDepthFuncNV +#undef glPathDashArrayNV +#undef glPathGlyphIndexArrayNV +#undef glPathGlyphIndexRangeNV +#undef glPathGlyphRangeNV +#undef glPathGlyphsNV +#undef glPathMemoryGlyphIndexArrayNV +#undef glPathParameterfNV +#undef glPathParameterfvNV +#undef glPathParameteriNV +#undef glPathParameterivNV +#undef glPathStencilDepthOffsetNV +#undef glPathStencilFuncNV +#undef glPathStringNV +#undef glPathSubCommandsNV +#undef glPathSubCoordsNV +#undef glPauseTransformFeedback +#undef glPixelStorei +#undef glPointAlongPathNV +#undef glPointParameterf +#undef glPointParameterfv +#undef glPointParameterx +#undef glPointParameterxOES +#undef glPointParameterxv +#undef glPointParameterxvOES +#undef glPointSize +#undef glPointSizePointerOES +#undef glPointSizex +#undef glPointSizexOES +#undef glPolygonModeNV +#undef glPolygonOffset +#undef glPolygonOffsetx +#undef glPolygonOffsetxOES +#undef glPopDebugGroup +#undef glPopDebugGroupKHR +#undef glPopGroupMarkerEXT +#undef glPopMatrix +#undef glPrimitiveBoundingBox +#undef glPrimitiveBoundingBoxEXT +#undef glPrimitiveBoundingBoxOES +#undef glProgramBinary +#undef glProgramBinaryOES +#undef glProgramParameteri +#undef glProgramParameteriEXT +#undef glProgramPathFragmentInputGenNV +#undef glProgramUniform1f +#undef glProgramUniform1fEXT +#undef glProgramUniform1fv +#undef glProgramUniform1fvEXT +#undef glProgramUniform1i +#undef glProgramUniform1iEXT +#undef glProgramUniform1iv +#undef glProgramUniform1ivEXT +#undef glProgramUniform1ui +#undef glProgramUniform1uiEXT +#undef glProgramUniform1uiv +#undef glProgramUniform1uivEXT +#undef glProgramUniform2f +#undef glProgramUniform2fEXT +#undef glProgramUniform2fv +#undef glProgramUniform2fvEXT +#undef glProgramUniform2i +#undef glProgramUniform2iEXT +#undef glProgramUniform2iv +#undef glProgramUniform2ivEXT +#undef glProgramUniform2ui +#undef glProgramUniform2uiEXT +#undef glProgramUniform2uiv +#undef glProgramUniform2uivEXT +#undef glProgramUniform3f +#undef glProgramUniform3fEXT +#undef glProgramUniform3fv +#undef glProgramUniform3fvEXT +#undef glProgramUniform3i +#undef glProgramUniform3iEXT +#undef glProgramUniform3iv +#undef glProgramUniform3ivEXT +#undef glProgramUniform3ui +#undef glProgramUniform3uiEXT +#undef glProgramUniform3uiv +#undef glProgramUniform3uivEXT +#undef glProgramUniform4f +#undef glProgramUniform4fEXT +#undef glProgramUniform4fv +#undef glProgramUniform4fvEXT +#undef glProgramUniform4i +#undef glProgramUniform4iEXT +#undef glProgramUniform4iv +#undef glProgramUniform4ivEXT +#undef glProgramUniform4ui +#undef glProgramUniform4uiEXT +#undef glProgramUniform4uiv +#undef glProgramUniform4uivEXT +#undef glProgramUniformHandleui64NV +#undef glProgramUniformHandleui64vNV +#undef glProgramUniformMatrix2fv +#undef glProgramUniformMatrix2fvEXT +#undef glProgramUniformMatrix2x3fv +#undef glProgramUniformMatrix2x3fvEXT +#undef glProgramUniformMatrix2x4fv +#undef glProgramUniformMatrix2x4fvEXT +#undef glProgramUniformMatrix3fv +#undef glProgramUniformMatrix3fvEXT +#undef glProgramUniformMatrix3x2fv +#undef glProgramUniformMatrix3x2fvEXT +#undef glProgramUniformMatrix3x4fv +#undef glProgramUniformMatrix3x4fvEXT +#undef glProgramUniformMatrix4fv +#undef glProgramUniformMatrix4fvEXT +#undef glProgramUniformMatrix4x2fv +#undef glProgramUniformMatrix4x2fvEXT +#undef glProgramUniformMatrix4x3fv +#undef glProgramUniformMatrix4x3fvEXT +#undef glPushDebugGroup +#undef glPushDebugGroupKHR +#undef glPushGroupMarkerEXT +#undef glPushMatrix +#undef glQueryCounterEXT +#undef glQueryMatrixxOES +#undef glRasterSamplesEXT +#undef glReadBuffer +#undef glReadBufferIndexedEXT +#undef glReadBufferNV +#undef glReadPixels +#undef glReadnPixels +#undef glReadnPixelsEXT +#undef glReadnPixelsKHR +#undef glReleaseShaderCompiler +#undef glRenderbufferStorage +#undef glRenderbufferStorageMultisample +#undef glRenderbufferStorageMultisampleANGLE +#undef glRenderbufferStorageMultisampleAPPLE +#undef glRenderbufferStorageMultisampleEXT +#undef glRenderbufferStorageMultisampleIMG +#undef glRenderbufferStorageMultisampleNV +#undef glRenderbufferStorageOES +#undef glResolveDepthValuesNV +#undef glResolveMultisampleFramebufferAPPLE +#undef glResumeTransformFeedback +#undef glRotatef +#undef glRotatex +#undef glRotatexOES +#undef glSampleCoverage +#undef glSampleCoveragex +#undef glSampleCoveragexOES +#undef glSampleMaski +#undef glSamplerParameterIiv +#undef glSamplerParameterIivEXT +#undef glSamplerParameterIivOES +#undef glSamplerParameterIuiv +#undef glSamplerParameterIuivEXT +#undef glSamplerParameterIuivOES +#undef glSamplerParameterf +#undef glSamplerParameterfv +#undef glSamplerParameteri +#undef glSamplerParameteriv +#undef glScalef +#undef glScalex +#undef glScalexOES +#undef glScissor +#undef glScissorArrayvNV +#undef glScissorIndexedNV +#undef glScissorIndexedvNV +#undef glSelectPerfMonitorCountersAMD +#undef glSetFenceNV +#undef glShadeModel +#undef glShaderBinary +#undef glShaderSource +#undef glStartTilingQCOM +#undef glStencilFillPathInstancedNV +#undef glStencilFillPathNV +#undef glStencilFunc +#undef glStencilFuncSeparate +#undef glStencilMask +#undef glStencilMaskSeparate +#undef glStencilOp +#undef glStencilOpSeparate +#undef glStencilStrokePathInstancedNV +#undef glStencilStrokePathNV +#undef glStencilThenCoverFillPathInstancedNV +#undef glStencilThenCoverFillPathNV +#undef glStencilThenCoverStrokePathInstancedNV +#undef glStencilThenCoverStrokePathNV +#undef glSubpixelPrecisionBiasNV +#undef glTestFenceNV +#undef glTexBuffer +#undef glTexBufferEXT +#undef glTexBufferOES +#undef glTexBufferRange +#undef glTexBufferRangeEXT +#undef glTexBufferRangeOES +#undef glTexCoordPointer +#undef glTexEnvf +#undef glTexEnvfv +#undef glTexEnvi +#undef glTexEnviv +#undef glTexEnvx +#undef glTexEnvxOES +#undef glTexEnvxv +#undef glTexEnvxvOES +#undef glTexGenfOES +#undef glTexGenfvOES +#undef glTexGeniOES +#undef glTexGenivOES +#undef glTexGenxOES +#undef glTexGenxvOES +#undef glTexImage2D +#undef glTexImage3D +#undef glTexImage3DOES +#undef glTexPageCommitmentEXT +#undef glTexParameterIiv +#undef glTexParameterIivEXT +#undef glTexParameterIivOES +#undef glTexParameterIuiv +#undef glTexParameterIuivEXT +#undef glTexParameterIuivOES +#undef glTexParameterf +#undef glTexParameterfv +#undef glTexParameteri +#undef glTexParameteriv +#undef glTexParameterx +#undef glTexParameterxOES +#undef glTexParameterxv +#undef glTexParameterxvOES +#undef glTexStorage1DEXT +#undef glTexStorage2D +#undef glTexStorage2DEXT +#undef glTexStorage2DMultisample +#undef glTexStorage3D +#undef glTexStorage3DEXT +#undef glTexStorage3DMultisample +#undef glTexStorage3DMultisampleOES +#undef glTexSubImage2D +#undef glTexSubImage3D +#undef glTexSubImage3DOES +#undef glTextureStorage1DEXT +#undef glTextureStorage2DEXT +#undef glTextureStorage3DEXT +#undef glTextureViewEXT +#undef glTextureViewOES +#undef glTransformFeedbackVaryings +#undef glTransformPathNV +#undef glTranslatef +#undef glTranslatex +#undef glTranslatexOES +#undef glUniform1f +#undef glUniform1fv +#undef glUniform1i +#undef glUniform1iv +#undef glUniform1ui +#undef glUniform1uiv +#undef glUniform2f +#undef glUniform2fv +#undef glUniform2i +#undef glUniform2iv +#undef glUniform2ui +#undef glUniform2uiv +#undef glUniform3f +#undef glUniform3fv +#undef glUniform3i +#undef glUniform3iv +#undef glUniform3ui +#undef glUniform3uiv +#undef glUniform4f +#undef glUniform4fv +#undef glUniform4i +#undef glUniform4iv +#undef glUniform4ui +#undef glUniform4uiv +#undef glUniformBlockBinding +#undef glUniformHandleui64NV +#undef glUniformHandleui64vNV +#undef glUniformMatrix2fv +#undef glUniformMatrix2x3fv +#undef glUniformMatrix2x3fvNV +#undef glUniformMatrix2x4fv +#undef glUniformMatrix2x4fvNV +#undef glUniformMatrix3fv +#undef glUniformMatrix3x2fv +#undef glUniformMatrix3x2fvNV +#undef glUniformMatrix3x4fv +#undef glUniformMatrix3x4fvNV +#undef glUniformMatrix4fv +#undef glUniformMatrix4x2fv +#undef glUniformMatrix4x2fvNV +#undef glUniformMatrix4x3fv +#undef glUniformMatrix4x3fvNV +#undef glUnmapBuffer +#undef glUnmapBufferOES +#undef glUseProgram +#undef glUseProgramStages +#undef glUseProgramStagesEXT +#undef glValidateProgram +#undef glValidateProgramPipeline +#undef glValidateProgramPipelineEXT +#undef glVertexAttrib1f +#undef glVertexAttrib1fv +#undef glVertexAttrib2f +#undef glVertexAttrib2fv +#undef glVertexAttrib3f +#undef glVertexAttrib3fv +#undef glVertexAttrib4f +#undef glVertexAttrib4fv +#undef glVertexAttribBinding +#undef glVertexAttribDivisor +#undef glVertexAttribDivisorANGLE +#undef glVertexAttribDivisorEXT +#undef glVertexAttribDivisorNV +#undef glVertexAttribFormat +#undef glVertexAttribI4i +#undef glVertexAttribI4iv +#undef glVertexAttribI4ui +#undef glVertexAttribI4uiv +#undef glVertexAttribIFormat +#undef glVertexAttribIPointer +#undef glVertexAttribPointer +#undef glVertexBindingDivisor +#undef glVertexPointer +#undef glViewport +#undef glViewportArrayvNV +#undef glViewportIndexedfNV +#undef glViewportIndexedfvNV +#undef glWaitSync +#undef glWaitSyncAPPLE +#undef glWeightPathsNV +#undef glWeightPointerOES + +#endif // HWUI_GLES_WRAP_ENABLED diff --git a/libs/hwui/debug/wrap_gles.cpp b/libs/hwui/debug/wrap_gles.cpp new file mode 100644 index 000000000000..c4f2e3537fe8 --- /dev/null +++ b/libs/hwui/debug/wrap_gles.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "unwrap_gles.h" + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES/gl.h> +#include <GLES/glext.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <GLES3/gl3.h> +#include <GLES3/gl31.h> +#include <GLES3/gl32.h> + +#include <cutils/log.h> + +void assertNoGlErrors(const char* apicall) { + GLenum status = GL_NO_ERROR; + GLenum lastError = GL_NO_ERROR; + const char* lastErrorName = nullptr; + while ((status = glGetError()) != GL_NO_ERROR) { + lastError = status; + switch (status) { + case GL_INVALID_ENUM: + ALOGE("GL error: GL_INVALID_ENUM"); + lastErrorName = "GL_INVALID_ENUM"; + break; + case GL_INVALID_VALUE: + ALOGE("GL error: GL_INVALID_VALUE"); + lastErrorName = "GL_INVALID_VALUE"; + break; + case GL_INVALID_OPERATION: + ALOGE("GL error: GL_INVALID_OPERATION"); + lastErrorName = "GL_INVALID_OPERATION"; + break; + case GL_OUT_OF_MEMORY: + ALOGE("GL error: Out of memory!"); + lastErrorName = "GL_OUT_OF_MEMORY"; + break; + default: + ALOGE("GL error: 0x%x", status); + lastErrorName = "UNKNOWN"; + } + } + LOG_ALWAYS_FATAL_IF(lastError != GL_NO_ERROR, + "%s error! %s (0x%x)", apicall, lastErrorName, lastError); +} + +#define API_ENTRY(x) wrap_##x +#define CALL_GL_API(x, ...) x(__VA_ARGS__); assertNoGlErrors(#x) +#define CALL_GL_API_RETURN(x, ...) auto ret = x(__VA_ARGS__);\ + assertNoGlErrors(#x);\ + return ret + +extern "C" { +#include <gl2_api.in> +#include <gl2ext_api.in> + +// libGLESv2 handles these specially, so they are not in gl2_api.in + +void API_ENTRY(glGetBooleanv)(GLenum pname, GLboolean *data) { + CALL_GL_API(glGetBooleanv, pname, data); +} +void API_ENTRY(glGetFloatv)(GLenum pname, GLfloat *data) { + CALL_GL_API(glGetFloatv, pname, data); +} +void API_ENTRY(glGetIntegerv)(GLenum pname, GLint *data) { + CALL_GL_API(glGetIntegerv, pname, data); +} +const GLubyte * API_ENTRY(glGetString)(GLenum name) { + CALL_GL_API_RETURN(glGetString, name); +} +const GLubyte * API_ENTRY(glGetStringi)(GLenum name, GLuint index) { + CALL_GL_API_RETURN(glGetStringi, name, index); +} +void API_ENTRY(glGetInteger64v)(GLenum pname, GLint64 *data) { + CALL_GL_API(glGetInteger64v, pname, data); +} +} diff --git a/libs/hwui/debug/wrap_gles.h b/libs/hwui/debug/wrap_gles.h new file mode 100644 index 000000000000..4a3537442e73 --- /dev/null +++ b/libs/hwui/debug/wrap_gles.h @@ -0,0 +1,918 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HWUI_GLES_WRAP_ENABLED +#define HWUI_GLES_WRAP_ENABLED + +#define glActiveShaderProgram wrap_glActiveShaderProgram +#define glActiveShaderProgramEXT wrap_glActiveShaderProgramEXT +#define glActiveTexture wrap_glActiveTexture +#define glAlphaFunc wrap_glAlphaFunc +#define glAlphaFuncQCOM wrap_glAlphaFuncQCOM +#define glAlphaFuncx wrap_glAlphaFuncx +#define glAlphaFuncxOES wrap_glAlphaFuncxOES +#define glApplyFramebufferAttachmentCMAAINTEL wrap_glApplyFramebufferAttachmentCMAAINTEL +#define glAttachShader wrap_glAttachShader +#define glBeginConditionalRenderNV wrap_glBeginConditionalRenderNV +#define glBeginPerfMonitorAMD wrap_glBeginPerfMonitorAMD +#define glBeginPerfQueryINTEL wrap_glBeginPerfQueryINTEL +#define glBeginQuery wrap_glBeginQuery +#define glBeginQueryEXT wrap_glBeginQueryEXT +#define glBeginTransformFeedback wrap_glBeginTransformFeedback +#define glBindAttribLocation wrap_glBindAttribLocation +#define glBindBuffer wrap_glBindBuffer +#define glBindBufferBase wrap_glBindBufferBase +#define glBindBufferRange wrap_glBindBufferRange +#define glBindFragDataLocationEXT wrap_glBindFragDataLocationEXT +#define glBindFragDataLocationIndexedEXT wrap_glBindFragDataLocationIndexedEXT +#define glBindFramebuffer wrap_glBindFramebuffer +#define glBindFramebufferOES wrap_glBindFramebufferOES +#define glBindImageTexture wrap_glBindImageTexture +#define glBindProgramPipeline wrap_glBindProgramPipeline +#define glBindProgramPipelineEXT wrap_glBindProgramPipelineEXT +#define glBindRenderbuffer wrap_glBindRenderbuffer +#define glBindRenderbufferOES wrap_glBindRenderbufferOES +#define glBindSampler wrap_glBindSampler +#define glBindTexture wrap_glBindTexture +#define glBindTransformFeedback wrap_glBindTransformFeedback +#define glBindVertexArray wrap_glBindVertexArray +#define glBindVertexArrayOES wrap_glBindVertexArrayOES +#define glBindVertexBuffer wrap_glBindVertexBuffer +#define glBlendBarrier wrap_glBlendBarrier +#define glBlendBarrierKHR wrap_glBlendBarrierKHR +#define glBlendBarrierNV wrap_glBlendBarrierNV +#define glBlendColor wrap_glBlendColor +#define glBlendEquation wrap_glBlendEquation +#define glBlendEquationOES wrap_glBlendEquationOES +#define glBlendEquationSeparate wrap_glBlendEquationSeparate +#define glBlendEquationSeparateOES wrap_glBlendEquationSeparateOES +#define glBlendEquationSeparatei wrap_glBlendEquationSeparatei +#define glBlendEquationSeparateiEXT wrap_glBlendEquationSeparateiEXT +#define glBlendEquationSeparateiOES wrap_glBlendEquationSeparateiOES +#define glBlendEquationi wrap_glBlendEquationi +#define glBlendEquationiEXT wrap_glBlendEquationiEXT +#define glBlendEquationiOES wrap_glBlendEquationiOES +#define glBlendFunc wrap_glBlendFunc +#define glBlendFuncSeparate wrap_glBlendFuncSeparate +#define glBlendFuncSeparateOES wrap_glBlendFuncSeparateOES +#define glBlendFuncSeparatei wrap_glBlendFuncSeparatei +#define glBlendFuncSeparateiEXT wrap_glBlendFuncSeparateiEXT +#define glBlendFuncSeparateiOES wrap_glBlendFuncSeparateiOES +#define glBlendFunci wrap_glBlendFunci +#define glBlendFunciEXT wrap_glBlendFunciEXT +#define glBlendFunciOES wrap_glBlendFunciOES +#define glBlendParameteriNV wrap_glBlendParameteriNV +#define glBlitFramebuffer wrap_glBlitFramebuffer +#define glBlitFramebufferANGLE wrap_glBlitFramebufferANGLE +#define glBlitFramebufferNV wrap_glBlitFramebufferNV +#define glBufferData wrap_glBufferData +#define glBufferStorageEXT wrap_glBufferStorageEXT +#define glBufferSubData wrap_glBufferSubData +#define glCheckFramebufferStatus wrap_glCheckFramebufferStatus +#define glCheckFramebufferStatusOES wrap_glCheckFramebufferStatusOES +#define glClear wrap_glClear +#define glClearBufferfi wrap_glClearBufferfi +#define glClearBufferfv wrap_glClearBufferfv +#define glClearBufferiv wrap_glClearBufferiv +#define glClearBufferuiv wrap_glClearBufferuiv +#define glClearColor wrap_glClearColor +#define glClearColorx wrap_glClearColorx +#define glClearColorxOES wrap_glClearColorxOES +#define glClearDepthf wrap_glClearDepthf +#define glClearDepthfOES wrap_glClearDepthfOES +#define glClearDepthx wrap_glClearDepthx +#define glClearDepthxOES wrap_glClearDepthxOES +#define glClearStencil wrap_glClearStencil +#define glClientActiveTexture wrap_glClientActiveTexture +#define glClientWaitSync wrap_glClientWaitSync +#define glClientWaitSyncAPPLE wrap_glClientWaitSyncAPPLE +#define glClipPlanef wrap_glClipPlanef +#define glClipPlanefIMG wrap_glClipPlanefIMG +#define glClipPlanefOES wrap_glClipPlanefOES +#define glClipPlanex wrap_glClipPlanex +#define glClipPlanexIMG wrap_glClipPlanexIMG +#define glClipPlanexOES wrap_glClipPlanexOES +#define glColor4f wrap_glColor4f +#define glColor4ub wrap_glColor4ub +#define glColor4x wrap_glColor4x +#define glColor4xOES wrap_glColor4xOES +#define glColorMask wrap_glColorMask +#define glColorMaski wrap_glColorMaski +#define glColorMaskiEXT wrap_glColorMaskiEXT +#define glColorMaskiOES wrap_glColorMaskiOES +#define glColorPointer wrap_glColorPointer +#define glCompileShader wrap_glCompileShader +#define glCompressedTexImage2D wrap_glCompressedTexImage2D +#define glCompressedTexImage3D wrap_glCompressedTexImage3D +#define glCompressedTexImage3DOES wrap_glCompressedTexImage3DOES +#define glCompressedTexSubImage2D wrap_glCompressedTexSubImage2D +#define glCompressedTexSubImage3D wrap_glCompressedTexSubImage3D +#define glCompressedTexSubImage3DOES wrap_glCompressedTexSubImage3DOES +#define glCopyBufferSubData wrap_glCopyBufferSubData +#define glCopyBufferSubDataNV wrap_glCopyBufferSubDataNV +#define glCopyImageSubData wrap_glCopyImageSubData +#define glCopyImageSubDataEXT wrap_glCopyImageSubDataEXT +#define glCopyImageSubDataOES wrap_glCopyImageSubDataOES +#define glCopyPathNV wrap_glCopyPathNV +#define glCopyTexImage2D wrap_glCopyTexImage2D +#define glCopyTexSubImage2D wrap_glCopyTexSubImage2D +#define glCopyTexSubImage3D wrap_glCopyTexSubImage3D +#define glCopyTexSubImage3DOES wrap_glCopyTexSubImage3DOES +#define glCopyTextureLevelsAPPLE wrap_glCopyTextureLevelsAPPLE +#define glCoverFillPathInstancedNV wrap_glCoverFillPathInstancedNV +#define glCoverFillPathNV wrap_glCoverFillPathNV +#define glCoverStrokePathInstancedNV wrap_glCoverStrokePathInstancedNV +#define glCoverStrokePathNV wrap_glCoverStrokePathNV +#define glCoverageMaskNV wrap_glCoverageMaskNV +#define glCoverageModulationNV wrap_glCoverageModulationNV +#define glCoverageModulationTableNV wrap_glCoverageModulationTableNV +#define glCoverageOperationNV wrap_glCoverageOperationNV +#define glCreatePerfQueryINTEL wrap_glCreatePerfQueryINTEL +#define glCreateProgram wrap_glCreateProgram +#define glCreateShader wrap_glCreateShader +#define glCreateShaderProgramv wrap_glCreateShaderProgramv +#define glCreateShaderProgramvEXT wrap_glCreateShaderProgramvEXT +#define glCullFace wrap_glCullFace +#define glCurrentPaletteMatrixOES wrap_glCurrentPaletteMatrixOES +#define glDebugMessageCallback wrap_glDebugMessageCallback +#define glDebugMessageCallbackKHR wrap_glDebugMessageCallbackKHR +#define glDebugMessageControl wrap_glDebugMessageControl +#define glDebugMessageControlKHR wrap_glDebugMessageControlKHR +#define glDebugMessageInsert wrap_glDebugMessageInsert +#define glDebugMessageInsertKHR wrap_glDebugMessageInsertKHR +#define glDeleteBuffers wrap_glDeleteBuffers +#define glDeleteFencesNV wrap_glDeleteFencesNV +#define glDeleteFramebuffers wrap_glDeleteFramebuffers +#define glDeleteFramebuffersOES wrap_glDeleteFramebuffersOES +#define glDeletePathsNV wrap_glDeletePathsNV +#define glDeletePerfMonitorsAMD wrap_glDeletePerfMonitorsAMD +#define glDeletePerfQueryINTEL wrap_glDeletePerfQueryINTEL +#define glDeleteProgram wrap_glDeleteProgram +#define glDeleteProgramPipelines wrap_glDeleteProgramPipelines +#define glDeleteProgramPipelinesEXT wrap_glDeleteProgramPipelinesEXT +#define glDeleteQueries wrap_glDeleteQueries +#define glDeleteQueriesEXT wrap_glDeleteQueriesEXT +#define glDeleteRenderbuffers wrap_glDeleteRenderbuffers +#define glDeleteRenderbuffersOES wrap_glDeleteRenderbuffersOES +#define glDeleteSamplers wrap_glDeleteSamplers +#define glDeleteShader wrap_glDeleteShader +#define glDeleteSync wrap_glDeleteSync +#define glDeleteSyncAPPLE wrap_glDeleteSyncAPPLE +#define glDeleteTextures wrap_glDeleteTextures +#define glDeleteTransformFeedbacks wrap_glDeleteTransformFeedbacks +#define glDeleteVertexArrays wrap_glDeleteVertexArrays +#define glDeleteVertexArraysOES wrap_glDeleteVertexArraysOES +#define glDepthFunc wrap_glDepthFunc +#define glDepthMask wrap_glDepthMask +#define glDepthRangeArrayfvNV wrap_glDepthRangeArrayfvNV +#define glDepthRangeIndexedfNV wrap_glDepthRangeIndexedfNV +#define glDepthRangef wrap_glDepthRangef +#define glDepthRangefOES wrap_glDepthRangefOES +#define glDepthRangex wrap_glDepthRangex +#define glDepthRangexOES wrap_glDepthRangexOES +#define glDetachShader wrap_glDetachShader +#define glDisable wrap_glDisable +#define glDisableClientState wrap_glDisableClientState +#define glDisableDriverControlQCOM wrap_glDisableDriverControlQCOM +#define glDisableVertexAttribArray wrap_glDisableVertexAttribArray +#define glDisablei wrap_glDisablei +#define glDisableiEXT wrap_glDisableiEXT +#define glDisableiNV wrap_glDisableiNV +#define glDisableiOES wrap_glDisableiOES +#define glDiscardFramebufferEXT wrap_glDiscardFramebufferEXT +#define glDispatchCompute wrap_glDispatchCompute +#define glDispatchComputeIndirect wrap_glDispatchComputeIndirect +#define glDrawArrays wrap_glDrawArrays +#define glDrawArraysIndirect wrap_glDrawArraysIndirect +#define glDrawArraysInstanced wrap_glDrawArraysInstanced +#define glDrawArraysInstancedANGLE wrap_glDrawArraysInstancedANGLE +#define glDrawArraysInstancedBaseInstanceEXT wrap_glDrawArraysInstancedBaseInstanceEXT +#define glDrawArraysInstancedEXT wrap_glDrawArraysInstancedEXT +#define glDrawArraysInstancedNV wrap_glDrawArraysInstancedNV +#define glDrawBuffers wrap_glDrawBuffers +#define glDrawBuffersEXT wrap_glDrawBuffersEXT +#define glDrawBuffersIndexedEXT wrap_glDrawBuffersIndexedEXT +#define glDrawBuffersNV wrap_glDrawBuffersNV +#define glDrawElements wrap_glDrawElements +#define glDrawElementsBaseVertex wrap_glDrawElementsBaseVertex +#define glDrawElementsBaseVertexEXT wrap_glDrawElementsBaseVertexEXT +#define glDrawElementsBaseVertexOES wrap_glDrawElementsBaseVertexOES +#define glDrawElementsIndirect wrap_glDrawElementsIndirect +#define glDrawElementsInstanced wrap_glDrawElementsInstanced +#define glDrawElementsInstancedANGLE wrap_glDrawElementsInstancedANGLE +#define glDrawElementsInstancedBaseInstanceEXT wrap_glDrawElementsInstancedBaseInstanceEXT +#define glDrawElementsInstancedBaseVertex wrap_glDrawElementsInstancedBaseVertex +#define glDrawElementsInstancedBaseVertexBaseInstanceEXT wrap_glDrawElementsInstancedBaseVertexBaseInstanceEXT +#define glDrawElementsInstancedBaseVertexEXT wrap_glDrawElementsInstancedBaseVertexEXT +#define glDrawElementsInstancedBaseVertexOES wrap_glDrawElementsInstancedBaseVertexOES +#define glDrawElementsInstancedEXT wrap_glDrawElementsInstancedEXT +#define glDrawElementsInstancedNV wrap_glDrawElementsInstancedNV +#define glDrawRangeElements wrap_glDrawRangeElements +#define glDrawRangeElementsBaseVertex wrap_glDrawRangeElementsBaseVertex +#define glDrawRangeElementsBaseVertexEXT wrap_glDrawRangeElementsBaseVertexEXT +#define glDrawRangeElementsBaseVertexOES wrap_glDrawRangeElementsBaseVertexOES +#define glDrawTexfOES wrap_glDrawTexfOES +#define glDrawTexfvOES wrap_glDrawTexfvOES +#define glDrawTexiOES wrap_glDrawTexiOES +#define glDrawTexivOES wrap_glDrawTexivOES +#define glDrawTexsOES wrap_glDrawTexsOES +#define glDrawTexsvOES wrap_glDrawTexsvOES +#define glDrawTexxOES wrap_glDrawTexxOES +#define glDrawTexxvOES wrap_glDrawTexxvOES +#define glEGLImageTargetRenderbufferStorageOES wrap_glEGLImageTargetRenderbufferStorageOES +#define glEGLImageTargetTexture2DOES wrap_glEGLImageTargetTexture2DOES +#define glEnable wrap_glEnable +#define glEnableClientState wrap_glEnableClientState +#define glEnableDriverControlQCOM wrap_glEnableDriverControlQCOM +#define glEnableVertexAttribArray wrap_glEnableVertexAttribArray +#define glEnablei wrap_glEnablei +#define glEnableiEXT wrap_glEnableiEXT +#define glEnableiNV wrap_glEnableiNV +#define glEnableiOES wrap_glEnableiOES +#define glEndConditionalRenderNV wrap_glEndConditionalRenderNV +#define glEndPerfMonitorAMD wrap_glEndPerfMonitorAMD +#define glEndPerfQueryINTEL wrap_glEndPerfQueryINTEL +#define glEndQuery wrap_glEndQuery +#define glEndQueryEXT wrap_glEndQueryEXT +#define glEndTilingQCOM wrap_glEndTilingQCOM +#define glEndTransformFeedback wrap_glEndTransformFeedback +#define glExtGetBufferPointervQCOM wrap_glExtGetBufferPointervQCOM +#define glExtGetBuffersQCOM wrap_glExtGetBuffersQCOM +#define glExtGetFramebuffersQCOM wrap_glExtGetFramebuffersQCOM +#define glExtGetProgramBinarySourceQCOM wrap_glExtGetProgramBinarySourceQCOM +#define glExtGetProgramsQCOM wrap_glExtGetProgramsQCOM +#define glExtGetRenderbuffersQCOM wrap_glExtGetRenderbuffersQCOM +#define glExtGetShadersQCOM wrap_glExtGetShadersQCOM +#define glExtGetTexLevelParameterivQCOM wrap_glExtGetTexLevelParameterivQCOM +#define glExtGetTexSubImageQCOM wrap_glExtGetTexSubImageQCOM +#define glExtGetTexturesQCOM wrap_glExtGetTexturesQCOM +#define glExtIsProgramBinaryQCOM wrap_glExtIsProgramBinaryQCOM +#define glExtTexObjectStateOverrideiQCOM wrap_glExtTexObjectStateOverrideiQCOM +#define glFenceSync wrap_glFenceSync +#define glFenceSyncAPPLE wrap_glFenceSyncAPPLE +#define glFinish wrap_glFinish +#define glFinishFenceNV wrap_glFinishFenceNV +#define glFlush wrap_glFlush +#define glFlushMappedBufferRange wrap_glFlushMappedBufferRange +#define glFlushMappedBufferRangeEXT wrap_glFlushMappedBufferRangeEXT +#define glFogf wrap_glFogf +#define glFogfv wrap_glFogfv +#define glFogx wrap_glFogx +#define glFogxOES wrap_glFogxOES +#define glFogxv wrap_glFogxv +#define glFogxvOES wrap_glFogxvOES +#define glFragmentCoverageColorNV wrap_glFragmentCoverageColorNV +#define glFramebufferParameteri wrap_glFramebufferParameteri +#define glFramebufferRenderbuffer wrap_glFramebufferRenderbuffer +#define glFramebufferRenderbufferOES wrap_glFramebufferRenderbufferOES +#define glFramebufferSampleLocationsfvNV wrap_glFramebufferSampleLocationsfvNV +#define glFramebufferTexture wrap_glFramebufferTexture +#define glFramebufferTexture2D wrap_glFramebufferTexture2D +#define glFramebufferTexture2DMultisampleEXT wrap_glFramebufferTexture2DMultisampleEXT +#define glFramebufferTexture2DMultisampleIMG wrap_glFramebufferTexture2DMultisampleIMG +#define glFramebufferTexture2DOES wrap_glFramebufferTexture2DOES +#define glFramebufferTexture3DOES wrap_glFramebufferTexture3DOES +#define glFramebufferTextureEXT wrap_glFramebufferTextureEXT +#define glFramebufferTextureLayer wrap_glFramebufferTextureLayer +#define glFramebufferTextureMultisampleMultiviewOVR wrap_glFramebufferTextureMultisampleMultiviewOVR +#define glFramebufferTextureMultiviewOVR wrap_glFramebufferTextureMultiviewOVR +#define glFramebufferTextureOES wrap_glFramebufferTextureOES +#define glFrontFace wrap_glFrontFace +#define glFrustumf wrap_glFrustumf +#define glFrustumfOES wrap_glFrustumfOES +#define glFrustumx wrap_glFrustumx +#define glFrustumxOES wrap_glFrustumxOES +#define glGenBuffers wrap_glGenBuffers +#define glGenFencesNV wrap_glGenFencesNV +#define glGenFramebuffers wrap_glGenFramebuffers +#define glGenFramebuffersOES wrap_glGenFramebuffersOES +#define glGenPathsNV wrap_glGenPathsNV +#define glGenPerfMonitorsAMD wrap_glGenPerfMonitorsAMD +#define glGenProgramPipelines wrap_glGenProgramPipelines +#define glGenProgramPipelinesEXT wrap_glGenProgramPipelinesEXT +#define glGenQueries wrap_glGenQueries +#define glGenQueriesEXT wrap_glGenQueriesEXT +#define glGenRenderbuffers wrap_glGenRenderbuffers +#define glGenRenderbuffersOES wrap_glGenRenderbuffersOES +#define glGenSamplers wrap_glGenSamplers +#define glGenTextures wrap_glGenTextures +#define glGenTransformFeedbacks wrap_glGenTransformFeedbacks +#define glGenVertexArrays wrap_glGenVertexArrays +#define glGenVertexArraysOES wrap_glGenVertexArraysOES +#define glGenerateMipmap wrap_glGenerateMipmap +#define glGenerateMipmapOES wrap_glGenerateMipmapOES +#define glGetActiveAttrib wrap_glGetActiveAttrib +#define glGetActiveUniform wrap_glGetActiveUniform +#define glGetActiveUniformBlockName wrap_glGetActiveUniformBlockName +#define glGetActiveUniformBlockiv wrap_glGetActiveUniformBlockiv +#define glGetActiveUniformsiv wrap_glGetActiveUniformsiv +#define glGetAttachedShaders wrap_glGetAttachedShaders +#define glGetAttribLocation wrap_glGetAttribLocation +#define glGetBooleani_v wrap_glGetBooleani_v +#define glGetBooleanv wrap_glGetBooleanv +#define glGetBufferParameteri64v wrap_glGetBufferParameteri64v +#define glGetBufferParameteriv wrap_glGetBufferParameteriv +#define glGetBufferPointerv wrap_glGetBufferPointerv +#define glGetBufferPointervOES wrap_glGetBufferPointervOES +#define glGetClipPlanef wrap_glGetClipPlanef +#define glGetClipPlanefOES wrap_glGetClipPlanefOES +#define glGetClipPlanex wrap_glGetClipPlanex +#define glGetClipPlanexOES wrap_glGetClipPlanexOES +#define glGetCoverageModulationTableNV wrap_glGetCoverageModulationTableNV +#define glGetDebugMessageLog wrap_glGetDebugMessageLog +#define glGetDebugMessageLogKHR wrap_glGetDebugMessageLogKHR +#define glGetDriverControlStringQCOM wrap_glGetDriverControlStringQCOM +#define glGetDriverControlsQCOM wrap_glGetDriverControlsQCOM +#define glGetError wrap_glGetError +#define glGetFenceivNV wrap_glGetFenceivNV +#define glGetFirstPerfQueryIdINTEL wrap_glGetFirstPerfQueryIdINTEL +#define glGetFixedv wrap_glGetFixedv +#define glGetFixedvOES wrap_glGetFixedvOES +#define glGetFloati_vNV wrap_glGetFloati_vNV +#define glGetFloatv wrap_glGetFloatv +#define glGetFragDataIndexEXT wrap_glGetFragDataIndexEXT +#define glGetFragDataLocation wrap_glGetFragDataLocation +#define glGetFramebufferAttachmentParameteriv wrap_glGetFramebufferAttachmentParameteriv +#define glGetFramebufferAttachmentParameterivOES wrap_glGetFramebufferAttachmentParameterivOES +#define glGetFramebufferParameteriv wrap_glGetFramebufferParameteriv +#define glGetGraphicsResetStatus wrap_glGetGraphicsResetStatus +#define glGetGraphicsResetStatusEXT wrap_glGetGraphicsResetStatusEXT +#define glGetGraphicsResetStatusKHR wrap_glGetGraphicsResetStatusKHR +#define glGetImageHandleNV wrap_glGetImageHandleNV +#define glGetInteger64i_v wrap_glGetInteger64i_v +#define glGetInteger64v wrap_glGetInteger64v +#define glGetInteger64vAPPLE wrap_glGetInteger64vAPPLE +#define glGetIntegeri_v wrap_glGetIntegeri_v +#define glGetIntegeri_vEXT wrap_glGetIntegeri_vEXT +#define glGetIntegerv wrap_glGetIntegerv +#define glGetInternalformatSampleivNV wrap_glGetInternalformatSampleivNV +#define glGetInternalformativ wrap_glGetInternalformativ +#define glGetLightfv wrap_glGetLightfv +#define glGetLightxv wrap_glGetLightxv +#define glGetLightxvOES wrap_glGetLightxvOES +#define glGetMaterialfv wrap_glGetMaterialfv +#define glGetMaterialxv wrap_glGetMaterialxv +#define glGetMaterialxvOES wrap_glGetMaterialxvOES +#define glGetMultisamplefv wrap_glGetMultisamplefv +#define glGetNextPerfQueryIdINTEL wrap_glGetNextPerfQueryIdINTEL +#define glGetObjectLabel wrap_glGetObjectLabel +#define glGetObjectLabelEXT wrap_glGetObjectLabelEXT +#define glGetObjectLabelKHR wrap_glGetObjectLabelKHR +#define glGetObjectPtrLabel wrap_glGetObjectPtrLabel +#define glGetObjectPtrLabelKHR wrap_glGetObjectPtrLabelKHR +#define glGetPathCommandsNV wrap_glGetPathCommandsNV +#define glGetPathCoordsNV wrap_glGetPathCoordsNV +#define glGetPathDashArrayNV wrap_glGetPathDashArrayNV +#define glGetPathLengthNV wrap_glGetPathLengthNV +#define glGetPathMetricRangeNV wrap_glGetPathMetricRangeNV +#define glGetPathMetricsNV wrap_glGetPathMetricsNV +#define glGetPathParameterfvNV wrap_glGetPathParameterfvNV +#define glGetPathParameterivNV wrap_glGetPathParameterivNV +#define glGetPathSpacingNV wrap_glGetPathSpacingNV +#define glGetPerfCounterInfoINTEL wrap_glGetPerfCounterInfoINTEL +#define glGetPerfMonitorCounterDataAMD wrap_glGetPerfMonitorCounterDataAMD +#define glGetPerfMonitorCounterInfoAMD wrap_glGetPerfMonitorCounterInfoAMD +#define glGetPerfMonitorCounterStringAMD wrap_glGetPerfMonitorCounterStringAMD +#define glGetPerfMonitorCountersAMD wrap_glGetPerfMonitorCountersAMD +#define glGetPerfMonitorGroupStringAMD wrap_glGetPerfMonitorGroupStringAMD +#define glGetPerfMonitorGroupsAMD wrap_glGetPerfMonitorGroupsAMD +#define glGetPerfQueryDataINTEL wrap_glGetPerfQueryDataINTEL +#define glGetPerfQueryIdByNameINTEL wrap_glGetPerfQueryIdByNameINTEL +#define glGetPerfQueryInfoINTEL wrap_glGetPerfQueryInfoINTEL +#define glGetPointerv wrap_glGetPointerv +#define glGetPointervKHR wrap_glGetPointervKHR +#define glGetProgramBinary wrap_glGetProgramBinary +#define glGetProgramBinaryOES wrap_glGetProgramBinaryOES +#define glGetProgramInfoLog wrap_glGetProgramInfoLog +#define glGetProgramInterfaceiv wrap_glGetProgramInterfaceiv +#define glGetProgramPipelineInfoLog wrap_glGetProgramPipelineInfoLog +#define glGetProgramPipelineInfoLogEXT wrap_glGetProgramPipelineInfoLogEXT +#define glGetProgramPipelineiv wrap_glGetProgramPipelineiv +#define glGetProgramPipelineivEXT wrap_glGetProgramPipelineivEXT +#define glGetProgramResourceIndex wrap_glGetProgramResourceIndex +#define glGetProgramResourceLocation wrap_glGetProgramResourceLocation +#define glGetProgramResourceLocationIndexEXT wrap_glGetProgramResourceLocationIndexEXT +#define glGetProgramResourceName wrap_glGetProgramResourceName +#define glGetProgramResourcefvNV wrap_glGetProgramResourcefvNV +#define glGetProgramResourceiv wrap_glGetProgramResourceiv +#define glGetProgramiv wrap_glGetProgramiv +#define glGetQueryObjecti64vEXT wrap_glGetQueryObjecti64vEXT +#define glGetQueryObjectivEXT wrap_glGetQueryObjectivEXT +#define glGetQueryObjectui64vEXT wrap_glGetQueryObjectui64vEXT +#define glGetQueryObjectuiv wrap_glGetQueryObjectuiv +#define glGetQueryObjectuivEXT wrap_glGetQueryObjectuivEXT +#define glGetQueryiv wrap_glGetQueryiv +#define glGetQueryivEXT wrap_glGetQueryivEXT +#define glGetRenderbufferParameteriv wrap_glGetRenderbufferParameteriv +#define glGetRenderbufferParameterivOES wrap_glGetRenderbufferParameterivOES +#define glGetSamplerParameterIiv wrap_glGetSamplerParameterIiv +#define glGetSamplerParameterIivEXT wrap_glGetSamplerParameterIivEXT +#define glGetSamplerParameterIivOES wrap_glGetSamplerParameterIivOES +#define glGetSamplerParameterIuiv wrap_glGetSamplerParameterIuiv +#define glGetSamplerParameterIuivEXT wrap_glGetSamplerParameterIuivEXT +#define glGetSamplerParameterIuivOES wrap_glGetSamplerParameterIuivOES +#define glGetSamplerParameterfv wrap_glGetSamplerParameterfv +#define glGetSamplerParameteriv wrap_glGetSamplerParameteriv +#define glGetShaderInfoLog wrap_glGetShaderInfoLog +#define glGetShaderPrecisionFormat wrap_glGetShaderPrecisionFormat +#define glGetShaderSource wrap_glGetShaderSource +#define glGetShaderiv wrap_glGetShaderiv +#define glGetString wrap_glGetString +#define glGetStringi wrap_glGetStringi +#define glGetSynciv wrap_glGetSynciv +#define glGetSyncivAPPLE wrap_glGetSyncivAPPLE +#define glGetTexEnvfv wrap_glGetTexEnvfv +#define glGetTexEnviv wrap_glGetTexEnviv +#define glGetTexEnvxv wrap_glGetTexEnvxv +#define glGetTexEnvxvOES wrap_glGetTexEnvxvOES +#define glGetTexGenfvOES wrap_glGetTexGenfvOES +#define glGetTexGenivOES wrap_glGetTexGenivOES +#define glGetTexGenxvOES wrap_glGetTexGenxvOES +#define glGetTexLevelParameterfv wrap_glGetTexLevelParameterfv +#define glGetTexLevelParameteriv wrap_glGetTexLevelParameteriv +#define glGetTexParameterIiv wrap_glGetTexParameterIiv +#define glGetTexParameterIivEXT wrap_glGetTexParameterIivEXT +#define glGetTexParameterIivOES wrap_glGetTexParameterIivOES +#define glGetTexParameterIuiv wrap_glGetTexParameterIuiv +#define glGetTexParameterIuivEXT wrap_glGetTexParameterIuivEXT +#define glGetTexParameterIuivOES wrap_glGetTexParameterIuivOES +#define glGetTexParameterfv wrap_glGetTexParameterfv +#define glGetTexParameteriv wrap_glGetTexParameteriv +#define glGetTexParameterxv wrap_glGetTexParameterxv +#define glGetTexParameterxvOES wrap_glGetTexParameterxvOES +#define glGetTextureHandleNV wrap_glGetTextureHandleNV +#define glGetTextureSamplerHandleNV wrap_glGetTextureSamplerHandleNV +#define glGetTransformFeedbackVarying wrap_glGetTransformFeedbackVarying +#define glGetTranslatedShaderSourceANGLE wrap_glGetTranslatedShaderSourceANGLE +#define glGetUniformBlockIndex wrap_glGetUniformBlockIndex +#define glGetUniformIndices wrap_glGetUniformIndices +#define glGetUniformLocation wrap_glGetUniformLocation +#define glGetUniformfv wrap_glGetUniformfv +#define glGetUniformiv wrap_glGetUniformiv +#define glGetUniformuiv wrap_glGetUniformuiv +#define glGetVertexAttribIiv wrap_glGetVertexAttribIiv +#define glGetVertexAttribIuiv wrap_glGetVertexAttribIuiv +#define glGetVertexAttribPointerv wrap_glGetVertexAttribPointerv +#define glGetVertexAttribfv wrap_glGetVertexAttribfv +#define glGetVertexAttribiv wrap_glGetVertexAttribiv +#define glGetnUniformfv wrap_glGetnUniformfv +#define glGetnUniformfvEXT wrap_glGetnUniformfvEXT +#define glGetnUniformfvKHR wrap_glGetnUniformfvKHR +#define glGetnUniformiv wrap_glGetnUniformiv +#define glGetnUniformivEXT wrap_glGetnUniformivEXT +#define glGetnUniformivKHR wrap_glGetnUniformivKHR +#define glGetnUniformuiv wrap_glGetnUniformuiv +#define glGetnUniformuivKHR wrap_glGetnUniformuivKHR +#define glHint wrap_glHint +#define glInsertEventMarkerEXT wrap_glInsertEventMarkerEXT +#define glInterpolatePathsNV wrap_glInterpolatePathsNV +#define glInvalidateFramebuffer wrap_glInvalidateFramebuffer +#define glInvalidateSubFramebuffer wrap_glInvalidateSubFramebuffer +#define glIsBuffer wrap_glIsBuffer +#define glIsEnabled wrap_glIsEnabled +#define glIsEnabledi wrap_glIsEnabledi +#define glIsEnablediEXT wrap_glIsEnablediEXT +#define glIsEnablediNV wrap_glIsEnablediNV +#define glIsEnablediOES wrap_glIsEnablediOES +#define glIsFenceNV wrap_glIsFenceNV +#define glIsFramebuffer wrap_glIsFramebuffer +#define glIsFramebufferOES wrap_glIsFramebufferOES +#define glIsImageHandleResidentNV wrap_glIsImageHandleResidentNV +#define glIsPathNV wrap_glIsPathNV +#define glIsPointInFillPathNV wrap_glIsPointInFillPathNV +#define glIsPointInStrokePathNV wrap_glIsPointInStrokePathNV +#define glIsProgram wrap_glIsProgram +#define glIsProgramPipeline wrap_glIsProgramPipeline +#define glIsProgramPipelineEXT wrap_glIsProgramPipelineEXT +#define glIsQuery wrap_glIsQuery +#define glIsQueryEXT wrap_glIsQueryEXT +#define glIsRenderbuffer wrap_glIsRenderbuffer +#define glIsRenderbufferOES wrap_glIsRenderbufferOES +#define glIsSampler wrap_glIsSampler +#define glIsShader wrap_glIsShader +#define glIsSync wrap_glIsSync +#define glIsSyncAPPLE wrap_glIsSyncAPPLE +#define glIsTexture wrap_glIsTexture +#define glIsTextureHandleResidentNV wrap_glIsTextureHandleResidentNV +#define glIsTransformFeedback wrap_glIsTransformFeedback +#define glIsVertexArray wrap_glIsVertexArray +#define glIsVertexArrayOES wrap_glIsVertexArrayOES +#define glLabelObjectEXT wrap_glLabelObjectEXT +#define glLightModelf wrap_glLightModelf +#define glLightModelfv wrap_glLightModelfv +#define glLightModelx wrap_glLightModelx +#define glLightModelxOES wrap_glLightModelxOES +#define glLightModelxv wrap_glLightModelxv +#define glLightModelxvOES wrap_glLightModelxvOES +#define glLightf wrap_glLightf +#define glLightfv wrap_glLightfv +#define glLightx wrap_glLightx +#define glLightxOES wrap_glLightxOES +#define glLightxv wrap_glLightxv +#define glLightxvOES wrap_glLightxvOES +#define glLineWidth wrap_glLineWidth +#define glLineWidthx wrap_glLineWidthx +#define glLineWidthxOES wrap_glLineWidthxOES +#define glLinkProgram wrap_glLinkProgram +#define glLoadIdentity wrap_glLoadIdentity +#define glLoadMatrixf wrap_glLoadMatrixf +#define glLoadMatrixx wrap_glLoadMatrixx +#define glLoadMatrixxOES wrap_glLoadMatrixxOES +#define glLoadPaletteFromModelViewMatrixOES wrap_glLoadPaletteFromModelViewMatrixOES +#define glLogicOp wrap_glLogicOp +#define glMakeImageHandleNonResidentNV wrap_glMakeImageHandleNonResidentNV +#define glMakeImageHandleResidentNV wrap_glMakeImageHandleResidentNV +#define glMakeTextureHandleNonResidentNV wrap_glMakeTextureHandleNonResidentNV +#define glMakeTextureHandleResidentNV wrap_glMakeTextureHandleResidentNV +#define glMapBufferOES wrap_glMapBufferOES +#define glMapBufferRange wrap_glMapBufferRange +#define glMapBufferRangeEXT wrap_glMapBufferRangeEXT +#define glMaterialf wrap_glMaterialf +#define glMaterialfv wrap_glMaterialfv +#define glMaterialx wrap_glMaterialx +#define glMaterialxOES wrap_glMaterialxOES +#define glMaterialxv wrap_glMaterialxv +#define glMaterialxvOES wrap_glMaterialxvOES +#define glMatrixIndexPointerOES wrap_glMatrixIndexPointerOES +#define glMatrixLoad3x2fNV wrap_glMatrixLoad3x2fNV +#define glMatrixLoad3x3fNV wrap_glMatrixLoad3x3fNV +#define glMatrixLoadTranspose3x3fNV wrap_glMatrixLoadTranspose3x3fNV +#define glMatrixMode wrap_glMatrixMode +#define glMatrixMult3x2fNV wrap_glMatrixMult3x2fNV +#define glMatrixMult3x3fNV wrap_glMatrixMult3x3fNV +#define glMatrixMultTranspose3x3fNV wrap_glMatrixMultTranspose3x3fNV +#define glMemoryBarrier wrap_glMemoryBarrier +#define glMemoryBarrierByRegion wrap_glMemoryBarrierByRegion +#define glMinSampleShading wrap_glMinSampleShading +#define glMinSampleShadingOES wrap_glMinSampleShadingOES +#define glMultMatrixf wrap_glMultMatrixf +#define glMultMatrixx wrap_glMultMatrixx +#define glMultMatrixxOES wrap_glMultMatrixxOES +#define glMultiDrawArraysEXT wrap_glMultiDrawArraysEXT +#define glMultiDrawArraysIndirectEXT wrap_glMultiDrawArraysIndirectEXT +#define glMultiDrawElementsBaseVertexEXT wrap_glMultiDrawElementsBaseVertexEXT +#define glMultiDrawElementsBaseVertexOES wrap_glMultiDrawElementsBaseVertexOES +#define glMultiDrawElementsEXT wrap_glMultiDrawElementsEXT +#define glMultiDrawElementsIndirectEXT wrap_glMultiDrawElementsIndirectEXT +#define glMultiTexCoord4f wrap_glMultiTexCoord4f +#define glMultiTexCoord4x wrap_glMultiTexCoord4x +#define glMultiTexCoord4xOES wrap_glMultiTexCoord4xOES +#define glNamedFramebufferSampleLocationsfvNV wrap_glNamedFramebufferSampleLocationsfvNV +#define glNormal3f wrap_glNormal3f +#define glNormal3x wrap_glNormal3x +#define glNormal3xOES wrap_glNormal3xOES +#define glNormalPointer wrap_glNormalPointer +#define glObjectLabel wrap_glObjectLabel +#define glObjectLabelKHR wrap_glObjectLabelKHR +#define glObjectPtrLabel wrap_glObjectPtrLabel +#define glObjectPtrLabelKHR wrap_glObjectPtrLabelKHR +#define glOrthof wrap_glOrthof +#define glOrthofOES wrap_glOrthofOES +#define glOrthox wrap_glOrthox +#define glOrthoxOES wrap_glOrthoxOES +#define glPatchParameteri wrap_glPatchParameteri +#define glPatchParameteriEXT wrap_glPatchParameteriEXT +#define glPatchParameteriOES wrap_glPatchParameteriOES +#define glPathCommandsNV wrap_glPathCommandsNV +#define glPathCoordsNV wrap_glPathCoordsNV +#define glPathCoverDepthFuncNV wrap_glPathCoverDepthFuncNV +#define glPathDashArrayNV wrap_glPathDashArrayNV +#define glPathGlyphIndexArrayNV wrap_glPathGlyphIndexArrayNV +#define glPathGlyphIndexRangeNV wrap_glPathGlyphIndexRangeNV +#define glPathGlyphRangeNV wrap_glPathGlyphRangeNV +#define glPathGlyphsNV wrap_glPathGlyphsNV +#define glPathMemoryGlyphIndexArrayNV wrap_glPathMemoryGlyphIndexArrayNV +#define glPathParameterfNV wrap_glPathParameterfNV +#define glPathParameterfvNV wrap_glPathParameterfvNV +#define glPathParameteriNV wrap_glPathParameteriNV +#define glPathParameterivNV wrap_glPathParameterivNV +#define glPathStencilDepthOffsetNV wrap_glPathStencilDepthOffsetNV +#define glPathStencilFuncNV wrap_glPathStencilFuncNV +#define glPathStringNV wrap_glPathStringNV +#define glPathSubCommandsNV wrap_glPathSubCommandsNV +#define glPathSubCoordsNV wrap_glPathSubCoordsNV +#define glPauseTransformFeedback wrap_glPauseTransformFeedback +#define glPixelStorei wrap_glPixelStorei +#define glPointAlongPathNV wrap_glPointAlongPathNV +#define glPointParameterf wrap_glPointParameterf +#define glPointParameterfv wrap_glPointParameterfv +#define glPointParameterx wrap_glPointParameterx +#define glPointParameterxOES wrap_glPointParameterxOES +#define glPointParameterxv wrap_glPointParameterxv +#define glPointParameterxvOES wrap_glPointParameterxvOES +#define glPointSize wrap_glPointSize +#define glPointSizePointerOES wrap_glPointSizePointerOES +#define glPointSizex wrap_glPointSizex +#define glPointSizexOES wrap_glPointSizexOES +#define glPolygonModeNV wrap_glPolygonModeNV +#define glPolygonOffset wrap_glPolygonOffset +#define glPolygonOffsetx wrap_glPolygonOffsetx +#define glPolygonOffsetxOES wrap_glPolygonOffsetxOES +#define glPopDebugGroup wrap_glPopDebugGroup +#define glPopDebugGroupKHR wrap_glPopDebugGroupKHR +#define glPopGroupMarkerEXT wrap_glPopGroupMarkerEXT +#define glPopMatrix wrap_glPopMatrix +#define glPrimitiveBoundingBox wrap_glPrimitiveBoundingBox +#define glPrimitiveBoundingBoxEXT wrap_glPrimitiveBoundingBoxEXT +#define glPrimitiveBoundingBoxOES wrap_glPrimitiveBoundingBoxOES +#define glProgramBinary wrap_glProgramBinary +#define glProgramBinaryOES wrap_glProgramBinaryOES +#define glProgramParameteri wrap_glProgramParameteri +#define glProgramParameteriEXT wrap_glProgramParameteriEXT +#define glProgramPathFragmentInputGenNV wrap_glProgramPathFragmentInputGenNV +#define glProgramUniform1f wrap_glProgramUniform1f +#define glProgramUniform1fEXT wrap_glProgramUniform1fEXT +#define glProgramUniform1fv wrap_glProgramUniform1fv +#define glProgramUniform1fvEXT wrap_glProgramUniform1fvEXT +#define glProgramUniform1i wrap_glProgramUniform1i +#define glProgramUniform1iEXT wrap_glProgramUniform1iEXT +#define glProgramUniform1iv wrap_glProgramUniform1iv +#define glProgramUniform1ivEXT wrap_glProgramUniform1ivEXT +#define glProgramUniform1ui wrap_glProgramUniform1ui +#define glProgramUniform1uiEXT wrap_glProgramUniform1uiEXT +#define glProgramUniform1uiv wrap_glProgramUniform1uiv +#define glProgramUniform1uivEXT wrap_glProgramUniform1uivEXT +#define glProgramUniform2f wrap_glProgramUniform2f +#define glProgramUniform2fEXT wrap_glProgramUniform2fEXT +#define glProgramUniform2fv wrap_glProgramUniform2fv +#define glProgramUniform2fvEXT wrap_glProgramUniform2fvEXT +#define glProgramUniform2i wrap_glProgramUniform2i +#define glProgramUniform2iEXT wrap_glProgramUniform2iEXT +#define glProgramUniform2iv wrap_glProgramUniform2iv +#define glProgramUniform2ivEXT wrap_glProgramUniform2ivEXT +#define glProgramUniform2ui wrap_glProgramUniform2ui +#define glProgramUniform2uiEXT wrap_glProgramUniform2uiEXT +#define glProgramUniform2uiv wrap_glProgramUniform2uiv +#define glProgramUniform2uivEXT wrap_glProgramUniform2uivEXT +#define glProgramUniform3f wrap_glProgramUniform3f +#define glProgramUniform3fEXT wrap_glProgramUniform3fEXT +#define glProgramUniform3fv wrap_glProgramUniform3fv +#define glProgramUniform3fvEXT wrap_glProgramUniform3fvEXT +#define glProgramUniform3i wrap_glProgramUniform3i +#define glProgramUniform3iEXT wrap_glProgramUniform3iEXT +#define glProgramUniform3iv wrap_glProgramUniform3iv +#define glProgramUniform3ivEXT wrap_glProgramUniform3ivEXT +#define glProgramUniform3ui wrap_glProgramUniform3ui +#define glProgramUniform3uiEXT wrap_glProgramUniform3uiEXT +#define glProgramUniform3uiv wrap_glProgramUniform3uiv +#define glProgramUniform3uivEXT wrap_glProgramUniform3uivEXT +#define glProgramUniform4f wrap_glProgramUniform4f +#define glProgramUniform4fEXT wrap_glProgramUniform4fEXT +#define glProgramUniform4fv wrap_glProgramUniform4fv +#define glProgramUniform4fvEXT wrap_glProgramUniform4fvEXT +#define glProgramUniform4i wrap_glProgramUniform4i +#define glProgramUniform4iEXT wrap_glProgramUniform4iEXT +#define glProgramUniform4iv wrap_glProgramUniform4iv +#define glProgramUniform4ivEXT wrap_glProgramUniform4ivEXT +#define glProgramUniform4ui wrap_glProgramUniform4ui +#define glProgramUniform4uiEXT wrap_glProgramUniform4uiEXT +#define glProgramUniform4uiv wrap_glProgramUniform4uiv +#define glProgramUniform4uivEXT wrap_glProgramUniform4uivEXT +#define glProgramUniformHandleui64NV wrap_glProgramUniformHandleui64NV +#define glProgramUniformHandleui64vNV wrap_glProgramUniformHandleui64vNV +#define glProgramUniformMatrix2fv wrap_glProgramUniformMatrix2fv +#define glProgramUniformMatrix2fvEXT wrap_glProgramUniformMatrix2fvEXT +#define glProgramUniformMatrix2x3fv wrap_glProgramUniformMatrix2x3fv +#define glProgramUniformMatrix2x3fvEXT wrap_glProgramUniformMatrix2x3fvEXT +#define glProgramUniformMatrix2x4fv wrap_glProgramUniformMatrix2x4fv +#define glProgramUniformMatrix2x4fvEXT wrap_glProgramUniformMatrix2x4fvEXT +#define glProgramUniformMatrix3fv wrap_glProgramUniformMatrix3fv +#define glProgramUniformMatrix3fvEXT wrap_glProgramUniformMatrix3fvEXT +#define glProgramUniformMatrix3x2fv wrap_glProgramUniformMatrix3x2fv +#define glProgramUniformMatrix3x2fvEXT wrap_glProgramUniformMatrix3x2fvEXT +#define glProgramUniformMatrix3x4fv wrap_glProgramUniformMatrix3x4fv +#define glProgramUniformMatrix3x4fvEXT wrap_glProgramUniformMatrix3x4fvEXT +#define glProgramUniformMatrix4fv wrap_glProgramUniformMatrix4fv +#define glProgramUniformMatrix4fvEXT wrap_glProgramUniformMatrix4fvEXT +#define glProgramUniformMatrix4x2fv wrap_glProgramUniformMatrix4x2fv +#define glProgramUniformMatrix4x2fvEXT wrap_glProgramUniformMatrix4x2fvEXT +#define glProgramUniformMatrix4x3fv wrap_glProgramUniformMatrix4x3fv +#define glProgramUniformMatrix4x3fvEXT wrap_glProgramUniformMatrix4x3fvEXT +#define glPushDebugGroup wrap_glPushDebugGroup +#define glPushDebugGroupKHR wrap_glPushDebugGroupKHR +#define glPushGroupMarkerEXT wrap_glPushGroupMarkerEXT +#define glPushMatrix wrap_glPushMatrix +#define glQueryCounterEXT wrap_glQueryCounterEXT +#define glQueryMatrixxOES wrap_glQueryMatrixxOES +#define glRasterSamplesEXT wrap_glRasterSamplesEXT +#define glReadBuffer wrap_glReadBuffer +#define glReadBufferIndexedEXT wrap_glReadBufferIndexedEXT +#define glReadBufferNV wrap_glReadBufferNV +#define glReadPixels wrap_glReadPixels +#define glReadnPixels wrap_glReadnPixels +#define glReadnPixelsEXT wrap_glReadnPixelsEXT +#define glReadnPixelsKHR wrap_glReadnPixelsKHR +#define glReleaseShaderCompiler wrap_glReleaseShaderCompiler +#define glRenderbufferStorage wrap_glRenderbufferStorage +#define glRenderbufferStorageMultisample wrap_glRenderbufferStorageMultisample +#define glRenderbufferStorageMultisampleANGLE wrap_glRenderbufferStorageMultisampleANGLE +#define glRenderbufferStorageMultisampleAPPLE wrap_glRenderbufferStorageMultisampleAPPLE +#define glRenderbufferStorageMultisampleEXT wrap_glRenderbufferStorageMultisampleEXT +#define glRenderbufferStorageMultisampleIMG wrap_glRenderbufferStorageMultisampleIMG +#define glRenderbufferStorageMultisampleNV wrap_glRenderbufferStorageMultisampleNV +#define glRenderbufferStorageOES wrap_glRenderbufferStorageOES +#define glResolveDepthValuesNV wrap_glResolveDepthValuesNV +#define glResolveMultisampleFramebufferAPPLE wrap_glResolveMultisampleFramebufferAPPLE +#define glResumeTransformFeedback wrap_glResumeTransformFeedback +#define glRotatef wrap_glRotatef +#define glRotatex wrap_glRotatex +#define glRotatexOES wrap_glRotatexOES +#define glSampleCoverage wrap_glSampleCoverage +#define glSampleCoveragex wrap_glSampleCoveragex +#define glSampleCoveragexOES wrap_glSampleCoveragexOES +#define glSampleMaski wrap_glSampleMaski +#define glSamplerParameterIiv wrap_glSamplerParameterIiv +#define glSamplerParameterIivEXT wrap_glSamplerParameterIivEXT +#define glSamplerParameterIivOES wrap_glSamplerParameterIivOES +#define glSamplerParameterIuiv wrap_glSamplerParameterIuiv +#define glSamplerParameterIuivEXT wrap_glSamplerParameterIuivEXT +#define glSamplerParameterIuivOES wrap_glSamplerParameterIuivOES +#define glSamplerParameterf wrap_glSamplerParameterf +#define glSamplerParameterfv wrap_glSamplerParameterfv +#define glSamplerParameteri wrap_glSamplerParameteri +#define glSamplerParameteriv wrap_glSamplerParameteriv +#define glScalef wrap_glScalef +#define glScalex wrap_glScalex +#define glScalexOES wrap_glScalexOES +#define glScissor wrap_glScissor +#define glScissorArrayvNV wrap_glScissorArrayvNV +#define glScissorIndexedNV wrap_glScissorIndexedNV +#define glScissorIndexedvNV wrap_glScissorIndexedvNV +#define glSelectPerfMonitorCountersAMD wrap_glSelectPerfMonitorCountersAMD +#define glSetFenceNV wrap_glSetFenceNV +#define glShadeModel wrap_glShadeModel +#define glShaderBinary wrap_glShaderBinary +#define glShaderSource wrap_glShaderSource +#define glStartTilingQCOM wrap_glStartTilingQCOM +#define glStencilFillPathInstancedNV wrap_glStencilFillPathInstancedNV +#define glStencilFillPathNV wrap_glStencilFillPathNV +#define glStencilFunc wrap_glStencilFunc +#define glStencilFuncSeparate wrap_glStencilFuncSeparate +#define glStencilMask wrap_glStencilMask +#define glStencilMaskSeparate wrap_glStencilMaskSeparate +#define glStencilOp wrap_glStencilOp +#define glStencilOpSeparate wrap_glStencilOpSeparate +#define glStencilStrokePathInstancedNV wrap_glStencilStrokePathInstancedNV +#define glStencilStrokePathNV wrap_glStencilStrokePathNV +#define glStencilThenCoverFillPathInstancedNV wrap_glStencilThenCoverFillPathInstancedNV +#define glStencilThenCoverFillPathNV wrap_glStencilThenCoverFillPathNV +#define glStencilThenCoverStrokePathInstancedNV wrap_glStencilThenCoverStrokePathInstancedNV +#define glStencilThenCoverStrokePathNV wrap_glStencilThenCoverStrokePathNV +#define glSubpixelPrecisionBiasNV wrap_glSubpixelPrecisionBiasNV +#define glTestFenceNV wrap_glTestFenceNV +#define glTexBuffer wrap_glTexBuffer +#define glTexBufferEXT wrap_glTexBufferEXT +#define glTexBufferOES wrap_glTexBufferOES +#define glTexBufferRange wrap_glTexBufferRange +#define glTexBufferRangeEXT wrap_glTexBufferRangeEXT +#define glTexBufferRangeOES wrap_glTexBufferRangeOES +#define glTexCoordPointer wrap_glTexCoordPointer +#define glTexEnvf wrap_glTexEnvf +#define glTexEnvfv wrap_glTexEnvfv +#define glTexEnvi wrap_glTexEnvi +#define glTexEnviv wrap_glTexEnviv +#define glTexEnvx wrap_glTexEnvx +#define glTexEnvxOES wrap_glTexEnvxOES +#define glTexEnvxv wrap_glTexEnvxv +#define glTexEnvxvOES wrap_glTexEnvxvOES +#define glTexGenfOES wrap_glTexGenfOES +#define glTexGenfvOES wrap_glTexGenfvOES +#define glTexGeniOES wrap_glTexGeniOES +#define glTexGenivOES wrap_glTexGenivOES +#define glTexGenxOES wrap_glTexGenxOES +#define glTexGenxvOES wrap_glTexGenxvOES +#define glTexImage2D wrap_glTexImage2D +#define glTexImage3D wrap_glTexImage3D +#define glTexImage3DOES wrap_glTexImage3DOES +#define glTexPageCommitmentEXT wrap_glTexPageCommitmentEXT +#define glTexParameterIiv wrap_glTexParameterIiv +#define glTexParameterIivEXT wrap_glTexParameterIivEXT +#define glTexParameterIivOES wrap_glTexParameterIivOES +#define glTexParameterIuiv wrap_glTexParameterIuiv +#define glTexParameterIuivEXT wrap_glTexParameterIuivEXT +#define glTexParameterIuivOES wrap_glTexParameterIuivOES +#define glTexParameterf wrap_glTexParameterf +#define glTexParameterfv wrap_glTexParameterfv +#define glTexParameteri wrap_glTexParameteri +#define glTexParameteriv wrap_glTexParameteriv +#define glTexParameterx wrap_glTexParameterx +#define glTexParameterxOES wrap_glTexParameterxOES +#define glTexParameterxv wrap_glTexParameterxv +#define glTexParameterxvOES wrap_glTexParameterxvOES +#define glTexStorage1DEXT wrap_glTexStorage1DEXT +#define glTexStorage2D wrap_glTexStorage2D +#define glTexStorage2DEXT wrap_glTexStorage2DEXT +#define glTexStorage2DMultisample wrap_glTexStorage2DMultisample +#define glTexStorage3D wrap_glTexStorage3D +#define glTexStorage3DEXT wrap_glTexStorage3DEXT +#define glTexStorage3DMultisample wrap_glTexStorage3DMultisample +#define glTexStorage3DMultisampleOES wrap_glTexStorage3DMultisampleOES +#define glTexSubImage2D wrap_glTexSubImage2D +#define glTexSubImage3D wrap_glTexSubImage3D +#define glTexSubImage3DOES wrap_glTexSubImage3DOES +#define glTextureStorage1DEXT wrap_glTextureStorage1DEXT +#define glTextureStorage2DEXT wrap_glTextureStorage2DEXT +#define glTextureStorage3DEXT wrap_glTextureStorage3DEXT +#define glTextureViewEXT wrap_glTextureViewEXT +#define glTextureViewOES wrap_glTextureViewOES +#define glTransformFeedbackVaryings wrap_glTransformFeedbackVaryings +#define glTransformPathNV wrap_glTransformPathNV +#define glTranslatef wrap_glTranslatef +#define glTranslatex wrap_glTranslatex +#define glTranslatexOES wrap_glTranslatexOES +#define glUniform1f wrap_glUniform1f +#define glUniform1fv wrap_glUniform1fv +#define glUniform1i wrap_glUniform1i +#define glUniform1iv wrap_glUniform1iv +#define glUniform1ui wrap_glUniform1ui +#define glUniform1uiv wrap_glUniform1uiv +#define glUniform2f wrap_glUniform2f +#define glUniform2fv wrap_glUniform2fv +#define glUniform2i wrap_glUniform2i +#define glUniform2iv wrap_glUniform2iv +#define glUniform2ui wrap_glUniform2ui +#define glUniform2uiv wrap_glUniform2uiv +#define glUniform3f wrap_glUniform3f +#define glUniform3fv wrap_glUniform3fv +#define glUniform3i wrap_glUniform3i +#define glUniform3iv wrap_glUniform3iv +#define glUniform3ui wrap_glUniform3ui +#define glUniform3uiv wrap_glUniform3uiv +#define glUniform4f wrap_glUniform4f +#define glUniform4fv wrap_glUniform4fv +#define glUniform4i wrap_glUniform4i +#define glUniform4iv wrap_glUniform4iv +#define glUniform4ui wrap_glUniform4ui +#define glUniform4uiv wrap_glUniform4uiv +#define glUniformBlockBinding wrap_glUniformBlockBinding +#define glUniformHandleui64NV wrap_glUniformHandleui64NV +#define glUniformHandleui64vNV wrap_glUniformHandleui64vNV +#define glUniformMatrix2fv wrap_glUniformMatrix2fv +#define glUniformMatrix2x3fv wrap_glUniformMatrix2x3fv +#define glUniformMatrix2x3fvNV wrap_glUniformMatrix2x3fvNV +#define glUniformMatrix2x4fv wrap_glUniformMatrix2x4fv +#define glUniformMatrix2x4fvNV wrap_glUniformMatrix2x4fvNV +#define glUniformMatrix3fv wrap_glUniformMatrix3fv +#define glUniformMatrix3x2fv wrap_glUniformMatrix3x2fv +#define glUniformMatrix3x2fvNV wrap_glUniformMatrix3x2fvNV +#define glUniformMatrix3x4fv wrap_glUniformMatrix3x4fv +#define glUniformMatrix3x4fvNV wrap_glUniformMatrix3x4fvNV +#define glUniformMatrix4fv wrap_glUniformMatrix4fv +#define glUniformMatrix4x2fv wrap_glUniformMatrix4x2fv +#define glUniformMatrix4x2fvNV wrap_glUniformMatrix4x2fvNV +#define glUniformMatrix4x3fv wrap_glUniformMatrix4x3fv +#define glUniformMatrix4x3fvNV wrap_glUniformMatrix4x3fvNV +#define glUnmapBuffer wrap_glUnmapBuffer +#define glUnmapBufferOES wrap_glUnmapBufferOES +#define glUseProgram wrap_glUseProgram +#define glUseProgramStages wrap_glUseProgramStages +#define glUseProgramStagesEXT wrap_glUseProgramStagesEXT +#define glValidateProgram wrap_glValidateProgram +#define glValidateProgramPipeline wrap_glValidateProgramPipeline +#define glValidateProgramPipelineEXT wrap_glValidateProgramPipelineEXT +#define glVertexAttrib1f wrap_glVertexAttrib1f +#define glVertexAttrib1fv wrap_glVertexAttrib1fv +#define glVertexAttrib2f wrap_glVertexAttrib2f +#define glVertexAttrib2fv wrap_glVertexAttrib2fv +#define glVertexAttrib3f wrap_glVertexAttrib3f +#define glVertexAttrib3fv wrap_glVertexAttrib3fv +#define glVertexAttrib4f wrap_glVertexAttrib4f +#define glVertexAttrib4fv wrap_glVertexAttrib4fv +#define glVertexAttribBinding wrap_glVertexAttribBinding +#define glVertexAttribDivisor wrap_glVertexAttribDivisor +#define glVertexAttribDivisorANGLE wrap_glVertexAttribDivisorANGLE +#define glVertexAttribDivisorEXT wrap_glVertexAttribDivisorEXT +#define glVertexAttribDivisorNV wrap_glVertexAttribDivisorNV +#define glVertexAttribFormat wrap_glVertexAttribFormat +#define glVertexAttribI4i wrap_glVertexAttribI4i +#define glVertexAttribI4iv wrap_glVertexAttribI4iv +#define glVertexAttribI4ui wrap_glVertexAttribI4ui +#define glVertexAttribI4uiv wrap_glVertexAttribI4uiv +#define glVertexAttribIFormat wrap_glVertexAttribIFormat +#define glVertexAttribIPointer wrap_glVertexAttribIPointer +#define glVertexAttribPointer wrap_glVertexAttribPointer +#define glVertexBindingDivisor wrap_glVertexBindingDivisor +#define glVertexPointer wrap_glVertexPointer +#define glViewport wrap_glViewport +#define glViewportArrayvNV wrap_glViewportArrayvNV +#define glViewportIndexedfNV wrap_glViewportIndexedfNV +#define glViewportIndexedfvNV wrap_glViewportIndexedfvNV +#define glWaitSync wrap_glWaitSync +#define glWaitSyncAPPLE wrap_glWaitSyncAPPLE +#define glWeightPathsNV wrap_glWeightPathsNV +#define glWeightPointerOES wrap_glWeightPointerOES + +#endif // HWUI_GLES_WRAP_ENABLED diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index 75dcf16123b5..81363d97876c 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -241,7 +241,7 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) { const Glop::Mesh::Indices& indices = mesh.indices; const Glop::Fill& fill = glop.fill; - GL_CHECKPOINT(); + GL_CHECKPOINT(MODERATE); // --------------------------------------------- // ---------- Program + uniform setup ---------- @@ -286,7 +286,7 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) { roundedOutRadius); } - GL_CHECKPOINT(); + GL_CHECKPOINT(MODERATE); // -------------------------------- // ---------- Mesh setup ---------- @@ -339,7 +339,7 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) { // Shader uniforms SkiaShader::apply(*mCaches, fill.skiaShaderData); - GL_CHECKPOINT(); + GL_CHECKPOINT(MODERATE); Texture* texture = (fill.skiaShaderData.skiaShaderType & kBitmap_SkiaShaderType) ? fill.skiaShaderData.bitmapData.bitmapTexture : nullptr; const AutoTexture autoCleanup(texture); @@ -349,7 +349,7 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) { // ------------------------------------ blend().setFactors(glop.blend.src, glop.blend.dst); - GL_CHECKPOINT(); + GL_CHECKPOINT(MODERATE); // ------------------------------------ // ---------- Actual drawing ---------- @@ -379,7 +379,7 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) { glDrawArrays(mesh.primitiveMode, 0, mesh.elementCount); } - GL_CHECKPOINT(); + GL_CHECKPOINT(MODERATE); // ----------------------------------- // ---------- Mesh teardown ---------- @@ -391,7 +391,7 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) { glDisableVertexAttribArray(colorLocation); } - GL_CHECKPOINT(); + GL_CHECKPOINT(MODERATE); } void RenderState::dump() { diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index cdd2da0d8c2f..e7cf3ecd2b72 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -19,6 +19,7 @@ #include "AnimationContext.h" #include "Caches.h" +#include "Canvas.h" #include "DeferredLayerUpdater.h" #include "EglManager.h" #include "LayerUpdateQueue.h" @@ -214,13 +215,13 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, // node(s) are non client / filler nodes. info.mode = (node.get() == target ? TreeInfo::MODE_FULL : TreeInfo::MODE_RT_ONLY); node->prepareTree(info); - GL_CHECKPOINT(); + GL_CHECKPOINT(MODERATE); } mAnimationContext->runRemainingAnimations(info); - GL_CHECKPOINT(); + GL_CHECKPOINT(MODERATE); freePrefetechedLayers(); - GL_CHECKPOINT(); + GL_CHECKPOINT(MODERATE); if (CC_UNLIKELY(!mNativeWindow.get())) { mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); @@ -344,7 +345,7 @@ void CanvasContext::draw() { #if HWUI_NEW_OPS FrameBuilder frameBuilder(mLayerUpdateQueue, dirty, frame.width(), frame.height(), - mRenderNodes, mLightCenter); + mRenderNodes, mLightCenter, mContentDrawBounds); mLayerUpdateQueue.clear(); BakedOpRenderer renderer(Caches::getInstance(), mRenderThread.renderState(), mOpaque, mLightInfo); @@ -394,7 +395,7 @@ void CanvasContext::draw() { backdropBounds.doIntersect(targetBounds); // Check if we have to draw something on the left side ... if (targetBounds.left < contentBounds.left) { - mCanvas->save(SkCanvas::kClip_SaveFlag); + mCanvas->save(SaveFlags::Clip); if (mCanvas->clipRect(targetBounds.left, targetBounds.top, contentBounds.left, targetBounds.bottom, SkRegion::kIntersect_Op)) { @@ -407,7 +408,7 @@ void CanvasContext::draw() { // ... or on the right side ... if (targetBounds.right > contentBounds.right && !targetBounds.isEmpty()) { - mCanvas->save(SkCanvas::kClip_SaveFlag); + mCanvas->save(SaveFlags::Clip); if (mCanvas->clipRect(contentBounds.right, targetBounds.top, targetBounds.right, targetBounds.bottom, SkRegion::kIntersect_Op)) { @@ -420,7 +421,7 @@ void CanvasContext::draw() { // ... or at the top ... if (targetBounds.top < contentBounds.top && !targetBounds.isEmpty()) { - mCanvas->save(SkCanvas::kClip_SaveFlag); + mCanvas->save(SaveFlags::Clip); if (mCanvas->clipRect(targetBounds.left, targetBounds.top, targetBounds.right, contentBounds.top, SkRegion::kIntersect_Op)) { @@ -433,7 +434,7 @@ void CanvasContext::draw() { // ... or at the bottom. if (targetBounds.bottom > contentBounds.bottom && !targetBounds.isEmpty()) { - mCanvas->save(SkCanvas::kClip_SaveFlag); + mCanvas->save(SaveFlags::Clip); if (mCanvas->clipRect(targetBounds.left, contentBounds.bottom, targetBounds.right, targetBounds.bottom, SkRegion::kIntersect_Op)) { mCanvas->drawRenderNode(node.get(), outBounds); @@ -442,7 +443,7 @@ void CanvasContext::draw() { } } else if (layer == 1) { // Content // It gets cropped against the bounds of the backdrop to stay inside. - mCanvas->save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag); + mCanvas->save(SaveFlags::MatrixClip); // We shift and clip the content to match its final location in the window. const float left = mContentDrawBounds.left; @@ -468,7 +469,7 @@ void CanvasContext::draw() { bool drew = mCanvas->finish(); #endif - GL_CHECKPOINT(); + GL_CHECKPOINT(LOW); // Even if we decided to cancel the frame, from the perspective of jank // metrics the frame was swapped at this point diff --git a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp index db6402ce136f..a5fd71266314 100644 --- a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp @@ -32,7 +32,7 @@ public: canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode); card = TestUtils::createNode(0, 0, 200, 400, [](RenderProperties& props, TestCanvas& canvas) { - canvas.save(SkCanvas::kMatrixClip_SaveFlag); + canvas.save(SaveFlags::MatrixClip); { canvas.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op); canvas.translate(100, 100); @@ -43,7 +43,7 @@ public: } canvas.restore(); - canvas.save(SkCanvas::kMatrixClip_SaveFlag); + canvas.save(SaveFlags::MatrixClip); { SkPath clipCircle; clipCircle.addCircle(100, 300, 100); diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp index c89985009fbd..6904bec304e3 100644 --- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp @@ -34,18 +34,18 @@ public: card = TestUtils::createNode(0, 0, 400, 800, [](RenderProperties& props, TestCanvas& canvas) { // nested clipped saveLayers - canvas.saveLayerAlpha(0, 0, 400, 400, 200, SkCanvas::kClipToLayer_SaveFlag); + canvas.saveLayerAlpha(0, 0, 400, 400, 200, SaveFlags::ClipToLayer); canvas.drawColor(Color::Green_700, SkXfermode::kSrcOver_Mode); canvas.clipRect(50, 50, 350, 350, SkRegion::kIntersect_Op); - canvas.saveLayerAlpha(100, 100, 300, 300, 128, SkCanvas::kClipToLayer_SaveFlag); + canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer); canvas.drawColor(Color::Blue_500, SkXfermode::kSrcOver_Mode); canvas.restore(); canvas.restore(); // single unclipped saveLayer - canvas.save(SkCanvas::kMatrixClip_SaveFlag); + canvas.save(SaveFlags::MatrixClip); canvas.translate(0, 400); - canvas.saveLayerAlpha(100, 100, 300, 300, 128, SkCanvas::SaveFlags(0)); // unclipped + canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::Flags(0)); // unclipped SkPaint paint; paint.setAntiAlias(true); paint.setColor(Color::Green_700); diff --git a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp index 0cba34479a3c..6d27c9d61a29 100644 --- a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp @@ -69,7 +69,7 @@ public: float cellSize = floorf(width / 7 - cellSpace); // each combination of strokeWidth + style gets a column - int outerCount = canvas.save(SkCanvas::kMatrixClip_SaveFlag); + int outerCount = canvas.save(SaveFlags::MatrixClip); SkPaint paint; paint.setAntiAlias(true); SkPaint::Style styles[] = { @@ -79,9 +79,9 @@ public: for (auto strokeWidth : { 0.0f, 0.5f, 8.0f }) { paint.setStrokeWidth(strokeWidth); // fill column with each op - int middleCount = canvas.save(SkCanvas::kMatrixClip_SaveFlag); + int middleCount = canvas.save(SaveFlags::MatrixClip); for (auto op : ops) { - int innerCount = canvas.save(SkCanvas::kMatrixClip_SaveFlag); + int innerCount = canvas.save(SaveFlags::MatrixClip); canvas.clipRect(0, 0, cellSize, cellSize, SkRegion::kIntersect_Op); canvas.drawColor(Color::White, SkXfermode::Mode::kSrcOver_Mode); op(canvas, cellSize, paint); diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp index 2e59eb450c9f..83af148d095e 100644 --- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp +++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp @@ -78,8 +78,8 @@ void BM_DisplayListCanvas_record_saverestore::Run(int iters) { StartBenchmarkTiming(); for (int i = 0; i < iters; ++i) { canvas.reset(100, 100); - canvas.save(SkCanvas::kMatrixClip_SaveFlag); - canvas.save(SkCanvas::kMatrixClip_SaveFlag); + canvas.save(SaveFlags::MatrixClip); + canvas.save(SaveFlags::MatrixClip); MicroBench::DoNotOptimize(&canvas); canvas.restore(); canvas.restore(); @@ -121,12 +121,12 @@ void BM_DisplayListCanvas_record_simpleBitmapView::Run(int iters) { for (int i = 0; i < iters; ++i) { canvas.reset(100, 100); { - canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + canvas.save(SaveFlags::MatrixClip); canvas.drawRect(0, 0, 100, 100, rectPaint); canvas.restore(); } { - canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + canvas.save(SaveFlags::MatrixClip); canvas.translate(10, 10); canvas.drawBitmap(iconBitmap, 0, 0, nullptr); canvas.restore(); @@ -151,8 +151,8 @@ void BM_CanvasState_saverestore::Run(int iters) { StartBenchmarkTiming(); for (int i = 0; i < iters; ++i) { - state.save(SkCanvas::kMatrixClip_SaveFlag); - state.save(SkCanvas::kMatrixClip_SaveFlag); + state.save(SaveFlags::MatrixClip); + state.save(SaveFlags::MatrixClip); MicroBench::DoNotOptimize(&state); state.restore(); state.restore(); diff --git a/libs/hwui/tests/microbench/FrameBuilderBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp index 67c95e2e921f..f9c2b671fdf3 100644 --- a/libs/hwui/tests/microbench/FrameBuilderBench.cpp +++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp @@ -47,7 +47,7 @@ static std::vector<sp<RenderNode>> createTestNodeList() { // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects. // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group. - canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + canvas.save(SaveFlags::MatrixClip); for (int i = 0; i < 30; i++) { canvas.translate(0, 10); canvas.drawRect(0, 0, 10, 10, paint); diff --git a/libs/hwui/tests/unit/BufferPoolTests.cpp b/libs/hwui/tests/unit/BufferPoolTests.cpp index 09bd302333d3..44e6d3a0cbea 100644 --- a/libs/hwui/tests/unit/BufferPoolTests.cpp +++ b/libs/hwui/tests/unit/BufferPoolTests.cpp @@ -36,6 +36,7 @@ TEST(BufferPool, acquireThenRelease) { ASSERT_EQ(bufferCount - i, pool->getAvailableBufferCount()); acquiredBuffers[i] = pool->acquire(); ASSERT_NE(nullptr, acquiredBuffers[i]); + ASSERT_TRUE(acquiredBuffers[i]->isUniqueRef()); } for (size_t i = 0; i < bufferCount; i++) { diff --git a/libs/hwui/tests/unit/CanvasStateTests.cpp b/libs/hwui/tests/unit/CanvasStateTests.cpp index 4df2687394fa..68d74ee1e91d 100644 --- a/libs/hwui/tests/unit/CanvasStateTests.cpp +++ b/libs/hwui/tests/unit/CanvasStateTests.cpp @@ -16,6 +16,7 @@ #include "CanvasState.h" +#include "Canvas.h" #include "Matrix.h" #include "Rect.h" #include "utils/LinearAllocator.h" @@ -23,7 +24,6 @@ #include <gtest/gtest.h> #include <SkPath.h> #include <SkRegion.h> -#include <SkCanvas.h> namespace android { namespace uirenderer { @@ -83,7 +83,7 @@ TEST(CanvasState, complexClipping) { state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3()); - state.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag); + state.save(SaveFlags::MatrixClip); { // rotated clip causes complex clip state.rotate(10); @@ -93,7 +93,7 @@ TEST(CanvasState, complexClipping) { } state.restore(); - state.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag); + state.save(SaveFlags::MatrixClip); { // subtracted clip causes complex clip EXPECT_TRUE(state.clipIsSimple()); @@ -102,7 +102,7 @@ TEST(CanvasState, complexClipping) { } state.restore(); - state.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag); + state.save(SaveFlags::MatrixClip); { // complex path causes complex clip SkPath path; @@ -119,7 +119,7 @@ TEST(CanvasState, saveAndRestore) { state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3()); - state.save(SkCanvas::kClip_SaveFlag); + state.save(SaveFlags::Clip); { state.clipRect(0, 0, 10, 10, SkRegion::kIntersect_Op); ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10)); @@ -129,7 +129,7 @@ TEST(CanvasState, saveAndRestore) { Matrix4 simpleTranslate; simpleTranslate.loadTranslate(10, 10, 0); - state.save(SkCanvas::kMatrix_SaveFlag); + state.save(SaveFlags::Matrix); { state.translate(10, 10, 0); EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate)); @@ -143,7 +143,7 @@ TEST(CanvasState, saveAndRestoreButNotTooMuch) { state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3()); - state.save(SkCanvas::kMatrix_SaveFlag); // NOTE: clip not saved + state.save(SaveFlags::Matrix); // NOTE: clip not saved { state.clipRect(0, 0, 10, 10, SkRegion::kIntersect_Op); ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10)); @@ -153,7 +153,7 @@ TEST(CanvasState, saveAndRestoreButNotTooMuch) { Matrix4 simpleTranslate; simpleTranslate.loadTranslate(10, 10, 0); - state.save(SkCanvas::kClip_SaveFlag); // NOTE: matrix not saved + state.save(SaveFlags::Clip); // NOTE: matrix not saved { state.translate(10, 10, 0); EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate)); diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp index b51bd2ff99cf..618df140deda 100644 --- a/libs/hwui/tests/unit/FrameBuilderTests.cpp +++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp @@ -167,7 +167,7 @@ TEST(FrameBuilder, simpleStroke) { TEST(FrameBuilder, simpleRejection) { auto node = TestUtils::createNode(0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { - canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + canvas.save(SaveFlags::MatrixClip); canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); // intersection should be empty canvas.drawRect(0, 0, 400, 400, SkPaint()); canvas.restore(); @@ -198,7 +198,7 @@ TEST(FrameBuilder, simpleBatching) { // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects. // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group. - canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + canvas.save(SaveFlags::MatrixClip); for (int i = 0; i < LOOPS; i++) { canvas.translate(0, 10); canvas.drawRect(0, 0, 10, 10, SkPaint()); @@ -215,7 +215,8 @@ TEST(FrameBuilder, simpleBatching) { << "Expect number of ops = 2 * loop count"; } -TEST(FrameBuilder, clippedMerging) { +// TODO: Disabled due to b/26793764 +TEST(FrameBuilder, DISABLED_clippedMerging) { class ClippedMergingTestRenderer : public TestRendererBase { public: void onMergedBitmapOps(const MergedBakedOpList& opList) override { @@ -336,7 +337,7 @@ RENDERTHREAD_TEST(FrameBuilder, textureLayer) { auto node = TestUtils::createNode(0, 0, 200, 200, [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) { - canvas.save(SkCanvas::kMatrixClip_SaveFlag); + canvas.save(SaveFlags::MatrixClip); canvas.clipRect(50, 50, 150, 150, SkRegion::kIntersect_Op); canvas.drawLayer(layerUpdater.get()); canvas.restore(); @@ -380,7 +381,7 @@ TEST(FrameBuilder, renderNode) { paint.setColor(SK_ColorDKGRAY); canvas.drawRect(0, 0, 200, 200, paint); - canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + canvas.save(SaveFlags::MatrixClip); canvas.translate(40, 40); canvas.drawRenderNode(child.get()); canvas.restore(); @@ -448,7 +449,7 @@ TEST(FrameBuilder, saveLayer_simple) { auto node = TestUtils::createNode(0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { - canvas.saveLayerAlpha(10, 10, 190, 190, 128, SkCanvas::kClipToLayer_SaveFlag); + canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer); canvas.drawRect(10, 10, 190, 190, SkPaint()); canvas.restore(); }); @@ -512,10 +513,10 @@ TEST(FrameBuilder, saveLayer_nested) { auto node = TestUtils::createNode(0, 0, 800, 800, [](RenderProperties& props, RecordingCanvas& canvas) { - canvas.saveLayerAlpha(0, 0, 800, 800, 128, SkCanvas::kClipToLayer_SaveFlag); + canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer); { canvas.drawRect(0, 0, 800, 800, SkPaint()); - canvas.saveLayerAlpha(0, 0, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag); + canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer); { canvas.drawRect(0, 0, 400, 400, SkPaint()); } @@ -534,9 +535,9 @@ TEST(FrameBuilder, saveLayer_nested) { TEST(FrameBuilder, saveLayer_contentRejection) { auto node = TestUtils::createNode(0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { - canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + canvas.save(SaveFlags::MatrixClip); canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); - canvas.saveLayerAlpha(200, 200, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag); + canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer); // draw within save layer may still be recorded, but shouldn't be drawn canvas.drawRect(200, 200, 400, 400, SkPaint()); @@ -583,7 +584,7 @@ TEST(FrameBuilder, saveLayerUnclipped_simple) { auto node = TestUtils::createNode(0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { - canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SkCanvas::SaveFlags)(0)); + canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0)); canvas.drawRect(0, 0, 200, 200, SkPaint()); canvas.restore(); }); @@ -632,12 +633,12 @@ TEST(FrameBuilder, saveLayerUnclipped_mergedClears) { auto node = TestUtils::createNode(0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { - int restoreTo = canvas.save(SkCanvas::kMatrixClip_SaveFlag); + int restoreTo = canvas.save(SaveFlags::MatrixClip); canvas.scale(2, 2); - canvas.saveLayerAlpha(0, 0, 5, 5, 128, SkCanvas::kMatrixClip_SaveFlag); - canvas.saveLayerAlpha(95, 0, 100, 5, 128, SkCanvas::kMatrixClip_SaveFlag); - canvas.saveLayerAlpha(0, 95, 5, 100, 128, SkCanvas::kMatrixClip_SaveFlag); - canvas.saveLayerAlpha(95, 95, 100, 100, 128, SkCanvas::kMatrixClip_SaveFlag); + canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip); + canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip); + canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip); + canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip); canvas.drawRect(0, 0, 100, 100, SkPaint()); canvas.restoreToCount(restoreTo); }); @@ -695,9 +696,9 @@ TEST(FrameBuilder, saveLayerUnclipped_complex) { auto node = TestUtils::createNode(0, 0, 600, 600, // 500x500 triggers clipping [](RenderProperties& props, RecordingCanvas& canvas) { - canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SkCanvas::SaveFlags)0); // unclipped - canvas.saveLayerAlpha(100, 100, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag); // clipped - canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SkCanvas::SaveFlags)0); // unclipped + canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped + canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped + canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped canvas.drawRect(200, 200, 300, 300, SkPaint()); canvas.restore(); canvas.restore(); @@ -849,7 +850,7 @@ RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) { paint.setColor(SK_ColorDKGRAY); canvas.drawRect(0, 0, 200, 200, paint); - canvas.saveLayerAlpha(50, 50, 150, 150, 128, SkCanvas::kClipToLayer_SaveFlag); + canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer); canvas.drawRenderNode(childPtr); canvas.restore(); }); @@ -987,7 +988,7 @@ TEST(FrameBuilder, projectionReorder) { }); auto parent = TestUtils::createNode(0, 0, 100, 100, [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { - canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + canvas.save(SaveFlags::MatrixClip); canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) canvas.drawRenderNode(receiverBackground.get()); canvas.drawRenderNode(child.get()); @@ -1071,7 +1072,7 @@ TEST(FrameBuilder, shadowSaveLayer) { [](RenderProperties& props, RecordingCanvas& canvas) { // save/restore outside of reorderBarrier, so they don't get moved out of place canvas.translate(20, 10); - int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SkCanvas::kClipToLayer_SaveFlag); + int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer); canvas.insertReorderBarrier(true); canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); canvas.insertReorderBarrier(false); @@ -1111,7 +1112,7 @@ RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) { [](RenderProperties& props, RecordingCanvas& canvas) { props.mutateLayerProperties().setType(LayerType::RenderLayer); canvas.insertReorderBarrier(true); - canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + canvas.save(SaveFlags::MatrixClip); canvas.translate(20, 10); canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); canvas.restore(); diff --git a/libs/hwui/tests/unit/LeakCheckTests.cpp b/libs/hwui/tests/unit/LeakCheckTests.cpp index 41e44fc88a6b..4a635fb1b8c8 100644 --- a/libs/hwui/tests/unit/LeakCheckTests.cpp +++ b/libs/hwui/tests/unit/LeakCheckTests.cpp @@ -32,7 +32,7 @@ const Vector3 sLightCenter = {100, 100, 100}; RENDERTHREAD_TEST(LeakCheck, saveLayerUnclipped_simple) { auto node = TestUtils::createNode(0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { - canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SkCanvas::SaveFlags)(0)); + canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0)); canvas.drawRect(0, 0, 200, 200, SkPaint()); canvas.restore(); }); diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp index ff098c8bb3d5..01bfc5adbbbd 100644 --- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp +++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp @@ -35,7 +35,7 @@ static void playbackOps(const DisplayList& displayList, TEST(RecordingCanvas, emptyPlayback) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) { - canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + canvas.save(SaveFlags::MatrixClip); canvas.restore(); }); playbackOps(*dl, [](const RecordedOp& op) { ADD_FAILURE(); }); @@ -43,7 +43,7 @@ TEST(RecordingCanvas, emptyPlayback) { TEST(RecordingCanvas, clipRect) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) { - canvas.save(SkCanvas::kMatrixClip_SaveFlag); + canvas.save(SaveFlags::MatrixClip); canvas.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op); canvas.drawRect(0, 0, 50, 50, SkPaint()); canvas.drawRect(50, 50, 100, 100, SkPaint()); @@ -176,16 +176,16 @@ TEST(RecordingCanvas, backgroundAndImage) { SkPaint paint; paint.setColor(SK_ColorBLUE); - canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + canvas.save(SaveFlags::MatrixClip); { // a background! - canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + canvas.save(SaveFlags::MatrixClip); canvas.drawRect(0, 0, 100, 200, paint); canvas.restore(); } { // an image! - canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + canvas.save(SaveFlags::MatrixClip); canvas.translate(25, 25); canvas.scale(2, 2); canvas.drawBitmap(bitmap, 0, 0, nullptr); @@ -224,7 +224,7 @@ TEST(RecordingCanvas, backgroundAndImage) { TEST(RecordingCanvas, saveLayer_simple) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { - canvas.saveLayerAlpha(10, 20, 190, 180, 128, SkCanvas::kClipToLayer_SaveFlag); + canvas.saveLayerAlpha(10, 20, 190, 180, 128, SaveFlags::ClipToLayer); canvas.drawRect(10, 20, 190, 180, SkPaint()); canvas.restore(); }); @@ -258,7 +258,7 @@ TEST(RecordingCanvas, saveLayer_simple) { TEST(RecordingCanvas, saveLayer_missingRestore) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { - canvas.saveLayerAlpha(0, 0, 200, 200, 128, SkCanvas::kClipToLayer_SaveFlag); + canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer); canvas.drawRect(0, 0, 200, 200, SkPaint()); // Note: restore omitted, shouldn't result in unmatched save }); @@ -273,7 +273,7 @@ TEST(RecordingCanvas, saveLayer_missingRestore) { TEST(RecordingCanvas, saveLayer_simpleUnclipped) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { - canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SkCanvas::SaveFlags)0); // unclipped + canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped canvas.drawRect(10, 20, 190, 180, SkPaint()); canvas.restore(); }); @@ -305,9 +305,9 @@ TEST(RecordingCanvas, saveLayer_simpleUnclipped) { TEST(RecordingCanvas, saveLayer_addClipFlag) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { - canvas.save(SkCanvas::kMatrixClip_SaveFlag); + canvas.save(SaveFlags::MatrixClip); canvas.clipRect(10, 20, 190, 180, SkRegion::kIntersect_Op); - canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SkCanvas::SaveFlags)0); // unclipped + canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped canvas.drawRect(10, 20, 190, 180, SkPaint()); canvas.restore(); canvas.restore(); @@ -327,7 +327,7 @@ TEST(RecordingCanvas, saveLayer_viewportCrop) { // shouldn't matter, since saveLayer will clip to its bounds canvas.clipRect(-1000, -1000, 1000, 1000, SkRegion::kReplace_Op); - canvas.saveLayerAlpha(100, 100, 300, 300, 128, SkCanvas::kClipToLayer_SaveFlag); + canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer); canvas.drawRect(0, 0, 400, 400, SkPaint()); canvas.restore(); }); @@ -348,12 +348,12 @@ TEST(RecordingCanvas, saveLayer_viewportCrop) { TEST(RecordingCanvas, saveLayer_rotateUnclipped) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { - canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + canvas.save(SaveFlags::MatrixClip); canvas.translate(100, 100); canvas.rotate(45); canvas.translate(-50, -50); - canvas.saveLayerAlpha(0, 0, 100, 100, 128, SkCanvas::kClipToLayer_SaveFlag); + canvas.saveLayerAlpha(0, 0, 100, 100, 128, SaveFlags::ClipToLayer); canvas.drawRect(0, 0, 100, 100, SkPaint()); canvas.restore(); @@ -374,13 +374,13 @@ TEST(RecordingCanvas, saveLayer_rotateUnclipped) { TEST(RecordingCanvas, saveLayer_rotateClipped) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { - canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + canvas.save(SaveFlags::MatrixClip); canvas.translate(100, 100); canvas.rotate(45); canvas.translate(-200, -200); // area of saveLayer will be clipped to parent viewport, so we ask for 400x400... - canvas.saveLayerAlpha(0, 0, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag); + canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer); canvas.drawRect(0, 0, 400, 400, SkPaint()); canvas.restore(); diff --git a/libs/hwui/utils/GLUtils.cpp b/libs/hwui/utils/GLUtils.cpp index 55104de5a9d8..332097593c86 100644 --- a/libs/hwui/utils/GLUtils.cpp +++ b/libs/hwui/utils/GLUtils.cpp @@ -21,10 +21,19 @@ #include "GLUtils.h" +#if DEBUG_OPENGL >= DEBUG_LEVEL_HIGH && !defined(HWUI_GLES_WRAP_ENABLED) +#error Setting DEBUG_OPENGL to HIGH requires setting HWUI_ENABLE_OPENGL_VALIDATION to true in the Android.mk! +#endif + namespace android { namespace uirenderer { bool GLUtils::dumpGLErrors() { +#if DEBUG_OPENGL >= DEBUG_LEVEL_HIGH + // If DEBUG_LEVEL_HIGH is set then every GLES call is already wrapped + // and asserts that there was no error. So this can just return success. + return false; +#else bool errorObserved = false; GLenum status = GL_NO_ERROR; while ((status = glGetError()) != GL_NO_ERROR) { @@ -47,6 +56,7 @@ bool GLUtils::dumpGLErrors() { } } return errorObserved; +#endif } }; // namespace uirenderer diff --git a/libs/hwui/utils/GLUtils.h b/libs/hwui/utils/GLUtils.h index 85a10f94dc4c..b49c1eb1dc05 100644 --- a/libs/hwui/utils/GLUtils.h +++ b/libs/hwui/utils/GLUtils.h @@ -23,17 +23,22 @@ namespace android { namespace uirenderer { + #if DEBUG_OPENGL -#define GL_CHECKPOINT() LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(),\ - "GL errors! %s:%d", __FILE__, __LINE__) +#define GL_CHECKPOINT(LEVEL) \ + do { if (DEBUG_OPENGL >= DEBUG_LEVEL_##LEVEL) {\ + LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(),\ + "GL errors! %s:%d", __FILE__, __LINE__);\ + } } while (0) #else -#define GL_CHECKPOINT() +#define GL_CHECKPOINT(LEVEL) #endif class GLUtils { public: /** * Print out any GL errors with ALOGE, returns true if any errors were found. + * You probably want to use GL_CHECKPOINT(LEVEL) instead of calling this directly */ static bool dumpGLErrors(); diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 504c6d0d2003..951738723a15 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -389,6 +389,7 @@ public class MediaRecorder public static final int H264 = 2; public static final int MPEG_4_SP = 3; public static final int VP8 = 4; + public static final int HEVC = 5; } /** diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java index 396023079709..671a86f0d1ae 100644 --- a/media/java/android/media/tv/TvInputInfo.java +++ b/media/java/android/media/tv/TvInputInfo.java @@ -120,6 +120,8 @@ public final class TvInputInfo implements Parcelable { private final String mId; private final String mParentId; private final int mType; + private final int mTunerCount; + private final boolean mCanRecord; private final boolean mIsHardwareInput; // Attributes from XML meta data. @@ -127,9 +129,11 @@ public final class TvInputInfo implements Parcelable { private String mSettingsActivity; private HdmiDeviceInfo mHdmiDeviceInfo; - private int mLabelRes; + private int mLabelResId; + // TODO: Remove when createTvInputInfo() is removed. private String mLabel; private Icon mIcon; + // TODO: Remove when createTvInputInfo() is removed. private Uri mIconUri; private boolean mIsConnectedToHdmiSwitch; @@ -149,20 +153,6 @@ public final class TvInputInfo implements Parcelable { } /** - * Create a new instance of the TvInputInfo class, - * instantiating it from the given Context and ResolveInfo. - * - * @param service The ResolveInfo returned from the package manager about this TV input service. - * @hide - */ - public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service) - throws XmlPullParserException, IOException { - return createTvInputInfo(context, service, generateInputIdForComponentName( - new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name)), - null, TYPE_TUNER, false, 0, null, null, null, false); - } - - /** * Create a new instance of the TvInputInfo class, instantiating it from the given Context, * ResolveInfo, and HdmiDeviceInfo. * @@ -175,18 +165,20 @@ public final class TvInputInfo implements Parcelable { * {@link android.content.ContentResolver#openInputStream}. If it is {@code null}, * the application icon of {@code service} will be loaded. * @hide + * @deprecated Use {@link Builder} instead. */ + @Deprecated @SystemApi public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service, HdmiDeviceInfo hdmiDeviceInfo, String parentId, String label, Uri iconUri) throws XmlPullParserException, IOException { - boolean isConnectedToHdmiSwitch = (hdmiDeviceInfo.getPhysicalAddress() & 0x0FFF) != 0; - TvInputInfo input = createTvInputInfo(context, service, generateInputIdForHdmiDevice( - new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name), - hdmiDeviceInfo), parentId, TYPE_HDMI, true, 0, label, null, iconUri, - isConnectedToHdmiSwitch); - input.mHdmiDeviceInfo = hdmiDeviceInfo; - return input; + TvInputInfo info = new TvInputInfo.Builder(context, service) + .setHdmiDeviceInfo(hdmiDeviceInfo) + .setParentId(parentId) + .build(); + info.mLabel = label; + info.mIconUri = iconUri; + return info; } /** @@ -201,18 +193,19 @@ public final class TvInputInfo implements Parcelable { * @param icon The {@link android.graphics.drawable.Icon} to load the icon image. If it is * {@code null}, the application icon of {@code service} will be loaded. * @hide + * @deprecated Use {@link Builder} instead. */ + @Deprecated @SystemApi public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service, HdmiDeviceInfo hdmiDeviceInfo, String parentId, int labelRes, Icon icon) throws XmlPullParserException, IOException { - boolean isConnectedToHdmiSwitch = (hdmiDeviceInfo.getPhysicalAddress() & 0x0FFF) != 0; - TvInputInfo input = createTvInputInfo(context, service, generateInputIdForHdmiDevice( - new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name), - hdmiDeviceInfo), parentId, TYPE_HDMI, true, labelRes, null, icon, null, - isConnectedToHdmiSwitch); - input.mHdmiDeviceInfo = hdmiDeviceInfo; - return input; + return new TvInputInfo.Builder(context, service) + .setHdmiDeviceInfo(hdmiDeviceInfo) + .setParentId(parentId) + .setLabel(labelRes) + .setIcon(icon) + .build(); } /** @@ -227,15 +220,19 @@ public final class TvInputInfo implements Parcelable { * {@link android.content.ContentResolver#openInputStream}. If it is {@code null}, * the application icon of {@code service} will be loaded. * @hide + * @deprecated Use {@link Builder} instead. */ + @Deprecated @SystemApi public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service, TvInputHardwareInfo hardwareInfo, String label, Uri iconUri) throws XmlPullParserException, IOException { - int inputType = sHardwareTypeToTvInputType.get(hardwareInfo.getType(), TYPE_TUNER); - return createTvInputInfo(context, service, generateInputIdForHardware( - new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name), - hardwareInfo), null, inputType, true, 0, label, null, iconUri, false); + TvInputInfo info = new TvInputInfo.Builder(context, service) + .setTvInputHardwareInfo(hardwareInfo) + .build(); + info.mLabel = label; + info.mIconUri = iconUri; + return info; } /** @@ -249,21 +246,55 @@ public final class TvInputInfo implements Parcelable { * @param icon The {@link android.graphics.drawable.Icon} to load the icon image. If it is * {@code null}, the application icon of {@code service} will be loaded. * @hide + * @deprecated Use {@link Builder} instead. */ + @Deprecated @SystemApi public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service, TvInputHardwareInfo hardwareInfo, int labelRes, Icon icon) throws XmlPullParserException, IOException { - int inputType = sHardwareTypeToTvInputType.get(hardwareInfo.getType(), TYPE_TUNER); - return createTvInputInfo(context, service, generateInputIdForHardware( - new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name), - hardwareInfo), null, inputType, true, labelRes, null, icon, null, false); + return new TvInputInfo.Builder(context, service) + .setTvInputHardwareInfo(hardwareInfo) + .setLabel(labelRes) + .setIcon(icon) + .build(); + } + + static TvInputInfo createTvInputInfo(Context context, ResolveInfo resolveInfo, Icon icon, + int labelResId, int tunerCount, boolean canRecord, HdmiDeviceInfo hdmiDeviceInfo, + String parentId, TvInputHardwareInfo tvInputHardwareInfo) + throws IOException, XmlPullParserException { + ComponentName componentName = new ComponentName(resolveInfo.serviceInfo.packageName, + resolveInfo.serviceInfo.name); + String id; + int type; + boolean isHardwareInput = false; + boolean isConnectedToHdmiSwitch = false; + + if (hdmiDeviceInfo != null) { + id = generateInputIdForHdmiDevice(componentName, hdmiDeviceInfo); + type = TYPE_HDMI; + isHardwareInput = true; + isConnectedToHdmiSwitch = (hdmiDeviceInfo.getPhysicalAddress() & 0x0FFF) != 0; + tunerCount = 0; + } else if (tvInputHardwareInfo != null) { + id = generateInputIdForHardware(componentName, tvInputHardwareInfo); + type = sHardwareTypeToTvInputType.get(tvInputHardwareInfo.getType(), TYPE_TUNER); + isHardwareInput = true; + tunerCount = 0; + } else { + id = generateInputIdForComponentName(componentName); + type = TYPE_TUNER; + } + + TvInputInfo info = new TvInputInfo(resolveInfo, id, parentId, type, isHardwareInput, + isConnectedToHdmiSwitch, tunerCount, canRecord); + return parseServiceMetadata(context, resolveInfo, type, info); } - private static TvInputInfo createTvInputInfo(Context context, ResolveInfo service, String id, - String parentId, int inputType, boolean isHardwareInput, int labelRes, String label, - Icon icon, Uri iconUri, boolean isConnectedToHdmiSwitch) - throws XmlPullParserException, IOException { + private static TvInputInfo parseServiceMetadata( + Context context, ResolveInfo service, int inputType, TvInputInfo input) + throws XmlPullParserException, IOException { ServiceInfo si = service.serviceInfo; PackageManager pm = context.getPackageManager(); XmlResourceParser parser = null; @@ -278,7 +309,7 @@ public final class TvInputInfo implements Parcelable { AttributeSet attrs = Xml.asAttributeSet(parser); int type; - while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) { } @@ -288,7 +319,6 @@ public final class TvInputInfo implements Parcelable { "Meta-data does not start with tv-input-service tag in " + si.name); } - TvInputInfo input = new TvInputInfo(service, id, parentId, inputType, isHardwareInput); TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.TvInputService); input.mSetupActivity = sa.getString( @@ -307,12 +337,6 @@ public final class TvInputInfo implements Parcelable { } sa.recycle(); - input.mLabelRes = labelRes; - input.mLabel = label; - input.mIcon = icon; - input.mIconUri = iconUri; - input.mIsConnectedToHdmiSwitch = isConnectedToHdmiSwitch; - return input; } catch (NameNotFoundException e) { throw new XmlPullParserException("Unable to create context for: " + si.packageName); } finally { @@ -320,6 +344,7 @@ public final class TvInputInfo implements Parcelable { parser.close(); } } + return input; } /** @@ -330,15 +355,23 @@ public final class TvInputInfo implements Parcelable { * @param parentId ID of this TV input's parent input. {@code null} if none exists. * @param type The type of this TV input service. * @param isHardwareInput {@code true} if this TV input represents a hardware device. - * {@code false} otherwise. + * {@code false} otherwise. + * @param isConnectedToHdmiSwitch Whether a CEC device for this TV input is connected to an HDMI + * switch, i.e., the device isn't directly connected to a HDMI port. + * @param tunerCount The number of tuners this TV input has. + * @param canRecord Whether this TV input can record TV programs. */ private TvInputInfo(ResolveInfo service, String id, String parentId, int type, - boolean isHardwareInput) { + boolean isHardwareInput, boolean isConnectedToHdmiSwitch, int tunerCount, + boolean canRecord) { mService = service; mId = id; mParentId = parentId; mType = type; mIsHardwareInput = isHardwareInput; + mIsConnectedToHdmiSwitch = isConnectedToHdmiSwitch; + mTunerCount = tunerCount; + mCanRecord = canRecord; } /** @@ -429,14 +462,14 @@ public final class TvInputInfo implements Parcelable { * */ public int getTunerCount() { - return mType == TYPE_TUNER ? 1 : 0; + return mTunerCount; } /** * Returns {@code true} if this TV input can record TV programs, {@code false} otherwise. */ public boolean canRecord() { - return false; + return mCanRecord; } /** @@ -502,9 +535,9 @@ public final class TvInputInfo implements Parcelable { * a label, its name is returned. */ public CharSequence loadLabel(@NonNull Context context) { - if (mLabelRes != 0) { - return context.getPackageManager().getText(mService.serviceInfo.packageName, mLabelRes, - null); + if (mLabelResId != 0) { + return context.getPackageManager().getText(mService.serviceInfo.packageName, + mLabelResId, null); } else if (!TextUtils.isEmpty(mLabel)) { return mLabel; } @@ -593,11 +626,13 @@ public final class TvInputInfo implements Parcelable { dest.writeString(mSetupActivity); dest.writeString(mSettingsActivity); dest.writeInt(mType); + dest.writeInt(mTunerCount); + dest.writeByte(mCanRecord ? (byte) 1 : 0); dest.writeByte(mIsHardwareInput ? (byte) 1 : 0); dest.writeParcelable(mHdmiDeviceInfo, flags); dest.writeParcelable(mIcon, flags); dest.writeParcelable(mIconUri, flags); - dest.writeInt(mLabelRes); + dest.writeInt(mLabelResId); dest.writeString(mLabel); dest.writeByte(mIsConnectedToHdmiSwitch ? (byte) 1 : 0); } @@ -670,16 +705,174 @@ public final class TvInputInfo implements Parcelable { mSetupActivity = in.readString(); mSettingsActivity = in.readString(); mType = in.readInt(); + mTunerCount = in.readInt(); + mCanRecord = in.readByte() == 1; mIsHardwareInput = in.readByte() == 1; mHdmiDeviceInfo = in.readParcelable(null); mIcon = in.readParcelable(null); mIconUri = in.readParcelable(null); - mLabelRes = in.readInt(); + mLabelResId = in.readInt(); mLabel = in.readString(); mIsConnectedToHdmiSwitch = in.readByte() == 1; } /** + * A convenience builder for creating {@link TvInputInfo} objects. + */ + public static final class Builder { + private final Context mContext; + private final ResolveInfo mResolveInfo; + private Icon mIcon; + private int mLabelResId; + private int mTunerCount = 1; + private boolean mCanRecord; + private HdmiDeviceInfo mHdmiDeviceInfo; + private String mParentId; + private TvInputHardwareInfo mTvInputHardwareInfo; + + /** + * Constructs a new builder for {@link TvInputInfo}. + * + * @param context A Context of the application package implementing this class. + * @param cls The component class that is to be used for the {@link TvInputService}. + */ + public Builder(Context context, Class<?> cls) { + mContext = context; + Intent intent = new Intent(TvInputService.SERVICE_INTERFACE).setClass(context, cls); + mResolveInfo = context.getPackageManager().resolveService(intent, + PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); + } + + /** + * Constructs a new builder for {@link TvInputInfo}. + * + * @param resolveInfo The ResolveInfo returned from the package manager about this TV input + * service. + * @hide + */ + public Builder(Context context, ResolveInfo resolveInfo) { + if (context == null) { + throw new IllegalArgumentException("context cannot be null"); + } + if (resolveInfo == null) { + throw new IllegalArgumentException("resolveInfo cannot be null"); + } + mContext = context; + mResolveInfo = resolveInfo; + } + + /** + * Sets the icon. + * + * @param icon The icon that represents this TV input. + * @return This Builder object to allow for chaining of calls to builder methods. + * @hide + */ + @SystemApi + public Builder setIcon(Icon icon) { + this.mIcon = icon; + return this; + } + + /** + * Sets the label. + * + * @param resId The resource ID of the text to use. + * @return This Builder object to allow for chaining of calls to builder methods. + * @hide + */ + @SystemApi + public Builder setLabel(int resId) { + this.mLabelResId = resId; + return this; + } + + /** + * Sets the HdmiDeviceInfo. + * + * @param hdmiDeviceInfo The HdmiDeviceInfo for a HDMI CEC logical device. + * @return This Builder object to allow for chaining of calls to builder methods. + * @hide + */ + @SystemApi + public Builder setHdmiDeviceInfo(HdmiDeviceInfo hdmiDeviceInfo) { + if (mTvInputHardwareInfo != null) { + Log.w(TAG, "TvInputHardwareInfo will not be used to build this TvInputInfo"); + mTvInputHardwareInfo = null; + } + this.mHdmiDeviceInfo = hdmiDeviceInfo; + return this; + } + + /** + * Sets the parent ID. + * + * @param parentId The parent ID. + * @return This Builder object to allow for chaining of calls to builder methods. + * @hide + */ + @SystemApi + public Builder setParentId(String parentId) { + this.mParentId = parentId; + return this; + } + + /** + * Sets the TvInputHardwareInfo. + * + * @param tvInputHardwareInfo + * @return This Builder object to allow for chaining of calls to builder methods. + * @hide + */ + @SystemApi + public Builder setTvInputHardwareInfo(TvInputHardwareInfo tvInputHardwareInfo) { + if (mHdmiDeviceInfo != null) { + Log.w(TAG, "mHdmiDeviceInfo will not be used to build this TvInputInfo"); + mHdmiDeviceInfo = null; + } + this.mTvInputHardwareInfo = tvInputHardwareInfo; + return this; + } + + /** + * Sets the tuner count. Valid only for {@link #TYPE_TUNER}. + * + * @param tunerCount The number of tuners this TV input has. + * @return This Builder object to allow for chaining of calls to builder methods. + */ + public Builder setTunerCount(int tunerCount) { + this.mTunerCount = tunerCount; + return this; + } + + /** + * Sets whether this TV input can record TV programs or not. + * + * @param canRecord Whether this TV input can record TV programs. + * @return This Builder object to allow for chaining of calls to builder methods. + */ + public Builder setCanRecord(boolean canRecord) { + this.mCanRecord = canRecord; + return this; + } + + /** + * Creates a {@link TvInputInfo} instance with the specified fields. Most of the information + * is obtained by parsing the AndroidManifest and {@link TvInputService#SERVICE_META_DATA} + * for the {@link TvInputService} this TV input implements. + * + * @return TvInputInfo containing information about this TV input. + * @throws IOException If there was an I/O error. + * @throws XmlPullParserException If there was an XML parsing error. + * + */ + public TvInputInfo build() throws IOException, XmlPullParserException { + return createTvInputInfo(mContext, mResolveInfo, mIcon, mLabelResId, mTunerCount, + mCanRecord, mHdmiDeviceInfo, mParentId, mTvInputHardwareInfo); + } + } + + /** * Utility class for putting and getting settings for TV input. * * @hide diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index 6d9b1ad339a4..f74ae660669a 100644 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -195,7 +195,9 @@ public abstract class TvInputService extends Service { * @param inputId The ID of the TV input associated with the recording session. */ @Nullable - public abstract RecordingSession onCreateRecordingSession(String inputId); + public RecordingSession onCreateRecordingSession(String inputId) { + return null; + } /** * Returns a new {@link TvInputInfo} object if this service is responsible for diff --git a/native/android/choreographer.cpp b/native/android/choreographer.cpp index cc2fd7758787..e35c85b883e8 100644 --- a/native/android/choreographer.cpp +++ b/native/android/choreographer.cpp @@ -82,7 +82,7 @@ private: }; -thread_local Choreographer* gChoreographer; +static thread_local Choreographer* gChoreographer; Choreographer* Choreographer::getForThread() { if (gChoreographer == nullptr) { sp<Looper> looper = Looper::getForThread(); diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java index 9c01f4f4d7a2..f37ec58fbe26 100644 --- a/opengl/java/android/opengl/GLSurfaceView.java +++ b/opengl/java/android/opengl/GLSurfaceView.java @@ -1291,6 +1291,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback boolean createGlInterface = false; boolean lostEglContext = false; boolean sizeChanged = false; + boolean wantRenderNotification = false; boolean doRenderNotification = false; boolean askedToReleaseEglContext = false; int w = 0; @@ -1448,6 +1449,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } mRequestRender = false; sGLThreadManager.notifyAll(); + if (mWantRenderNotification) { + wantRenderNotification = true; + } break; } } @@ -1574,8 +1578,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback break; } - if (mWantRenderNotification) { + if (wantRenderNotification) { doRenderNotification = true; + wantRenderNotification = false; } } @@ -1625,11 +1630,21 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback public void requestRenderAndWait() { synchronized(sGLThreadManager) { + // If we are already on the GL thread, this means a client callback + // has caused reentrancy, for example via updating the SurfaceView parameters. + // We will return to the client rendering code, so here we don't need to + // do anything. + if (Thread.currentThread() == this) { + return; + } + mWantRenderNotification = true; mRequestRender = true; mRenderComplete = false; + sGLThreadManager.notifyAll(); - while (!mExited && !mPaused && mRenderComplete == false) { + + while (!mExited && !mPaused && !mRenderComplete && ableToDraw()) { try { sGLThreadManager.wait(); } catch (InterruptedException ex) { @@ -1726,6 +1741,16 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback mSizeChanged = true; mRequestRender = true; mRenderComplete = false; + + // If we are already on the GL thread, this means a client callback + // has caused reentrancy, for example via updating the SurfaceView parameters. + // We need to process the size change eventually though and update our EGLSurface. + // So we set the parameters and return so they can be processed on our + // next iteration. + if (Thread.currentThread() == this) { + return; + } + sGLThreadManager.notifyAll(); // Wait for thread to react to resize and render a frame diff --git a/packages/DocumentsUI/res/layout/item_dir_grid.xml b/packages/DocumentsUI/res/layout/item_dir_grid.xml index a08e02987292..b0331bec6e49 100644 --- a/packages/DocumentsUI/res/layout/item_dir_grid.xml +++ b/packages/DocumentsUI/res/layout/item_dir_grid.xml @@ -17,7 +17,7 @@ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_height="wrap_content" android:layout_margin="@dimen/grid_item_margin" android:background="@color/item_doc_background" android:elevation="5dp" diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml index 8ac228e77282..5a9bc162f3d6 100644 --- a/packages/DocumentsUI/res/values/strings.xml +++ b/packages/DocumentsUI/res/values/strings.xml @@ -179,8 +179,8 @@ </plurals> <!-- Second line for notifications saying that more information will be shown after touching [CHAR LIMIT=48] --> <string name="notification_touch_for_details">Tap to view details</string> - <!-- Label of a dialog button for retrying a failed operation [CHAR LIMIT=24] --> - <string name="retry">Retry</string> + <!-- Label of the close dialog button.[CHAR LIMIT=24] --> + <string name="close">Close</string> <!-- Contents of the copying failure alert dialog. [CHAR LIMIT=48] --> <string name="copy_failure_alert_content">These files weren\'t copied: <xliff:g id="list">%1$s</xliff:g></string> <!-- Contents of the moving failure alert dialog. [CHAR LIMIT=48] --> diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java index f43b81c207dc..ad8b0d12d438 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java @@ -107,6 +107,8 @@ public abstract class BaseActivity extends Activity implements SearchManagerList ? icicle.<State>getParcelable(EXTRA_STATE) : buildState(); + Metrics.logActivityLaunch(this, mState, getIntent()); + setContentView(mLayoutId); mRoots = DocumentsApplication.getRootsCache(this); diff --git a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java index 7f6f1c6bef14..b8ef5ac4a542 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java @@ -33,17 +33,14 @@ import com.android.documentsui.services.FileOperationService; import com.android.documentsui.services.FileOperations; import java.util.ArrayList; +import java.util.List; /** * Alert dialog for failed operations. */ -public class FailureDialogFragment extends DialogFragment - implements DialogInterface.OnClickListener { +public class FailureDialogFragment extends DialogFragment { private static final String TAG = "FailureDialogFragment"; - private int mOperationType; - private ArrayList<DocumentInfo> mFailedSrcList; - public static void show(FragmentManager fm, int failure, ArrayList<DocumentInfo> failedSrcList, DocumentStack dstStack, int operationType) { // TODO: Add support for other failures than copy. @@ -65,36 +62,25 @@ public class FailureDialogFragment extends DialogFragment } @Override - public void onClick(DialogInterface dialog, int whichButton) { - if (whichButton == DialogInterface.BUTTON_POSITIVE) { - FileOperations.start( - getActivity(), - mFailedSrcList, - (DocumentStack) getActivity().getIntent().getParcelableExtra( - Shared.EXTRA_STACK), - mOperationType); - } - } - - @Override public Dialog onCreateDialog(Bundle inState) { super.onCreate(inState); - mOperationType = getArguments().getInt(FileOperationService.EXTRA_OPERATION); - mFailedSrcList = getArguments().getParcelableArrayList(FileOperationService.EXTRA_SRC_LIST); + final int operationType = getArguments().getInt(FileOperationService.EXTRA_OPERATION); + final List<DocumentInfo> failedSrcList = getArguments().getParcelableArrayList( + FileOperationService.EXTRA_SRC_LIST); final StringBuilder list = new StringBuilder("<p>"); - for (DocumentInfo documentInfo : mFailedSrcList) { + for (DocumentInfo documentInfo : failedSrcList) { list.append(String.format("• %s<br>", documentInfo.displayName)); } list.append("</p>"); // TODO: Add support for other file operations. checkArgument( - mOperationType == FileOperationService.OPERATION_COPY - || mOperationType == FileOperationService.OPERATION_MOVE); + operationType == FileOperationService.OPERATION_COPY + || operationType == FileOperationService.OPERATION_MOVE); - int messageId = mOperationType == FileOperationService.OPERATION_COPY + int messageId = operationType == FileOperationService.OPERATION_COPY ? R.string.copy_failure_alert_content : R.string.move_failure_alert_content; @@ -105,8 +91,12 @@ public class FailureDialogFragment extends DialogFragment return new AlertDialog.Builder(getActivity()) .setMessage(Html.fromHtml(message)) - .setPositiveButton(R.string.retry, this) - .setNegativeButton(android.R.string.cancel, this) + .setPositiveButton(R.string.close, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + } + }) .create(); } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java index 58537ee495a8..5abe7f6f5234 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java @@ -282,6 +282,7 @@ public class FilesActivity extends BaseActivity { } private void createNewWindow() { + Metrics.logMultiWindow(this); Intent intent = LauncherActivity.createLaunchIntent(this); intent.putExtra(Shared.EXTRA_STACK, (Parcelable) mState.stack); startActivity(intent); diff --git a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java new file mode 100644 index 000000000000..eb90b753a4ae --- /dev/null +++ b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui; + +import static com.android.documentsui.Shared.DEBUG; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.provider.DocumentsContract; +import android.util.Log; + +import com.android.documentsui.model.RootInfo; +import com.android.internal.logging.MetricsLogger; + +/** @hide */ +public final class Metrics { + private static final String TAG = "Metrics"; + + // These are the native provider authorities that the metrics code is capable of recognizing and + // explicitly counting. + private static final String AUTHORITY_MEDIA = "com.android.providers.media.documents"; + private static final String AUTHORITY_STORAGE = "com.android.externalstorage.documents"; + private static final String AUTHORITY_DOWNLOADS = "com.android.providers.downloads.documents"; + + // These strings have to be whitelisted in tron. Do not change them. + private static final String COUNT_LAUNCH_ACTION = "docsui_launch_action"; + private static final String COUNT_ROOT_VISITED = "docsui_root_visited"; + private static final String COUNT_OPEN_MIME = "docsui_open_mime"; + private static final String COUNT_CREATE_MIME = "docsui_create_mime"; + private static final String COUNT_GET_CONTENT_MIME = "docsui_get_content_mime"; + private static final String COUNT_BROWSE_ROOT = "docsui_browse_root"; + private static final String COUNT_MANAGE_ROOT = "docsui_manage_root"; + private static final String COUNT_MULTI_WINDOW = "docsui_multi_window"; + + // Indices for bucketing roots in the roots histogram. "Other" is the catch-all index for any + // root that is not explicitly recognized by the Metrics code (see {@link + // #getSanitizedRootIndex}). Apps are also bucketed in this histogram using negative indices + // (see below). + private static final int ROOT_NONE = 0; + private static final int ROOT_OTHER = 1; + private static final int ROOT_AUDIO = 2; + private static final int ROOT_DEVICE_STORAGE = 3; + private static final int ROOT_DOWNLOADS = 4; + private static final int ROOT_HOME = 5; + private static final int ROOT_IMAGES = 6; + private static final int ROOT_RECENTS = 7; + private static final int ROOT_VIDEOS = 8; + // Apps aren't really "roots", but they are treated as such in the roots fragment UI and so they + // are logged analogously to roots. Use negative numbers to identify apps. + private static final int ROOT_THIRD_PARTY_APP = -1; + + // Indices for bucketing mime types. + private static final int MIME_OTHER = -2; // anything not enumerated below + private static final int MIME_NONE = -1; // null mime + private static final int MIME_ANY = 0; // */* + private static final int MIME_APPLICATION = 1; // application/* + private static final int MIME_AUDIO = 2; // audio/* + private static final int MIME_IMAGE = 3; // image/* + private static final int MIME_MESSAGE = 4; // message/* + private static final int MIME_MULTIPART = 5; // multipart/* + private static final int MIME_TEXT = 6; // text/* + private static final int MIME_VIDEO = 7; // video/* + + /** + * Logs when DocumentsUI is started, and how. Call this when DocumentsUI first starts up. + * + * @param context + * @param state + * @param intent + */ + public static void logActivityLaunch(Context context, State state, Intent intent) { + // Log the launch action. + logHistogram(context, COUNT_LAUNCH_ACTION, state.action); + // Then log auxiliary data (roots/mime types) associated with some actions. + Uri uri = intent.getData(); + switch (state.action) { + case State.ACTION_OPEN: + logHistogram(context, COUNT_OPEN_MIME, sanitizeMime(intent.getType())); + break; + case State.ACTION_CREATE: + logHistogram(context, COUNT_CREATE_MIME, sanitizeMime(intent.getType())); + break; + case State.ACTION_GET_CONTENT: + logHistogram(context, COUNT_GET_CONTENT_MIME, sanitizeMime(intent.getType())); + break; + case State.ACTION_MANAGE: + logHistogram(context, COUNT_MANAGE_ROOT, sanitizeRoot(uri)); + break; + case State.ACTION_BROWSE: + logHistogram(context, COUNT_BROWSE_ROOT, sanitizeRoot(uri)); + break; + default: + break; + } + } + + /** + * Logs a root visited event. Call this when the user clicks on a root in the RootsFragment. + * + * @param context + * @param info + */ + public static void logRootVisited(Context context, RootInfo info) { + logHistogram(context, COUNT_ROOT_VISITED, sanitizeRoot(info)); + } + + /** + * Logs an app visited event. Call this when the user clicks on an app in the RootsFragment. + * + * @param context + * @param info + */ + public static void logAppVisited(Context context, ResolveInfo info) { + logHistogram(context, COUNT_ROOT_VISITED, sanitizeRoot(info)); + } + + /** + * Logs a multi-window start. Call this when the user spawns a new DocumentsUI window. + * + * @param context + */ + public static void logMultiWindow(Context context) { + logCount(context, COUNT_MULTI_WINDOW); + } + + /** + * Internal method for making a MetricsLogger.count call. Increments the given counter by 1. + * + * @param context + * @param name The counter to increment. + */ + private static void logCount(Context context, String name) { + if (DEBUG) Log.d(TAG, name + ": " + 1); + MetricsLogger.count(context, name, 1); + } + + /** + * Internal method for making a MetricsLogger.histogram call. + * + * @param context + * @param name The name of the histogram. + * @param bucket The bucket to increment. + */ + private static void logHistogram(Context context, String name, int bucket) { + if (DEBUG) Log.d(TAG, name + ": " + bucket); + MetricsLogger.histogram(context, name, bucket); + } + + /** + * Generates an integer identifying the given root. For privacy, this function only recognizes a + * small set of hard-coded roots (ones provided by the system). Other roots are all grouped into + * a single ROOT_OTHER bucket. + */ + private static int sanitizeRoot(Uri uri) { + if (LauncherActivity.isLaunchUri(uri)) { + return ROOT_NONE; + } + + switch (uri.getAuthority()) { + case AUTHORITY_MEDIA: + switch (DocumentsContract.getRootId(uri)) { + case "audio_root": + return ROOT_AUDIO; + case "images_root": + return ROOT_IMAGES; + case "videos_root": + return ROOT_VIDEOS; + default: + return ROOT_OTHER; + } + case AUTHORITY_STORAGE: + if ("home".equals(DocumentsContract.getRootId(uri))) { + return ROOT_HOME; + } else { + return ROOT_DEVICE_STORAGE; + } + case AUTHORITY_DOWNLOADS: + return ROOT_DOWNLOADS; + default: + return ROOT_OTHER; + } + } + + /** @see #sanitizeRoot(Uri) */ + private static int sanitizeRoot(RootInfo root) { + if (root.isRecents()) { + // Recents root is special and only identifiable via this method call. Other roots are + // identified by URI. + return ROOT_RECENTS; + } else { + return sanitizeRoot(root.getUri()); + } + } + + /** @see #sanitizeRoot(Uri) */ + private static int sanitizeRoot(ResolveInfo info) { + // Log all apps under a single bucket in the roots histogram. + return ROOT_THIRD_PARTY_APP; + } + + /** + * Generates an int identifying a mime type. For privacy, this function only recognizes a small + * set of hard-coded types. For any other type, this function returns "other". + * + * @param mimeType + * @return + */ + private static int sanitizeMime(String mimeType) { + if (mimeType == null) { + return MIME_NONE; + } else if ("*/*".equals(mimeType)) { + return MIME_ANY; + } else { + String type = mimeType.substring(0, mimeType.indexOf('/')); + switch (type) { + case "application": + return MIME_APPLICATION; + case "audio": + return MIME_AUDIO; + case "image": + return MIME_IMAGE; + case "message": + return MIME_MESSAGE; + case "multipart": + return MIME_MULTIPART; + case "text": + return MIME_TEXT; + case "video": + return MIME_VIDEO; + } + } + // Bucket all other types into one bucket. + return MIME_OTHER; + } +} diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java index beff196509b2..c2aeb86a1f07 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java @@ -180,10 +180,14 @@ public class RootsFragment extends Fragment { Item item = mAdapter.getItem(position); if (item instanceof RootItem) { BaseActivity activity = BaseActivity.get(RootsFragment.this); - activity.onRootPicked(((RootItem) item).root); + RootInfo info = ((RootItem) item).root; + Metrics.logRootVisited(getActivity(), info); + activity.onRootPicked(info); } else if (item instanceof AppItem) { DocumentsActivity activity = DocumentsActivity.get(RootsFragment.this); - activity.onAppPicked(((AppItem) item).info); + ResolveInfo info = ((AppItem) item).info; + Metrics.logAppVisited(getActivity(), info); + activity.onAppPicked(info); } else if (item instanceof SpacerItem) { if (DEBUG) Log.d(TAG, "Ignoring click on spacer item."); } else { diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java index 809db3110338..74fa8d07da87 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java @@ -385,6 +385,7 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi FileOperations.start( getActivity(), getDisplayState().selectedDocumentsForCopy, + getDisplayState().stack.peek(), (DocumentStack) data.getParcelableExtra(Shared.EXTRA_STACK), operationType); } @@ -782,20 +783,21 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi private void deleteDocuments(final Selection selected) { - checkArgument(!selected.isEmpty()); - new GetDocumentsTask() { - @Override - void onDocumentsReady(List<DocumentInfo> docs) { - // Hide the files in the UI. - final SparseArray<String> hidden = mAdapter.hide(selected.getAll()); - - checkState(DELETE_JOB_DELAY > DELETE_UNDO_TIMEOUT); - String operationId = FileOperations.delete( - getActivity(), docs, getDisplayState().stack, - DELETE_JOB_DELAY); - showDeleteSnackbar(hidden, operationId); - } - }.execute(selected); + checkArgument(!selected.isEmpty()); + final DocumentInfo srcParent = getDisplayState().stack.peek(); + new GetDocumentsTask() { + @Override + void onDocumentsReady(List<DocumentInfo> docs) { + // Hide the files in the UI. + final SparseArray<String> hidden = mAdapter.hide(selected.getAll()); + + checkState(DELETE_JOB_DELAY > DELETE_UNDO_TIMEOUT); + String operationId = FileOperations.delete( + getActivity(), docs, srcParent, getDisplayState().stack, + DELETE_JOB_DELAY); + showDeleteSnackbar(hidden, operationId); + } + }.execute(selected); } private void showDeleteSnackbar(final SparseArray<String> hidden, final String jobId) { diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java b/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java index f3195a7a0fdf..1d8eba53e443 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java +++ b/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java @@ -199,7 +199,7 @@ class CopyJob extends Job { "Copying " + srcInfo.displayName + " (" + srcInfo.derivedUri + ")" + " to " + dstInfo.displayName + " (" + dstInfo.derivedUri + ")"); - processDocument(srcInfo, dstInfo); + processDocument(srcInfo, null, dstInfo); } } @@ -220,11 +220,15 @@ class CopyJob extends Job { * Copies a the given document to the given location. * * @param src DocumentInfos for the documents to copy. + * @param srcParent DocumentInfo for the parent of the document to process. * @param dstDirInfo The destination directory. * @return True on success, false on failure. * @throws RemoteException + * + * TODO: Stop passing srcParent, as it's not used for copy, but for move only. */ - boolean processDocument(DocumentInfo src, DocumentInfo dstDirInfo) throws RemoteException { + boolean processDocument(DocumentInfo src, DocumentInfo srcParent, + DocumentInfo dstDirInfo) throws RemoteException { // TODO: When optimized copy kicks in, we'll not making any progress updates. // For now. Local storage isn't using optimized copy. @@ -332,7 +336,7 @@ class CopyJob extends Job { cursor = getClient(srcDir).query(queryUri, queryColumns, null, null, null); while (cursor.moveToNext() && !isCanceled()) { DocumentInfo src = DocumentInfo.fromCursor(cursor, srcDir.authority); - success &= processDocument(src, destDir); + success &= processDocument(src, srcDir, destDir); } } finally { IoUtils.closeQuietly(cursor); @@ -509,7 +513,7 @@ class CopyJob extends Job { .append("CopyJob") .append("{") .append("id=" + id) - .append("srcs=" + mSrcs) + .append(", srcs=" + mSrcs) .append(", destination=" + stack) .append("}") .toString(); diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java b/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java index 6a2a7946d99c..eef696a1cfbf 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java +++ b/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java @@ -35,6 +35,7 @@ final class DeleteJob extends Job { private static final String TAG = "DeleteJob"; private List<DocumentInfo> mSrcs; + final DocumentInfo mSrcParent; /** * Moves files to a destination identified by {@code destination}. @@ -43,12 +44,14 @@ final class DeleteJob extends Job { * * @see @link {@link Job} constructor for most param descriptions. * - * @param srcs List of files to delete + * @param srcs List of files to delete. + * @param srcParent Parent of all source files. */ DeleteJob(Context service, Context appContext, Listener listener, - String id, DocumentStack stack, List<DocumentInfo> srcs) { + String id, DocumentStack stack, List<DocumentInfo> srcs, DocumentInfo srcParent) { super(service, appContext, listener, OPERATION_DELETE, id, stack); this.mSrcs = srcs; + this.mSrcParent = srcParent; } @Override @@ -75,6 +78,8 @@ final class DeleteJob extends Job { void start() throws RemoteException { for (DocumentInfo doc : mSrcs) { if (DEBUG) Log.d(TAG, "Deleting document @ " + doc.derivedUri); + // TODO: Start using mSrcParent as soon as DocumentsProvider::removeDocument() is + // implemented. if (!deleteDocument(doc)) { Log.w(TAG, "Failed to delete document @ " + doc.derivedUri); onFileFailed(doc); @@ -88,7 +93,8 @@ final class DeleteJob extends Job { .append("DeleteJob") .append("{") .append("id=" + id) - .append("srcs=" + mSrcs) + .append(", srcs=" + mSrcs) + .append(", srcParent=" + mSrcParent) .append(", location=" + stack) .append("}") .toString(); diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/FileOperationService.java b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperationService.java index aca2d7a55f5d..5d74cdc0b82a 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/services/FileOperationService.java +++ b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperationService.java @@ -65,6 +65,11 @@ public class FileOperationService extends Service implements Job.Listener { public static final String EXTRA_SRC_LIST = "com.android.documentsui.SRC_LIST"; public static final String EXTRA_FAILURE = "com.android.documentsui.FAILURE"; + // This extra is used only for moving and deleting. Currently it's not the case, + // but in the future those files may be from multiple different parents. In + // such case, this needs to be replaced with pairs of parent and child. + public static final String EXTRA_SRC_PARENT = "com.android.documentsui.SRC_PARENT"; + public static final int OPERATION_UNKNOWN = -1; public static final int OPERATION_COPY = 1; public static final int OPERATION_MOVE = 2; @@ -152,14 +157,14 @@ public class FileOperationService extends Service implements Job.Listener { Job job = null; synchronized (mRunning) { if (mWakeLock == null) { - mWakeLock = mPowerManager.newWakeLock( - PowerManager.PARTIAL_WAKE_LOCK, TAG); + mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); } List<DocumentInfo> srcs = intent.getParcelableArrayListExtra(EXTRA_SRC_LIST); + DocumentInfo srcParent = intent.getParcelableExtra(EXTRA_SRC_PARENT); DocumentStack stack = intent.getParcelableExtra(Shared.EXTRA_STACK); - job = createJob(operationType, jobId, srcs, stack); + job = createJob(operationType, jobId, srcs, srcParent, stack); if (job == null) { return; @@ -222,7 +227,8 @@ public class FileOperationService extends Service implements Job.Listener { */ @GuardedBy("mRunning") private @Nullable Job createJob( - @OpType int operationType, String id, List<DocumentInfo> srcs, DocumentStack stack) { + @OpType int operationType, String id, List<DocumentInfo> srcs, DocumentInfo srcParent, + DocumentStack stack) { if (mRunning.containsKey(id)) { Log.w(TAG, "Duplicate job id: " + id @@ -236,10 +242,12 @@ public class FileOperationService extends Service implements Job.Listener { job = jobFactory.createCopy(this, getApplicationContext(), this, id, stack, srcs); break; case OPERATION_MOVE: - job = jobFactory.createMove(this, getApplicationContext(), this, id, stack, srcs); + job = jobFactory.createMove(this, getApplicationContext(), this, id, stack, srcs, + srcParent); break; case OPERATION_DELETE: - job = jobFactory.createDelete(this, getApplicationContext(), this, id, stack, srcs); + job = jobFactory.createDelete(this, getApplicationContext(), this, id, stack, srcs, + srcParent); break; default: throw new UnsupportedOperationException(); diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java index f59a32ad60ea..9d017ee7943d 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java +++ b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java @@ -26,6 +26,7 @@ import static com.android.documentsui.services.FileOperationService.EXTRA_DELAY; import static com.android.documentsui.services.FileOperationService.EXTRA_JOB_ID; import static com.android.documentsui.services.FileOperationService.EXTRA_OPERATION; import static com.android.documentsui.services.FileOperationService.EXTRA_SRC_LIST; +import static com.android.documentsui.services.FileOperationService.EXTRA_SRC_PARENT; import static com.android.documentsui.services.FileOperationService.OPERATION_COPY; import static com.android.documentsui.services.FileOperationService.OPERATION_DELETE; import static com.android.documentsui.services.FileOperationService.OPERATION_MOVE; @@ -35,6 +36,7 @@ import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.os.Parcelable; +import android.support.annotation.VisibleForTesting; import android.support.design.widget.Snackbar; import android.util.Log; @@ -65,8 +67,8 @@ public final class FileOperations { * Tries to start the activity. Returns the job id. */ public static String start( - Activity activity, List<DocumentInfo> srcDocs, DocumentStack stack, - int operationType) { + Activity activity, List<DocumentInfo> srcDocs, + DocumentStack stack, int operationType) { if (DEBUG) Log.d(TAG, "Handling generic 'start' call."); @@ -74,7 +76,7 @@ public final class FileOperations { case OPERATION_COPY: return FileOperations.copy(activity, srcDocs, stack); case OPERATION_MOVE: - return FileOperations.move(activity, srcDocs, stack); + throw new IllegalArgumentException("Moving requires providing the source parent."); case OPERATION_DELETE: throw new UnsupportedOperationException("Delete isn't currently supported."); default: @@ -83,14 +85,27 @@ public final class FileOperations { } /** - * Makes a best effort to cancel operation identified by jobId. - * - * @param context Context for the intent. - * @param jobId The id of the job to cancel. - * Use {@link #createJobId} if you don't have one handy. - * @param srcDocs A list of src files to copy. - * @param dstStack The copy destination stack. + * Tries to start the activity. Returns the job id. */ + public static String start( + Activity activity, List<DocumentInfo> srcDocs, DocumentInfo srcParent, + DocumentStack stack, int operationType) { + + if (DEBUG) Log.d(TAG, "Handling generic 'start' call."); + + switch (operationType) { + case OPERATION_COPY: + return FileOperations.copy(activity, srcDocs, stack); + case OPERATION_MOVE: + return FileOperations.move(activity, srcDocs, srcParent, stack); + case OPERATION_DELETE: + throw new UnsupportedOperationException("Delete isn't currently supported."); + default: + throw new UnsupportedOperationException("Unknown operation: " + operationType); + } + } + + @VisibleForTesting public static void cancel(Activity activity, String jobId) { if (DEBUG) Log.d(TAG, "Attempting to canceling operation: " + jobId); @@ -101,15 +116,7 @@ public final class FileOperations { activity.startService(intent); } - /** - * Starts the service for a copy operation. - * - * @param context Context for the intent. - * @param jobId A unique jobid for this job. - * Use {@link #createJobId} if you don't have one handy. - * @param srcDocs A list of src files to copy. - * @param destination The copy destination stack. - */ + @VisibleForTesting public static String copy( Activity activity, List<DocumentInfo> srcDocs, DocumentStack destination) { String jobId = createJobId(); @@ -131,14 +138,17 @@ public final class FileOperations { * @param jobId A unique jobid for this job. * Use {@link #createJobId} if you don't have one handy. * @param srcDocs A list of src files to copy. + * @param srcParent Parent of all the source documents. * @param destination The move destination stack. */ public static String move( - Activity activity, List<DocumentInfo> srcDocs, DocumentStack destination) { + Activity activity, List<DocumentInfo> srcDocs, DocumentInfo srcParent, + DocumentStack destination) { String jobId = createJobId(); if (DEBUG) Log.d(TAG, "Initiating 'move' operation id: " + jobId); - Intent intent = createBaseIntent(OPERATION_MOVE, activity, jobId, srcDocs, destination); + Intent intent = createBaseIntent(OPERATION_MOVE, activity, jobId, srcDocs, srcParent, + destination); createSharedSnackBar(activity, R.plurals.move_begin, srcDocs.size()) .show(); @@ -154,16 +164,19 @@ public final class FileOperations { * @param jobId A unique jobid for this job. * Use {@link #createJobId} if you don't have one handy. * @param srcDocs A list of src files to copy. + * @param srcParent Parent of all the source documents. * @param delay Number of milliseconds to wait before executing the job. * @return Id of the job. */ public static String delete( - Activity activity, List<DocumentInfo> srcDocs, DocumentStack location, int delay) { + Activity activity, List<DocumentInfo> srcDocs, DocumentInfo srcParent, + DocumentStack location, int delay) { String jobId = createJobId(); if (DEBUG) Log.d(TAG, "Initiating 'delete' operation id " + jobId + " delayed by " + delay + " milliseconds."); - Intent intent = createBaseIntent(OPERATION_DELETE, activity, jobId, srcDocs, location); + Intent intent = createBaseIntent(OPERATION_DELETE, activity, jobId, srcDocs, srcParent, + location); intent.putExtra(EXTRA_DELAY, delay); activity.startService(intent); @@ -171,21 +184,43 @@ public final class FileOperations { } /** - * Starts the service for a move operation. + * Starts the service for an operation. + * + * @param jobId A unique jobid for this job. + * Use {@link #createJobId} if you don't have one handy. + * @param srcDocs A list of src files to copy. + * @return Id of the job. + */ + public static Intent createBaseIntent( + @OpType int operationType, Context context, String jobId, List<DocumentInfo> srcDocs, + DocumentStack localeStack) { + + Intent intent = new Intent(context, FileOperationService.class); + intent.putExtra(EXTRA_JOB_ID, jobId); + intent.putParcelableArrayListExtra(EXTRA_SRC_LIST, asArrayList(srcDocs)); + intent.putExtra(EXTRA_STACK, (Parcelable) localeStack); + intent.putExtra(EXTRA_OPERATION, operationType); + + return intent; + } + + /** + * Starts the service for an operation. * * @param jobId A unique jobid for this job. * Use {@link #createJobId} if you don't have one handy. * @param srcDocs A list of src files to copy. + * @param srcParent Parent of all the source documents. * @return Id of the job. */ public static Intent createBaseIntent( @OpType int operationType, Context context, String jobId, - List<DocumentInfo> srcDocs, DocumentStack localeStack) { + List<DocumentInfo> srcDocs, DocumentInfo srcParent, DocumentStack localeStack) { Intent intent = new Intent(context, FileOperationService.class); intent.putExtra(EXTRA_JOB_ID, jobId); - intent.putParcelableArrayListExtra( - EXTRA_SRC_LIST, asArrayList(srcDocs)); + intent.putParcelableArrayListExtra(EXTRA_SRC_LIST, asArrayList(srcDocs)); + intent.putExtra(EXTRA_SRC_PARENT, srcParent); intent.putExtra(EXTRA_STACK, (Parcelable) localeStack); intent.putExtra(EXTRA_OPERATION, operationType); diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/Job.java b/packages/DocumentsUI/src/com/android/documentsui/services/Job.java index f351df94a0d8..9534d6cf118f 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/services/Job.java +++ b/packages/DocumentsUI/src/com/android/documentsui/services/Job.java @@ -274,13 +274,15 @@ abstract class Job implements Runnable { } Job createMove(Context service, Context appContext, Listener listener, - String id, DocumentStack stack, List<DocumentInfo> srcs) { - return new MoveJob(service, appContext, listener, id, stack, srcs); + String id, DocumentStack stack, List<DocumentInfo> srcs, + DocumentInfo srcParent) { + return new MoveJob(service, appContext, listener, id, stack, srcs, srcParent); } Job createDelete(Context service, Context appContext, Listener listener, - String id, DocumentStack stack, List<DocumentInfo> srcs) { - return new DeleteJob(service, appContext, listener, id, stack, srcs); + String id, DocumentStack stack, List<DocumentInfo> srcs, + DocumentInfo srcParent) { + return new DeleteJob(service, appContext, listener, id, stack, srcs, srcParent); } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java b/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java index 7a238bd6ce7d..9b7207726256 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java +++ b/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java @@ -21,6 +21,7 @@ import static com.android.documentsui.services.FileOperationService.OPERATION_MO import android.app.Notification; import android.app.Notification.Builder; import android.content.Context; +import android.net.Uri; import android.os.RemoteException; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; @@ -32,9 +33,11 @@ import com.android.documentsui.model.DocumentStack; import java.util.List; +// TODO: Stop extending CopyJob. final class MoveJob extends CopyJob { private static final String TAG = "MoveJob"; + final DocumentInfo mSrcParent; /** * Moves files to a destination identified by {@code destination}. @@ -44,10 +47,12 @@ final class MoveJob extends CopyJob { * @see @link {@link Job} constructor for most param descriptions. * * @param srcs List of files to be moved. + * @param srcParent Parent of all source files. */ MoveJob(Context service, Context appContext, Listener listener, - String id, DocumentStack destination, List<DocumentInfo> srcs) { + String id, DocumentStack destination, List<DocumentInfo> srcs, DocumentInfo srcParent) { super(service, appContext, listener, OPERATION_MOVE, id, destination, srcs); + this.mSrcParent = srcParent; } @Override @@ -75,8 +80,8 @@ final class MoveJob extends CopyJob { R.plurals.move_error_notification_title, R.drawable.ic_menu_copy); } - @Override - boolean processDocument(DocumentInfo src, DocumentInfo dest) throws RemoteException { + boolean processDocument(DocumentInfo src, DocumentInfo srcParent, DocumentInfo dest) + throws RemoteException { // TODO: When optimized move kicks in, we're not making any progress updates. FIX IT! @@ -85,6 +90,7 @@ final class MoveJob extends CopyJob { if (src.authority.equals(dest.authority)) { if ((src.flags & Document.FLAG_SUPPORTS_MOVE) != 0) { if (DocumentsContract.moveDocument(getClient(src), src.derivedUri, + srcParent != null ? srcParent.derivedUri : mSrcParent.derivedUri, dest.derivedUri) == null) { onFileFailed(src); return false; @@ -105,6 +111,7 @@ final class MoveJob extends CopyJob { // If we couldn't do an optimized copy...we fall back to vanilla byte copy. boolean copied = byteCopyDocument(src, dest); + // TODO: Replace deleteDocument() with removeDocument() once implemented. return copied && !isCanceled() && deleteDocument(src); } @@ -114,7 +121,8 @@ final class MoveJob extends CopyJob { .append("MoveJob") .append("{") .append("id=" + id) - .append("srcs=" + mSrcs) + .append(", srcs=" + mSrcs) + .append(", srcParent=" + mSrcParent) .append(", destination=" + stack) .append("}") .toString(); diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java b/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java index 3c40b672cdcc..467d97ec6402 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java @@ -20,6 +20,7 @@ import static android.provider.DocumentsContract.buildChildDocumentsUri; import static android.provider.DocumentsContract.buildDocumentUri; import static android.provider.DocumentsContract.buildRootsUri; import static com.android.documentsui.model.DocumentInfo.getCursorString; +import static com.android.internal.util.Preconditions.checkArgument; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.fail; @@ -37,7 +38,7 @@ import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Root; import android.support.annotation.Nullable; import android.test.MoreAsserts; -import android.util.Log; +import android.text.TextUtils; import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.RootInfo; @@ -60,13 +61,13 @@ public class DocumentsProviderHelper { private final ContentProviderClient mClient; public DocumentsProviderHelper(String authority, ContentProviderClient client) { + checkArgument(!TextUtils.isEmpty(authority)); mAuthority = authority; mClient = client; } public RootInfo getRoot(String documentId) throws RemoteException { final Uri rootsUri = buildRootsUri(mAuthority); - Cursor cursor = null; try { cursor = mClient.query(rootsUri, null, null, null, null); @@ -126,7 +127,6 @@ public class DocumentsProviderHelper { } public byte[] readDocument(Uri documentUri) throws RemoteException, IOException { - Log.d("DocumentsProviderHelper", "Trying to read file contents: " + documentUri); ParcelFileDescriptor file = mClient.openFile(documentUri, "r", null); byte[] buf = null; try (AutoCloseInputStream in = new AutoCloseInputStream(file)) { @@ -245,21 +245,18 @@ public class DocumentsProviderHelper { Uri uri = buildChildDocumentsUri(mAuthority, documentId); List<DocumentInfo> children = new ArrayList<>(); try (Cursor cursor = mClient.query(uri, null, null, null, null, null)) { - while (cursor.moveToNext()) { - children.add(DocumentInfo.fromDirectoryCursor(cursor)); + Cursor wrapper = new RootCursorWrapper(mAuthority, "totally-fake", cursor, 100); + while (wrapper.moveToNext()) { + children.add(DocumentInfo.fromDirectoryCursor(wrapper)); } } return children; } public void assertFileContents(Uri documentUri, byte[] expected) throws Exception { - // TODO: Fix this: java.lang.SecurityException: - // The authority of the uri content:/document/%2Fdata%2Fuser%2F0%2Fcom.android.documentsui.\ - // tests%2Fcache%2FTEST_ROOT_1%2Ftest1.txt does not match the one of the contentProvider: \ - // com.android.documentsui.stubprovider -// MoreAsserts.assertEquals( -// "Copied file contents differ", -// expected, readDocument(documentUri)); + MoreAsserts.assertEquals( + "Copied file contents differ", + expected, readDocument(documentUri)); } public void assertFileContents(String parentId, String fileName, byte[] expected) diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/services/AbstractCopyJobTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/services/AbstractCopyJobTest.java index ec21150412f5..eb8a061833b2 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/services/AbstractCopyJobTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/services/AbstractCopyJobTest.java @@ -23,6 +23,7 @@ import android.provider.DocumentsContract; import android.test.suitebuilder.annotation.MediumTest; import com.android.documentsui.model.DocumentInfo; +import com.android.documentsui.model.DocumentStack; import java.util.List; @@ -109,7 +110,9 @@ public abstract class AbstractCopyJobTest<T extends CopyJob> extends AbstractJob public void runNoCopyDirToSelfTest() throws Exception { Uri testDir = mDocs.createFolder(mSrcRoot, "someDir"); - createJob(newArrayList(testDir), testDir).run(); + createJob(newArrayList(testDir), + DocumentsContract.buildDocumentUri(AUTHORITY, mSrcRoot.documentId), + testDir).run(); mJobListener.waitForFinished(); mJobListener.assertFailed(); @@ -122,7 +125,9 @@ public abstract class AbstractCopyJobTest<T extends CopyJob> extends AbstractJob Uri testDir = mDocs.createFolder(mSrcRoot, "someDir"); Uri destDir = mDocs.createFolder(testDir, "theDescendent"); - createJob(newArrayList(testDir), destDir).run(); + createJob(newArrayList(testDir), + DocumentsContract.buildDocumentUri(AUTHORITY, mSrcRoot.documentId), + destDir).run(); mJobListener.waitForFinished(); mJobListener.assertFailed(); @@ -148,10 +153,13 @@ public abstract class AbstractCopyJobTest<T extends CopyJob> extends AbstractJob } /** - * Creates a job with a stack consisting to the default destination. + * Creates a job with a stack consisting to the default source and destination. + * TODO: Clean up, as mDestRoot.documentInfo may not really be the parent of + * srcs. */ final T createJob(List<Uri> srcs) throws Exception { + Uri srcParent = DocumentsContract.buildDocumentUri(AUTHORITY, mSrcRoot.documentId); Uri destination = DocumentsContract.buildDocumentUri(AUTHORITY, mDestRoot.documentId); - return createJob(srcs, destination); + return createJob(srcs, srcParent, destination); } } diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/services/AbstractJobTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/services/AbstractJobTest.java index 691af6ac03e8..e559503f8b30 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/services/AbstractJobTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/services/AbstractJobTest.java @@ -85,7 +85,7 @@ public abstract class AbstractJobTest<T extends Job> extends AndroidTestCase { mDestRoot = mDocs.getRoot(ROOT_1_ID); } - final T createJob(List<Uri> srcs, Uri destination) throws Exception { + final T createJob(List<Uri> srcs, Uri srcParent, Uri destination) throws Exception { DocumentStack stack = new DocumentStack(); stack.push(DocumentInfo.fromUri(mResolver, destination)); @@ -94,8 +94,9 @@ public abstract class AbstractJobTest<T extends Job> extends AndroidTestCase { srcDocs.add(DocumentInfo.fromUri(mResolver, src)); } - return createJob(srcDocs, stack); + return createJob(srcDocs, DocumentInfo.fromUri(mResolver, srcParent), stack); } - abstract T createJob(List<DocumentInfo> srcs, DocumentStack destination) throws Exception; + abstract T createJob(List<DocumentInfo> srcs, DocumentInfo srcParent, DocumentStack destination) + throws Exception; } diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/services/CopyJobTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/services/CopyJobTest.java index 1acf2dc5f4cf..543396eec7b5 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/services/CopyJobTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/services/CopyJobTest.java @@ -59,7 +59,9 @@ public class CopyJobTest extends AbstractCopyJobTest<CopyJob> { } @Override - CopyJob createJob(List<DocumentInfo> srcs, DocumentStack stack) throws Exception { + // TODO: Stop passing srcParent here, as it's not used for copying. + CopyJob createJob(List<DocumentInfo> srcs, DocumentInfo srcParent, DocumentStack stack) + throws Exception { return new CopyJob( mContext, mContext, mJobListener, FileOperations.createJobId(), stack, srcs); } diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/services/DeleteJobTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/services/DeleteJobTest.java index d6d102397280..722df75593bd 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/services/DeleteJobTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/services/DeleteJobTest.java @@ -37,7 +37,8 @@ public class DeleteJobTest extends AbstractJobTest<DeleteJob> { Uri testFile2 = mDocs.createDocument(mSrcRoot, "text/plain", "test2.txt"); mDocs.writeDocument(testFile2, FRUITY_BYTES); - createJob(newArrayList(testFile1, testFile2)).run(); + createJob(newArrayList(testFile1, testFile2), + DocumentsContract.buildDocumentUri(AUTHORITY, mSrcRoot.documentId)).run(); mJobListener.waitForFinished(); mDocs.assertChildCount(mSrcRoot, 0); @@ -46,14 +47,17 @@ public class DeleteJobTest extends AbstractJobTest<DeleteJob> { /** * Creates a job with a stack consisting to the default src directory. */ - private final DeleteJob createJob(List<Uri> srcs) throws Exception { + private final DeleteJob createJob(List<Uri> srcs, Uri srcParent) throws Exception { Uri stack = DocumentsContract.buildDocumentUri(AUTHORITY, mSrcRoot.documentId); - return createJob(srcs, stack); + return createJob(srcs, srcParent, stack); } @Override - DeleteJob createJob(List<DocumentInfo> srcs, DocumentStack stack) throws Exception { + // TODO: Remove inheritance, as stack is not used for deleting, nor srcParent. + DeleteJob createJob(List<DocumentInfo> srcs, DocumentInfo srcParent, DocumentStack stack) + throws Exception { return new DeleteJob( - mContext, mContext, mJobListener, FileOperations.createJobId(), stack, srcs); + mContext, mContext, mJobListener, FileOperations.createJobId(), stack, srcs, + srcParent); } } diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/services/MoveJobTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/services/MoveJobTest.java index 69d2db778be9..749264a9ada8 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/services/MoveJobTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/services/MoveJobTest.java @@ -19,6 +19,8 @@ package com.android.documentsui.services; import static com.google.common.collect.Lists.newArrayList; import android.net.Uri; +import android.provider.DocumentsContract.Document; +import android.provider.DocumentsContract; import android.test.suitebuilder.annotation.MediumTest; import com.android.documentsui.model.DocumentInfo; @@ -36,8 +38,9 @@ public class MoveJobTest extends AbstractCopyJobTest<MoveJob> { } public void testMoveVirtualTypedFile() throws Exception { + mDocs.createFolder(mSrcRoot, "hello"); Uri testFile = mDocs.createVirtualFile( - mSrcRoot, "/virtual.sth", "virtual/mime-type", + mSrcRoot, "/hello/virtual.sth", "virtual/mime-type", FRUITY_BYTES, "application/pdf", "text/html"); createJob(newArrayList(testFile)).run(); @@ -89,9 +92,13 @@ public class MoveJobTest extends AbstractCopyJobTest<MoveJob> { mDocs.assertChildCount(mSrcRoot, 1); } + // TODO: Add test cases for moving when multi-parented. + @Override - MoveJob createJob(List<DocumentInfo> srcs, DocumentStack stack) throws Exception { + MoveJob createJob(List<DocumentInfo> srcs, DocumentInfo srcParent, DocumentStack stack) + throws Exception { return new MoveJob( - mContext, mContext, mJobListener, FileOperations.createJobId(), stack, srcs); + mContext, mContext, mJobListener, FileOperations.createJobId(), stack, srcs, + srcParent); } } diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index c6e55314770a..43527a23539c 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -463,7 +463,8 @@ public class ExternalStorageProvider extends DocumentsProvider { } @Override - public String moveDocument(String sourceDocumentId, String targetParentDocumentId) + public String moveDocument(String sourceDocumentId, String sourceParentDocumentId, + String targetParentDocumentId) throws FileNotFoundException { final File before = getFileForDocId(sourceDocumentId); final File after = new File(getFileForDocId(targetParentDocumentId), before.getName()); diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java index b4f9b9f4a653..a8419bf4cbdf 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -188,7 +188,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { /** Tracks whether strong authentication hasn't been used since quite some time per user. */ private ArraySet<Integer> mStrongAuthNotTimedOut = new ArraySet<>(); - private final StrongAuthTracker mStrongAuthTracker = new StrongAuthTracker(); + private final StrongAuthTracker mStrongAuthTracker; private final ArrayList<WeakReference<KeyguardUpdateMonitorCallback>> mCallbacks = Lists.newArrayList(); @@ -871,6 +871,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } public class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker { + public StrongAuthTracker(Context context) { + super(context); + } public boolean isUnlockingWithFingerprintAllowed() { int userId = getCurrentUser(); @@ -981,6 +984,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { mSubscriptionManager = SubscriptionManager.from(context); mAlarmManager = context.getSystemService(AlarmManager.class); mDeviceProvisioned = isDeviceProvisionedInSettingsDb(); + mStrongAuthTracker = new StrongAuthTracker(context); // Since device can't be un-provisioned, we only need to register a content observer // to update mDeviceProvisioned when we are... diff --git a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp index 66a3247f78e6..7b57d8916859 100644 --- a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp +++ b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp @@ -332,23 +332,20 @@ private: uint64_t offset, uint32_t size, void* buf) { - const uint32_t read_size = static_cast<uint32_t>(std::min( - static_cast<uint64_t>(size), - get_file_size(inode) - offset)); ScopedLocalRef<jbyteArray> array(env_, static_cast<jbyteArray>(env_->CallObjectMethod( self_, app_fuse_get_object_bytes, inode, offset, - read_size))); + size))); if (array.get() == nullptr) { return -1; } ScopedByteArrayRO bytes(env_, array.get()); - if (bytes.size() != read_size || bytes.get() == nullptr) { + if (bytes.get() == nullptr) { return -1; } - + const uint32_t read_size = std::min(static_cast<uint32_t>(bytes.size()), size); memcpy(buf, bytes.get(), read_size); return read_size; } diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java index 3381656846a6..3ac1b3df04ac 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java @@ -37,10 +37,10 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.Preconditions; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -103,18 +103,28 @@ public class MtpDocumentsProvider extends DocumentsProvider { } @VisibleForTesting - void onCreateForTesting( + boolean onCreateForTesting( Resources resources, MtpManager mtpManager, ContentResolver resolver, - MtpDatabase database) { + MtpDatabase database, + StorageManager storageManager) { mResources = resources; mMtpManager = mtpManager; mResolver = resolver; mDeviceToolkits = new HashMap<Integer, DeviceToolkit>(); mDatabase = database; mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase); + mAppFuse = new AppFuse(TAG, new AppFuseCallback()); + // TODO: Mount AppFuse on demands. + try { + mAppFuse.mount(storageManager); + } catch (IOException e) { + Log.e(TAG, "Failed to start app fuse.", e); + return false; + } resume(); + return true; } @Override @@ -165,7 +175,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { // Fallback to non-seekable file descriptor. // TODO: Use getPartialObject64 for MTP devices that support Android vendor // extension. - if (fileSize <= 0xffffffff) { + if (fileSize <= 0xffffffffl) { return mAppFuse.openFile(Integer.parseInt(documentId)); } else { return getPipeManager(identifier).readDocument(mMtpManager, identifier); @@ -305,6 +315,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { throw new RuntimeException(e); } finally { mDatabase.close(); + mAppFuse.close(); super.shutdown(); } } @@ -385,9 +396,11 @@ public class MtpDocumentsProvider extends DocumentsProvider { @Override public byte[] getObjectBytes(int inode, long offset, int size) throws IOException { final Identifier identifier = mDatabase.createIdentifier(Integer.toString(inode)); - mMtpManager.getPartialObject( - identifier.mDeviceId, identifier.mObjectHandle, (int) offset, size, mBytes); - return mBytes; + final long readSize = mMtpManager.getPartialObject( + identifier.mDeviceId, identifier.mObjectHandle, offset, size, mBytes); + // TODO: Change signature so that getObjectBytes can return read size without copying + // bytes. + return Arrays.copyOf(mBytes, (int) readSize); } @Override diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java index b51cf71cf38e..7066f7d48401 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java @@ -20,18 +20,25 @@ import android.database.Cursor; import android.mtp.MtpConstants; import android.mtp.MtpObjectInfo; import android.net.Uri; +import android.os.ParcelFileDescriptor; +import android.os.storage.StorageManager; import android.provider.DocumentsContract.Root; +import android.system.ErrnoException; +import android.system.Os; +import android.system.OsConstants; import android.provider.DocumentsContract; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import android.test.suitebuilder.annotation.MediumTest; +import android.util.Log; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.Arrays; import java.util.concurrent.TimeoutException; import static com.android.mtp.MtpDatabase.strings; -@SmallTest +@MediumTest public class MtpDocumentsProviderTest extends AndroidTestCase { private final static Uri ROOTS_URI = DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY); @@ -421,10 +428,71 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { MtpDocumentsProvider.AUTHORITY, "1"))); } + public void testOpenDocument() throws Exception { + setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); + setupRoots(0, new MtpRoot[] { + new MtpRoot(0, 0, "Device", "Storage", 0, 0, "") + }); + final byte[] bytes = "Hello world".getBytes(); + setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] { + new MtpObjectInfo.Builder() + .setName("test.txt") + .setObjectHandle(1) + .setCompressedSize(bytes.length) + .setParent(-1) + .build() + }); + mMtpManager.setImportFileBytes(0, 1, bytes); + try (final ParcelFileDescriptor fd = mProvider.openDocument("3", "r", null)) { + final byte[] readBytes = new byte[5]; + assertEquals(6, Os.lseek(fd.getFileDescriptor(), 6, OsConstants.SEEK_SET)); + assertEquals(5, Os.read(fd.getFileDescriptor(), readBytes, 0, 5)); + assertTrue(Arrays.equals("world".getBytes(), readBytes)); + + assertEquals(0, Os.lseek(fd.getFileDescriptor(), 0, OsConstants.SEEK_SET)); + assertEquals(5, Os.read(fd.getFileDescriptor(), readBytes, 0, 5)); + assertTrue(Arrays.equals("Hello".getBytes(), readBytes)); + } + } + + public void testOpenDocument_shortBytes() throws Exception { + mMtpManager = new TestMtpManager(getContext()) { + @Override + MtpObjectInfo getObjectInfo(int deviceId, int objectHandle) throws IOException { + if (objectHandle == 1) { + return new MtpObjectInfo.Builder(super.getObjectInfo(deviceId, objectHandle)) + .setObjectHandle(1).setCompressedSize(1024 * 1024).build(); + } + + return super.getObjectInfo(deviceId, objectHandle); + } + }; + setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); + setupRoots(0, new MtpRoot[] { + new MtpRoot(0, 0, "Device", "Storage", 0, 0, "") + }); + final byte[] bytes = "Hello world".getBytes(); + setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] { + new MtpObjectInfo.Builder() + .setName("test.txt") + .setObjectHandle(1) + .setCompressedSize(bytes.length) + .setParent(-1) + .build() + }); + mMtpManager.setImportFileBytes(0, 1, bytes); + try (final ParcelFileDescriptor fd = mProvider.openDocument("3", "r", null)) { + final byte[] readBytes = new byte[1024 * 1024]; + assertEquals(11, Os.read(fd.getFileDescriptor(), readBytes, 0, readBytes.length)); + } + } + private void setupProvider(int flag) { mDatabase = new MtpDatabase(getContext(), flag); mProvider = new MtpDocumentsProvider(); - mProvider.onCreateForTesting(mResources, mMtpManager, mResolver, mDatabase); + final StorageManager storageManager = getContext().getSystemService(StorageManager.class); + assertTrue(mProvider.onCreateForTesting( + mResources, mMtpManager, mResolver, mDatabase, storageManager)); } private String[] getStrings(Cursor cursor) { diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java index a1732dc88ab2..1aaeb6092fa5 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java @@ -208,4 +208,16 @@ public class TestMtpManager extends MtpManager { byte[] getObject(int deviceId, int objectHandle, int expectedSize) throws IOException { return mImportFileBytes.get(pack(deviceId, objectHandle)); } + + @Override + long getPartialObject(int deviceId, int objectHandle, long offset, long size, byte[] buffer) + throws IOException { + final byte[] bytes = mImportFileBytes.get(pack(deviceId, objectHandle)); + int i = 0; + while (i < size && i + offset < bytes.length) { + buffer[i] = bytes[(int) (i + offset)]; + i++; + } + return i; + } } diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java index cd30e263a65d..46a2098ad5da 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java @@ -16,6 +16,7 @@ package com.android.printspooler.ui; +import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.Loader; @@ -452,7 +453,7 @@ public final class FusedPrintersProvider extends Loader<List<PrinterInfo>> return mPersistenceManager.mReadHistoryCompleted; } - public void setTrackedPrinter(PrinterId printerId) { + public void setTrackedPrinter(@Nullable PrinterId printerId) { if (isStarted() && mDiscoverySession != null && mDiscoverySession.isPrinterDiscoveryStarted()) { if (mTrackedPrinter != null) { diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java index 811adda74390..743df999fdab 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java @@ -1003,7 +1003,9 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat if (currMediaSize == null) { attributes.setMediaSize(defaults.getMediaSize()); } else { - boolean foundCurrentMediaSize = false; + MediaSize newMediaSize = null; + boolean isPortrait = currMediaSize.isPortrait(); + // Try to find the current media size in the capabilities as // it may be in a different orientation. MediaSize currMediaSizePortrait = currMediaSize.asPortrait(); @@ -1011,14 +1013,21 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat for (int i = 0; i < mediaSizeCount; i++) { MediaSize mediaSize = sortedMediaSizes.get(i); if (currMediaSizePortrait.equals(mediaSize.asPortrait())) { - attributes.setMediaSize(currMediaSize); - foundCurrentMediaSize = true; + newMediaSize = mediaSize; break; } } // If we did not find the current media size fall back to default. - if (!foundCurrentMediaSize) { - attributes.setMediaSize(defaults.getMediaSize()); + if (newMediaSize == null) { + newMediaSize = defaults.getMediaSize(); + } + + if (newMediaSize != null) { + if (isPortrait) { + attributes.setMediaSize(newMediaSize.asPortrait()); + } else { + attributes.setMediaSize(newMediaSize.asLandscape()); + } } } diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java index cbc568ae1e11..6d60bb86cd7e 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java @@ -16,6 +16,7 @@ package com.android.printspooler.ui; +import android.annotation.Nullable; import android.app.Activity; import android.app.LoaderManager.LoaderCallbacks; import android.content.Loader; @@ -89,7 +90,7 @@ public class PrinterRegistry { return false; } - public void setTrackedPrinter(PrinterId printerId) { + public void setTrackedPrinter(@Nullable PrinterId printerId) { FusedPrintersProvider provider = getPrinterProvider(); if (provider != null) { provider.setTrackedPrinter(printerId); diff --git a/packages/SettingsLib/res/values/attrs.xml b/packages/SettingsLib/res/values/attrs.xml index 15b2a978a04a..3e1fc4a4e343 100644 --- a/packages/SettingsLib/res/values/attrs.xml +++ b/packages/SettingsLib/res/values/attrs.xml @@ -16,7 +16,8 @@ <resources> <declare-styleable name="RestrictedPreference"> - <attr name="userRestriction" format="string"/> + <attr name="userRestriction" format="string" /> + <attr name="useAdminDisabledSummary" format="boolean" /> </declare-styleable> <declare-styleable name="WifiEncryptionState"> <attr name="state_encrypted" format="boolean" /> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 6dfa9ad2d26e..d3c8416a4abb 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -765,4 +765,7 @@ <!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed --> <string name="battery_info_status_full">Full</string> + <!-- Summary for settings preference disabled by administrator [CHAR LIMIT=50] --> + <string name="disabled_by_admin_summary_text">Disabled by administrator</string> + </resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java index d69250b8a6ba..d3c1364adac8 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java @@ -22,6 +22,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.IPackageManager; +import android.content.pm.UserInfo; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.os.Bundle; @@ -36,6 +37,8 @@ import android.text.style.ImageSpan; import android.view.MenuItem; import android.widget.TextView; +import com.android.internal.widget.LockPatternUtils; + import java.util.List; /** @@ -60,15 +63,18 @@ public class RestrictedLockUtils { * * @param userRestriction Restriction to check * @param userId User which we need to check if restriction is enforced on. - * @return EnforcedAdmin Object containing the enforce admin and admin user details, or - * {@code null} If the restriction is not set. If the restriction is set by both device owner - * and profile owner, then the admin will be set to {@code null} and userId to + * @return EnforcedAdmin Object containing the enforced admin component and admin user details, + * or {@code null} If the restriction is not set. If the restriction is set by both device owner + * and profile owner, then the admin component will be set to {@code null} and userId to * {@link UserHandle#USER_NULL}. */ public static EnforcedAdmin checkIfRestrictionEnforced(Context context, String userRestriction, int userId) { DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( Context.DEVICE_POLICY_SERVICE); + if (dpm == null) { + return null; + } ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnAnyUser(); int deviceOwnerUserId = dpm.getDeviceOwnerUserId(); boolean enforcedByDeviceOwner = false; @@ -109,45 +115,80 @@ public class RestrictedLockUtils { } /** - * Checks if lock screen notification features are disabled by policy. This should be - * only used for keyguard notification features but not the keyguard features - * (e.g. KEYGUARD_DISABLE_FINGERPRINT) where a profile owner can set them on the parent user - * as it won't work for that case. + * Checks if keyguard features are disabled by policy. * - * @param keyguardNotificationFeatures Could be any of notification features that can be + * @param keyguardFeatures Could be any of keyguard features that can be * disabled by {@link android.app.admin.DevicePolicyManager#setKeyguardDisabledFeatures}. - * @return EnforcedAdmin Object containing the enforce admin and admin user details, or - * {@code null} If the notification features are not disabled. If the restriction is set by - * multiple admins, then the admin will be set to {@code null} and userId to + * @return EnforcedAdmin Object containing the enforced admin component and admin user details, + * or {@code null} If the notification features are not disabled. If the restriction is set by + * multiple admins, then the admin component will be set to {@code null} and userId to * {@link UserHandle#USER_NULL}. */ - public static EnforcedAdmin checkIfKeyguardNotificationFeaturesDisabled(Context context, - int keyguardNotificationFeatures) { + public static EnforcedAdmin checkIfKeyguardFeaturesDisabled(Context context, + int keyguardFeatures) { final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( Context.DEVICE_POLICY_SERVICE); - boolean isDisabledByMultipleAdmins = false; - ComponentName adminComponent = null; - List<ComponentName> admins = dpm.getActiveAdmins(); - if (admins != null) { - int disabledKeyguardFeatures; + if (dpm == null) { + return null; + } + final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); + LockPatternUtils lockPatternUtils = new LockPatternUtils(context); + EnforcedAdmin enforcedAdmin = null; + final int userId = UserHandle.myUserId(); + if (um.getUserInfo(userId).isManagedProfile()) { + final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userId); + if (admins == null) { + return null; + } for (ComponentName admin : admins) { - disabledKeyguardFeatures = dpm.getKeyguardDisabledFeatures(admin); - if ((disabledKeyguardFeatures & keyguardNotificationFeatures) != 0) { - if (adminComponent == null) { - adminComponent = admin; + if ((dpm.getKeyguardDisabledFeatures(admin, userId) & keyguardFeatures) != 0) { + if (enforcedAdmin == null) { + enforcedAdmin = new EnforcedAdmin(admin, userId); } else { - isDisabledByMultipleAdmins = true; - break; + return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; } } } - } - EnforcedAdmin enforcedAdmin = null; - if (adminComponent != null) { - if (!isDisabledByMultipleAdmins) { - enforcedAdmin = new EnforcedAdmin(adminComponent, UserHandle.myUserId()); - } else { - enforcedAdmin = new EnforcedAdmin(); + } else { + // Consider all admins for this user and the profiles that are visible from this + // user that do not use a separate work challenge. + for (UserInfo userInfo : um.getProfiles(userId)) { + final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userInfo.id); + if (admins == null) { + return null; + } + final boolean isSeparateProfileChallengeEnabled = + lockPatternUtils.isSeparateProfileChallengeEnabled(userInfo.id); + for (ComponentName admin : admins) { + if (!isSeparateProfileChallengeEnabled) { + if ((dpm.getKeyguardDisabledFeatures(admin, userInfo.id) + & keyguardFeatures) != 0) { + if (enforcedAdmin == null) { + enforcedAdmin = new EnforcedAdmin(admin, userInfo.id); + } else { + return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; + } + // This same admins could have set policies both on the managed profile + // and on the parent. So, if the admin has set the policy on the + // managed profile here, we don't need to further check if that admin + // has set policy on the parent admin. + continue; + } + } + if (userInfo.isManagedProfile()) { + // If userInfo.id is a managed profile, we also need to look at + // the policies set on the parent. + DevicePolicyManager parentDpm = dpm.getParentProfileInstance(admin); + if ((parentDpm.getKeyguardDisabledFeatures(admin, userInfo.id) + & keyguardFeatures) != 0) { + if (enforcedAdmin == null) { + enforcedAdmin = new EnforcedAdmin(admin, userInfo.id); + } else { + return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; + } + } + } + } } } return enforcedAdmin; @@ -170,6 +211,9 @@ public class RestrictedLockUtils { if (ipm.getBlockUninstallForUser(packageName, userId)) { DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( Context.DEVICE_POLICY_SERVICE); + if (dpm == null) { + return null; + } ComponentName admin = dpm.getProfileOwner(); if (admin == null) { admin = dpm.getDeviceOwnerComponentOnCallingUser(); @@ -197,6 +241,9 @@ public class RestrictedLockUtils { } DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( Context.DEVICE_POLICY_SERVICE); + if (dpm == null) { + return null; + } boolean isAccountTypeDisabled = false; String[] disabledTypes = dpm.getAccountTypesWithManagementDisabled(); for (String type : disabledTypes) { @@ -221,7 +268,7 @@ public class RestrictedLockUtils { public static EnforcedAdmin checkIfAutoTimeRequired(Context context) { DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( Context.DEVICE_POLICY_SERVICE); - if (dpm.getAutoTimeRequired()) { + if (dpm == null || !dpm.getAutoTimeRequired()) { return null; } ComponentName adminComponent = dpm.getDeviceOwnerComponentOnCallingUser(); @@ -240,6 +287,9 @@ public class RestrictedLockUtils { public static EnforcedAdmin checkIfPasswordQualityIsSet(Context context) { final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( Context.DEVICE_POLICY_SERVICE); + if (dpm == null) { + return null; + } boolean isDisabledByMultipleAdmins = false; ComponentName adminComponent = null; List<ComponentName> admins = dpm.getActiveAdmins(); @@ -268,9 +318,84 @@ public class RestrictedLockUtils { return enforcedAdmin; } + /** + * Checks if any admin has set maximum time to lock. + * + * @return EnforcedAdmin Object containing the enforced admin component and admin user details, + * or {@code null} if no admin has set this restriction. If multiple admins has set this, then + * the admin component will be set to {@code null} and userId to {@link UserHandle#USER_NULL} + */ + public static EnforcedAdmin checkIfMaximumTimeToLockIsSet(Context context) { + final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( + Context.DEVICE_POLICY_SERVICE); + LockPatternUtils lockPatternUtils = new LockPatternUtils(context); + EnforcedAdmin enforcedAdmin = null; + final int userId = UserHandle.myUserId(); + if (lockPatternUtils.isSeparateProfileChallengeEnabled(userId)) { + // If the user has a separate challenge, only consider the admins in that user. + final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userId); + if (admins == null) { + return null; + } + for (ComponentName admin : admins) { + if (dpm.getMaximumTimeToLock(admin, userId) > 0) { + if (enforcedAdmin == null) { + enforcedAdmin = new EnforcedAdmin(admin, userId); + } else { + return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; + } + } + } + } else { + // Return all admins for this user and the profiles that are visible from this + // user that do not use a separate work challenge. + final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); + for (UserInfo userInfo : um.getProfiles(userId)) { + final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userInfo.id); + if (admins == null) { + return null; + } + final boolean isSeparateProfileChallengeEnabled = + lockPatternUtils.isSeparateProfileChallengeEnabled(userInfo.id); + for (ComponentName admin : admins) { + if (!isSeparateProfileChallengeEnabled) { + if (dpm.getMaximumTimeToLock(admin, userInfo.id) > 0) { + if (enforcedAdmin == null) { + enforcedAdmin = new EnforcedAdmin(admin, userInfo.id); + } else { + return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; + } + // This same admins could have set policies both on the managed profile + // and on the parent. So, if the admin has set the policy on the + // managed profile here, we don't need to further check if that admin + // has set policy on the parent admin. + continue; + } + } + if (userInfo.isManagedProfile()) { + // If userInfo.id is a managed profile, we also need to look at + // the policies set on the parent. + DevicePolicyManager parentDpm = dpm.getParentProfileInstance(admin); + if (parentDpm.getMaximumTimeToLock(admin, userInfo.id) > 0) { + if (enforcedAdmin == null) { + enforcedAdmin = new EnforcedAdmin(admin, userInfo.id); + } else { + return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; + } + } + } + } + } + } + return enforcedAdmin; + } + public static EnforcedAdmin getProfileOrDeviceOwnerOnCallingUser(Context context) { final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( Context.DEVICE_POLICY_SERVICE); + if (dpm == null) { + return null; + } ComponentName adminComponent = dpm.getDeviceOwnerComponentOnCallingUser(); if (adminComponent != null) { return new EnforcedAdmin(adminComponent, UserHandle.myUserId()); @@ -391,6 +516,9 @@ public class RestrictedLockUtils { public ComponentName component = null; public int userId = UserHandle.USER_NULL; + // We use this to represent the case where a policy is enforced by multiple admins. + public final static EnforcedAdmin MULTIPLE_ENFORCED_ADMIN = new EnforcedAdmin(); + public EnforcedAdmin(ComponentName component, int userId) { this.component = component; this.userId = userId; diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java index 13a46d093549..810f6eb28a56 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java @@ -65,6 +65,10 @@ public class RestrictedPreference extends Preference { } } + public void useAdminDisabledSummary(boolean useSummary) { + mHelper.useAdminDisabledSummary(useSummary); + } + @Override protected void onAttachedToHierarchy(PreferenceManager preferenceManager) { mHelper.onAttachedToHierarchy(); diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java index 06aba968ea9e..9bd4eb185bee 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java @@ -27,6 +27,7 @@ import android.text.SpannableStringBuilder; import android.text.style.ImageSpan; import android.util.AttributeSet; import android.util.TypedValue; +import android.view.View; import android.widget.TextView; import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; @@ -44,6 +45,7 @@ public class RestrictedPreferenceHelper { private boolean mDisabledByAdmin; private EnforcedAdmin mEnforcedAdmin; private String mAttrUserRestriction = null; + private boolean mUseAdminDisabledSummary = false; public RestrictedPreferenceHelper(Context context, Preference preference, AttributeSet attrs) { @@ -68,6 +70,14 @@ public class RestrictedPreferenceHelper { } } mAttrUserRestriction = data == null ? null : data.toString(); + + final TypedValue useAdminDisabledSummary = + attributes.peekValue(R.styleable.RestrictedPreference_useAdminDisabledSummary); + if (useAdminDisabledSummary != null) { + mUseAdminDisabledSummary = + (useAdminDisabledSummary.type == TypedValue.TYPE_INT_BOOLEAN + && useAdminDisabledSummary.data != 0); + } } } @@ -82,6 +92,21 @@ public class RestrictedPreferenceHelper { holder.itemView.setEnabled(true); } } + if (mUseAdminDisabledSummary) { + final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary); + if (summaryView != null) { + if (mDisabledByAdmin) { + summaryView.setText(R.string.disabled_by_admin_summary_text); + summaryView.setVisibility(View.VISIBLE); + } else { + summaryView.setVisibility(View.GONE); + } + } + } + } + + public void useAdminDisabledSummary(boolean useSummary) { + mUseAdminDisabledSummary = useSummary; } /** diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java index 84e2bff4fc40..6cae8aa70847 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java @@ -65,6 +65,10 @@ public class RestrictedSwitchPreference extends SwitchPreference { } } + public void useAdminDisabledSummary(boolean useSummary) { + mHelper.useAdminDisabledSummary(useSummary); + } + @Override protected void onAttachedToHierarchy(PreferenceManager preferenceManager) { mHelper.onAttachedToHierarchy(); diff --git a/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java b/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java index 58a477efac09..18592076ba27 100644 --- a/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java +++ b/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java @@ -17,6 +17,7 @@ package com.android.settingslib; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.os.UserHandle; import android.text.TextUtils; import android.util.ArrayMap; @@ -38,14 +39,39 @@ public class SuggestionParser { private static final String TAG = "SuggestionParser"; + // If defined, only returns this suggestion if the feature is supported. + public static final String META_DATA_REQUIRE_FEATURE = "com.android.settings.require_feature"; + + /** + * Allows suggestions to appear after a certain number of days, and to re-appear if dismissed. + * For instance: + * 0,10 + * Will appear immediately, but if the user removes it, it will come back after 10 days. + * + * Another example: + * 10,30 + * Will only show up after 10 days, and then again after 30. + */ + public static final String META_DATA_DISMISS_CONTROL = "com.android.settings.dismiss"; + + // Shared prefs keys for storing dismissed state. + // Index into current dismissed state. + private static final String DISMISS_INDEX = "_dismiss_index"; + private static final String SETUP_TIME = "_setup_time"; + private static final String IS_DISMISSED = "_is_dismissed"; + + private static final long MILLIS_IN_DAY = 24 * 60 * 60 * 1000; + private final Context mContext; private final List<SuggestionCategory> mSuggestionList; private final ArrayMap<Pair<String, String>, Tile> addCache = new ArrayMap<>(); + private final SharedPreferences mSharedPrefs; - public SuggestionParser(Context context, int orderXml) { + public SuggestionParser(Context context, SharedPreferences sharedPrefs, int orderXml) { mContext = context; mSuggestionList = (List<SuggestionCategory>) new SuggestionOrderInflater(mContext) .parse(orderXml); + mSharedPrefs = sharedPrefs; } public List<Tile> getSuggestions() { @@ -57,6 +83,23 @@ public class SuggestionParser { return suggestions; } + /** + * Dismisses a suggestion, returns true if the suggestion has no more dismisses left and should + * be disabled. + */ + public boolean dismissSuggestion(Tile suggestion) { + String keyBase = suggestion.intent.getComponent().flattenToShortString(); + int index = mSharedPrefs.getInt(keyBase + DISMISS_INDEX, 0); + String dismissControl = suggestion.metaData.getString(META_DATA_DISMISS_CONTROL); + if (dismissControl == null || parseDismissString(dismissControl).length == index) { + return true; + } + mSharedPrefs.edit() + .putBoolean(keyBase + IS_DISMISSED, true) + .commit(); + return false; + } + private void readSuggestions(SuggestionCategory category, List<Tile> suggestions) { int countBefore = suggestions.size(); Intent intent = new Intent(Intent.ACTION_MAIN); @@ -66,6 +109,11 @@ public class SuggestionParser { } TileUtils.getTilesForIntent(mContext, new UserHandle(UserHandle.myUserId()), intent, addCache, null, suggestions, true, false); + for (int i = countBefore; i < suggestions.size(); i++) { + if (!isAvailable(suggestions.get(i)) || isDismissed(suggestions.get(i))) { + suggestions.remove(i--); + } + } if (!category.multiple && suggestions.size() > (countBefore + 1)) { // If there are too many, remove them all and only re-add the one with the highest // priority. @@ -80,6 +128,59 @@ public class SuggestionParser { } } + private boolean isAvailable(Tile suggestion) { + String featureRequired = suggestion.metaData.getString(META_DATA_REQUIRE_FEATURE); + if (featureRequired != null) { + return mContext.getPackageManager().hasSystemFeature(featureRequired); + } + return true; + } + + private boolean isDismissed(Tile suggestion) { + Object dismissObj = suggestion.metaData.get(META_DATA_DISMISS_CONTROL); + if (dismissObj == null) { + return false; + } + String dismissControl = String.valueOf(dismissObj); + String keyBase = suggestion.intent.getComponent().flattenToShortString(); + if (!mSharedPrefs.contains(keyBase + SETUP_TIME)) { + mSharedPrefs.edit() + .putLong(keyBase + SETUP_TIME, System.currentTimeMillis()) + .commit(); + } + // Default to dismissed, so that we can have suggestions that only first appear after + // some number of days. + if (!mSharedPrefs.getBoolean(keyBase + IS_DISMISSED, true)) { + return false; + } + int index = mSharedPrefs.getInt(keyBase + DISMISS_INDEX, 0); + int currentDismiss = parseDismissString(dismissControl)[index]; + long time = getEndTime(mSharedPrefs.getLong(keyBase + SETUP_TIME, 0), currentDismiss); + if (System.currentTimeMillis() >= time) { + // Dismiss timeout has passed, undismiss it. + mSharedPrefs.edit() + .putBoolean(keyBase + IS_DISMISSED, false) + .putInt(keyBase + DISMISS_INDEX, index + 1) + .commit(); + return false; + } + return true; + } + + private long getEndTime(long startTime, int daysDelay) { + long days = daysDelay * MILLIS_IN_DAY; + return startTime + days; + } + + private int[] parseDismissString(String dismissControl) { + String[] dismissStrs = dismissControl.split(","); + int[] dismisses = new int[dismissStrs.length]; + for (int i = 0; i < dismissStrs.length; i++) { + dismisses[i] = Integer.parseInt(dismissStrs[i]); + } + return dismisses; + } + private static class SuggestionCategory { public String category; public String pkg; diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java b/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java index 2fd043f64208..dc6002da98c7 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java @@ -61,6 +61,8 @@ public class ProfileSelectDialog extends DialogFragment implements OnClickListen @Override public void onClick(DialogInterface dialog, int which) { UserHandle user = mSelectedTile.userHandle.get(which); + // Show menu on top level items. + mSelectedTile.intent.putExtra(SettingsDrawerActivity.EXTRA_SHOW_MENU, true); getActivity().startActivityAsUser(mSelectedTile.intent, user); ((SettingsDrawerActivity) getActivity()).onProfileTileOpen(); } diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java index 3fc0c22a264e..56c4edb7f377 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java @@ -48,6 +48,8 @@ public class SettingsDrawerActivity extends Activity { protected static final boolean DEBUG_TIMING = false; private static final String TAG = "SettingsDrawerActivity"; + static final String EXTRA_SHOW_MENU = "show_drawer_menu"; + private static List<DashboardCategory> sDashboardCategories; private static HashMap<Pair<String, String>, Tile> sTileCache; @@ -56,6 +58,7 @@ public class SettingsDrawerActivity extends Activity { private SettingsDrawerAdapter mDrawerAdapter; private DrawerLayout mDrawerLayout; + private boolean mShowingMenu; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { @@ -94,7 +97,7 @@ public class SettingsDrawerActivity extends Activity { @Override public boolean onOptionsItemSelected(MenuItem item) { - if (mDrawerLayout != null && item.getItemId() == android.R.id.home + if (mShowingMenu && mDrawerLayout != null && item.getItemId() == android.R.id.home && mDrawerAdapter.getCount() != 0) { openDrawer(); return true; @@ -116,6 +119,9 @@ public class SettingsDrawerActivity extends Activity { new CategoriesUpdater().execute(); } + if (getIntent() != null && getIntent().getBooleanExtra(EXTRA_SHOW_MENU, false)) { + showMenuIcon(); + } } @Override @@ -171,13 +177,17 @@ public class SettingsDrawerActivity extends Activity { mDrawerAdapter.updateCategories(); if (mDrawerAdapter.getCount() != 0) { mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); - getActionBar().setHomeAsUpIndicator(R.drawable.ic_menu); - getActionBar().setDisplayHomeAsUpEnabled(true); } else { mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); } } + public void showMenuIcon() { + mShowingMenu = true; + getActionBar().setHomeAsUpIndicator(R.drawable.ic_menu); + getActionBar().setDisplayHomeAsUpEnabled(true); + } + public List<DashboardCategory> getDashboardCategories() { if (sDashboardCategories == null) { sTileCache = new HashMap<>(); @@ -204,8 +214,12 @@ public class SettingsDrawerActivity extends Activity { ProfileSelectDialog.show(getFragmentManager(), tile); return false; } else if (numUserHandles == 1) { + // Show menu on top level items. + tile.intent.putExtra(EXTRA_SHOW_MENU, true); startActivityAsUser(tile.intent, tile.userHandle.get(0)); } else { + // Show menu on top level items. + tile.intent.putExtra(EXTRA_SHOW_MENU, true); startActivity(tile.intent); } return true; diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java index 5d8668b64405..e53dd2f42c75 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java @@ -228,7 +228,7 @@ public class DataUsageController { private static String getActiveSubscriberId(Context context) { final TelephonyManager tele = TelephonyManager.from(context); final String actualSubscriberId = tele.getSubscriberId( - SubscriptionManager.getDefaultDataSubId()); + SubscriptionManager.getDefaultDataSubscriptionId()); return actualSubscriberId; } diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java index 52e1b56d16d7..5908d02b738d 100644 --- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java +++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java @@ -461,6 +461,7 @@ public class BugreportReceiverTest extends InstrumentationTestCase { */ private void sendBugreportStarted(int max) throws Exception { Intent intent = new Intent(INTENT_BUGREPORT_STARTED); + intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(EXTRA_PID, PID); intent.putExtra(EXTRA_NAME, NAME); intent.putExtra(EXTRA_MAX, max); @@ -518,6 +519,7 @@ public class BugreportReceiverTest extends InstrumentationTestCase { */ private void sendBugreportFinished(Integer pid, String bugreportPath, String screenshotPath) { Intent intent = new Intent(INTENT_BUGREPORT_FINISHED); + intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); if (pid != null) { intent.putExtra(EXTRA_PID, pid); } diff --git a/packages/SystemUI/res/drawable/brightness_mirror_background.xml b/packages/SystemUI/res/drawable/brightness_mirror_background.xml index fadfe635ba2c..e901e40fe23d 100644 --- a/packages/SystemUI/res/drawable/brightness_mirror_background.xml +++ b/packages/SystemUI/res/drawable/brightness_mirror_background.xml @@ -16,9 +16,4 @@ --> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="@color/system_primary_color" /> - <corners - android:topLeftRadius="@dimen/notification_material_rounded_rect_radius" - android:topRightRadius="@dimen/notification_material_rounded_rect_radius" - android:bottomLeftRadius="@dimen/notification_material_rounded_rect_radius" - android:bottomRightRadius="@dimen/notification_material_rounded_rect_radius"/> </shape> diff --git a/packages/SystemUI/res/drawable/notification_material_bg.xml b/packages/SystemUI/res/drawable/notification_material_bg.xml index 6a0277f0d6de..ae456631c4f1 100644 --- a/packages/SystemUI/res/drawable/notification_material_bg.xml +++ b/packages/SystemUI/res/drawable/notification_material_bg.xml @@ -20,7 +20,6 @@ <item> <shape> <solid android:color="@color/notification_material_background_color" /> - <corners android:radius="@dimen/notification_material_rounded_rect_radius" /> </shape> </item> </ripple>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/notification_material_bg_dim.xml b/packages/SystemUI/res/drawable/notification_material_bg_dim.xml index 65819428caf8..b6a8b70bb3e0 100644 --- a/packages/SystemUI/res/drawable/notification_material_bg_dim.xml +++ b/packages/SystemUI/res/drawable/notification_material_bg_dim.xml @@ -18,7 +18,6 @@ <item> <shape> <solid android:color="@color/notification_material_background_dimmed_color" /> - <corners android:radius="@dimen/notification_material_rounded_rect_radius" /> </shape> </item> </ripple> diff --git a/packages/SystemUI/res/drawable/qs_background_primary.xml b/packages/SystemUI/res/drawable/qs_background_primary.xml index 686df2c817e2..1bf7d4c95216 100644 --- a/packages/SystemUI/res/drawable/qs_background_primary.xml +++ b/packages/SystemUI/res/drawable/qs_background_primary.xml @@ -13,10 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. --> -<inset xmlns:android="http://schemas.android.com/apk/res/android" - android:insetTop="@dimen/notification_material_rounded_rect_radius_negative"> +<inset xmlns:android="http://schemas.android.com/apk/res/android"> <shape> <solid android:color="@color/system_primary_color"/> - <corners android:radius="@dimen/notification_material_rounded_rect_radius"/> </shape> </inset> diff --git a/packages/SystemUI/res/drawable/qs_background_secondary.xml b/packages/SystemUI/res/drawable/qs_background_secondary.xml index 3662e5a7e301..31c01629cf67 100644 --- a/packages/SystemUI/res/drawable/qs_background_secondary.xml +++ b/packages/SystemUI/res/drawable/qs_background_secondary.xml @@ -17,7 +17,5 @@ <solid android:color="@color/system_secondary_color" /> <corners android:topLeftRadius="0dp" - android:topRightRadius="0dp" - android:bottomLeftRadius="@dimen/notification_material_rounded_rect_radius" - android:bottomRightRadius="@dimen/notification_material_rounded_rect_radius"/> + android:topRightRadius="0dp" /> </shape> diff --git a/packages/SystemUI/res/drawable/volume_dialog_background.xml b/packages/SystemUI/res/drawable/volume_dialog_background.xml index f09c01b8cf71..e98bfb8d09f6 100644 --- a/packages/SystemUI/res/drawable/volume_dialog_background.xml +++ b/packages/SystemUI/res/drawable/volume_dialog_background.xml @@ -14,9 +14,5 @@ limitations under the License. --> <shape xmlns:android="http://schemas.android.com/apk/res/android" > - <solid android:color="@color/system_primary_color" /> - - <corners android:radius="@dimen/notification_material_rounded_rect_radius" /> - </shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/apps_bar.xml b/packages/SystemUI/res/layout/apps_bar.xml deleted file mode 100644 index e226805dc268..000000000000 --- a/packages/SystemUI/res/layout/apps_bar.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2016, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. ---> - -<!-- Container for the app shelf. --> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/app_shelf" - android:orientation="horizontal" - android:layout_height="match_parent" - android:layout_width="0dp" - android:layout_weight="1"> - <com.android.systemui.statusbar.phone.NavigationBarApps - android:id="@+id/navigation_bar_apps" - android:layout_width="wrap_content" - android:layout_height="match_parent" /> -</LinearLayout> diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml b/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml index 9ef743d9e15b..6e4b213b8698 100644 --- a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml +++ b/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml @@ -33,7 +33,8 @@ android:paddingTop="@dimen/car_lockscreen_disclaimer_title_padding_top" /> <TextView android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:layout_height="0dp" + android:layout_weight="1" android:text="@string/car_lockscreen_disclaimer_text" android:textSize="@dimen/car_lockscreen_disclaimer_text_size" android:paddingStart="@dimen/car_lockscreen_disclaimer_text_padding_start" @@ -44,7 +45,6 @@ android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:paddingTop="@dimen/car_lockscreen_user_grid_view_padding_top" android:stretchMode="columnWidth"> </com.android.systemui.statusbar.car.UserGridView> </LinearLayout> diff --git a/packages/SystemUI/res/layout/car_navigation_button.xml b/packages/SystemUI/res/layout/car_navigation_button.xml index 479f18d1741b..767764694fae 100644 --- a/packages/SystemUI/res/layout/car_navigation_button.xml +++ b/packages/SystemUI/res/layout/car_navigation_button.xml @@ -26,9 +26,10 @@ <com.android.keyguard.AlphaOptimizedImageButton android:id="@+id/car_nav_button_icon" android:layout_height="match_parent" - android:layout_width="wrap_content" + android:layout_width="@dimen/car_navigation_button_width" android:layout_centerInParent="true" - android:animateLayoutChanges="true"> + android:animateLayoutChanges="true" + android:scaleType="fitCenter"> </com.android.keyguard.AlphaOptimizedImageButton> <com.android.keyguard.AlphaOptimizedImageButton @@ -37,6 +38,7 @@ android:layout_width="wrap_content" android:layout_centerVertical="true" android:layout_toRightOf="@+id/car_nav_button_icon" - android:animateLayoutChanges="true"> + android:animateLayoutChanges="true" + android:scaleType="fitCenter"> </com.android.keyguard.AlphaOptimizedImageButton> </com.android.systemui.statusbar.car.CarNavigationButton> diff --git a/packages/SystemUI/res/layout/notification_children_divider.xml b/packages/SystemUI/res/layout/notification_children_divider.xml index 53273cf108ca..dad7cea3d989 100644 --- a/packages/SystemUI/res/layout/notification_children_divider.xml +++ b/packages/SystemUI/res/layout/notification_children_divider.xml @@ -19,5 +19,5 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/notification_more_divider" android:layout_width="match_parent" - android:layout_height="@dimen/notification_children_divider_height" + android:layout_height="@dimen/notification_divider_height" android:background="#61000000" /> diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml index f430fa516ffd..e56431b012e7 100644 --- a/packages/SystemUI/res/layout/qs_customize_panel.xml +++ b/packages/SystemUI/res/layout/qs_customize_panel.xml @@ -87,9 +87,7 @@ android:id="@+id/quick_settings_panel" android:background="#0000" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginLeft="@dimen/notification_side_padding" - android:layout_marginRight="@dimen/notification_side_padding" /> + android:layout_height="wrap_content" /> </com.android.systemui.tuner.AutoScrollView> diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml index 237768422e2a..6784695e5a99 100644 --- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml +++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml @@ -23,8 +23,6 @@ android:layout_width="@dimen/notification_panel_width" android:layout_height="@dimen/status_bar_header_height" android:layout_gravity="@integer/notification_panel_layout_gravity" - android:paddingStart="@dimen/notification_side_padding" - android:paddingEnd="@dimen/notification_side_padding" android:clipChildren="false" android:clipToPadding="false" android:baselineAligned="false" @@ -70,6 +68,18 @@ </com.android.systemui.statusbar.AlphaOptimizedFrameLayout> + <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch" + android:layout_width="48dp" + android:layout_height="48dp" + android:layout_alignParentEnd="true" + android:background="@drawable/ripple_drawable" > + <ImageView android:id="@+id/multi_user_avatar" + android:layout_width="@dimen/multi_user_avatar_expanded_size" + android:layout_height="@dimen/multi_user_avatar_expanded_size" + android:layout_gravity="center" + android:scaleType="centerInside"/> + </com.android.systemui.statusbar.phone.MultiUserSwitch> + <ImageView android:layout_width="48dp" android:layout_height="48dp" diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index 12cf1372d7ca..89abe2dc2c4c 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -55,20 +55,13 @@ layout="@layout/qs_panel" android:layout_marginTop="@dimen/status_bar_header_height_expanded" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginLeft="@dimen/notification_side_padding" - android:layout_marginRight="@dimen/notification_side_padding"/> + android:layout_height="wrap_content" /> <!-- A view to reserve space for the collapsed stack --> <!-- Layout height: notification_min_height + bottom_stack_peek_amount --> <View android:id="@+id/reserve_notification_space" android:layout_height="@dimen/min_stack_height" - android:layout_width="match_parent" - android:layout_marginTop="@dimen/notifications_top_padding" /> - - <View - android:layout_height="@dimen/notification_side_padding" android:layout_width="match_parent" /> </LinearLayout> </com.android.systemui.statusbar.phone.ObservableScrollView> diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml index 5eca47106a1d..dd75dbfd47dc 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml @@ -23,8 +23,6 @@ android:layout_width="@dimen/notification_panel_width" android:layout_height="@dimen/status_bar_header_height" android:layout_gravity="@integer/notification_panel_layout_gravity" - android:paddingStart="@dimen/notification_side_padding" - android:paddingEnd="@dimen/notification_side_padding" android:baselineAligned="false" android:elevation="4dp" android:background="@drawable/notification_header_bg" diff --git a/packages/SystemUI/res/layout/status_bar_notification_speed_bump.xml b/packages/SystemUI/res/layout/status_bar_notification_speed_bump.xml deleted file mode 100644 index e220a16d0ad2..000000000000 --- a/packages/SystemUI/res/layout/status_bar_notification_speed_bump.xml +++ /dev/null @@ -1,30 +0,0 @@ -<!-- - ~ Copyright (C) 2014 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License - --> - -<!-- Extends FrameLayout --> -<com.android.systemui.statusbar.SpeedBumpView - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="@dimen/speed_bump_height" - android:visibility="gone" - > - <com.android.systemui.statusbar.AlphaOptimizedView - android:id="@+id/speedbump_line" - android:layout_width="match_parent" - android:layout_height="1dp" - android:background="#6fdddddd" - android:layout_gravity="center_vertical"/> -</com.android.systemui.statusbar.SpeedBumpView> diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml index 4c80b4843231..3dca77d65f41 100644 --- a/packages/SystemUI/res/layout/super_status_bar.xml +++ b/packages/SystemUI/res/layout/super_status_bar.xml @@ -65,8 +65,6 @@ android:layout_width="@dimen/notification_panel_width" android:layout_height="wrap_content" android:layout_gravity="@integer/notification_panel_layout_gravity" - android:paddingLeft="@dimen/notification_side_padding" - android:paddingRight="@dimen/notification_side_padding" android:visibility="invisible"> <FrameLayout android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml index 7cbc55cc5997..34796cd8d53c 100644 --- a/packages/SystemUI/res/layout/volume_dialog.xml +++ b/packages/SystemUI/res/layout/volume_dialog.xml @@ -19,8 +19,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="@dimen/volume_dialog_margin_bottom" - android:layout_marginLeft="@dimen/notification_side_padding" - android:layout_marginRight="@dimen/notification_side_padding" android:background="@drawable/volume_dialog_background" android:translationZ="4dp" > diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index b2190ec10998..5e25d2c9fecb 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -78,14 +78,13 @@ <color name="notification_material_background_color">#ffffffff</color> <!-- The color of the material notification background when dimmed --> - <color name="notification_material_background_dimmed_color">#f2ffffff</color> + <color name="notification_material_background_dimmed_color">#ccffffff</color> <!-- The color of the material notification background when low priority --> <color name="notification_material_background_low_priority_color">#fff5f5f5</color> - <!-- The color of the material notification background for media notifications when no custom - color is specified --> - <color name="notification_material_background_media_default_color">#ff424242</color> + <!-- The background color of the notification shade --> + <color name="notification_shade_background_color">#ffeeeeee</color> <!-- The color of the ripples on the untinted notifications --> <color name="notification_ripple_untinted_color">#28000000</color> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index aedc2c52e05b..6df31ffceba9 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -181,7 +181,10 @@ <integer name="recents_animate_task_stack_scroll_duration">200</integer> <!-- The animation duration for scrolling the stack to a particular item. --> - <integer name="recents_auto_advance_duration">2000</integer> + <integer name="recents_auto_advance_duration">750</integer> + + <!-- The animation duration for subsequent scrolling the stack to a particular item. --> + <integer name="recents_subsequent_auto_advance_duration">1000</integer> <!-- The animation duration for entering and exiting the history. --> <integer name="recents_history_transition_duration">250</integer> @@ -195,6 +198,10 @@ <!-- Svelte specific logic, see RecentsConfiguration.SVELTE_* constants. --> <integer name="recents_svelte_level">0</integer> + <!-- In multi-window, determines whether the stack where recents lives should grow from + the smallest position when being launched. --> + <bool name="recents_grow_in_multiwindow">true</bool> + <!-- Recents: The relative range of visible tasks from the current scroll position while the stack is focused. --> <item name="recents_layout_focused_range_min" format="float" type="integer">-3</item> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 46a0f2ae2ae7..e245c2422b86 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -87,9 +87,6 @@ <!-- gap on either side of status bar notification icons --> <dimen name="status_bar_icon_padding">0dp</dimen> - <!-- half the distance between notifications in the panel --> - <dimen name="notification_divider_height">2dp</dimen> - <!-- The padding on the global screenshot background image --> <dimen name="global_screenshot_bg_padding">20dp</dimen> @@ -137,7 +134,7 @@ <!-- Width for the notification panel and related windows --> <dimen name="match_parent">-1px</dimen> - <dimen name="standard_notification_panel_width">416dp</dimen><!-- includes notification_side_padding on each side --> + <dimen name="standard_notification_panel_width">416dp</dimen> <dimen name="notification_panel_width">@dimen/match_parent</dimen> <!-- Gravity for the notification panel --> @@ -298,17 +295,14 @@ <!-- The height of the area before the top stack in which the notifications slow down --> <dimen name="top_stack_slow_down_length">12dp</dimen> - <!-- The side padding of the notifications--> - <dimen name="notification_side_padding">8dp</dimen> - <!-- Z distance between notifications if they are in the stack --> - <dimen name="z_distance_between_notifications">1dp</dimen> + <dimen name="z_distance_between_notifications">0.5dp</dimen> - <!-- The padding between the individual notification cards when dimmed. --> - <dimen name="notification_padding_dimmed">0dp</dimen> + <!-- The height of the divider between the individual notifications. --> + <dimen name="notification_divider_height">0.5dp</dimen> - <!-- The padding between the individual notification cards. --> - <dimen name="notification_padding">2dp</dimen> + <!-- The height of the divider between the individual notifications when the notification wants it to be increased. This is currently the case for notification groups --> + <dimen name="notification_divider_height_increased">6dp</dimen> <!-- The minimum amount of top overscroll to go to the quick settings. --> <dimen name="min_top_overscroll_to_qs">36dp</dimen> @@ -328,8 +322,6 @@ <!-- Falsing threshold used when dismissing notifications from the lockscreen. --> <dimen name="swipe_helper_falsing_threshold">70dp</dimen> - <dimen name="notifications_top_padding">4dp</dimen> - <!-- Minimum distance the user has to drag down to go to the full shade. --> <dimen name="keyguard_drag_down_min_distance">100dp</dimen> @@ -374,21 +366,12 @@ phone hints. --> <dimen name="edge_tap_area_width">48dp</dimen> - <!-- radius of the corners of the material rounded rect background --> - <dimen name="notification_material_rounded_rect_radius">2dp</dimen> - - <!-- radius of the corners of the material rounded rect background but negative--> - <dimen name="notification_material_rounded_rect_radius_negative">-2dp</dimen> - <!-- The padding between notification children when collapsed --> <dimen name="notification_children_padding">4dp</dimen> <!-- The padding on top of the first notification to the children container --> <dimen name="notification_children_container_top_padding">8dp</dimen> - <!-- The height of the divider between the notfication children --> - <dimen name="notification_children_divider_height">1dp</dimen> - <!-- The vertical distance from which the notification appear when children are expanded --> <dimen name="notification_appear_distance">140dp</dimen> diff --git a/packages/SystemUI/res/values/dimens_car.xml b/packages/SystemUI/res/values/dimens_car.xml index ecdccee003ee..04402b7a4a62 100644 --- a/packages/SystemUI/res/values/dimens_car.xml +++ b/packages/SystemUI/res/values/dimens_car.xml @@ -18,15 +18,15 @@ <resources> <dimen name="car_lockscreen_disclaimer_title_size">48sp</dimen> <dimen name="car_lockscreen_disclaimer_title_padding_start">96dp</dimen> - <dimen name="car_lockscreen_disclaimer_title_padding_top">96dp</dimen> + <dimen name="car_lockscreen_disclaimer_title_padding_top">82dp</dimen> <dimen name="car_lockscreen_disclaimer_text_size">28sp</dimen> <dimen name="car_lockscreen_disclaimer_text_padding_start">96dp</dimen> <dimen name="car_lockscreen_disclaimer_text_padding_end">96dp</dimen> - <dimen name="car_lockscreen_disclaimer_text_padding_top">32dp</dimen> + <dimen name="car_lockscreen_disclaimer_text_padding_top">8dp</dimen> <dimen name="car_lockscreen_user_grid_view_padding_start">10dp</dimen> <dimen name="car_lockscreen_user_grid_view_padding_end">10dp</dimen> - <dimen name="car_lockscreen_user_grid_view_padding_top">128dp</dimen> <dimen name="car_fullscreen_user_pod_image_avatar_width">128dp</dimen> <dimen name="car_fullscreen_user_pod_image_avatar_height">128dp</dimen> <dimen name="car_fullscreen_user_pod_text_size">24sp</dimen> + <dimen name="car_navigation_button_width">64dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 0bd350b3c786..87aedab6eec5 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -18,22 +18,22 @@ <resources> <item type="id" name="translation_y_animator_tag"/> <item type="id" name="translation_z_animator_tag"/> - <item type="id" name="scale_animator_tag"/> <item type="id" name="alpha_animator_tag"/> <item type="id" name="top_inset_animator_tag"/> <item type="id" name="height_animator_tag"/> + <item type="id" name="shadow_alpha_animator_tag"/> <item type="id" name="translation_y_animator_end_value_tag"/> <item type="id" name="translation_z_animator_end_value_tag"/> - <item type="id" name="scale_animator_end_value_tag"/> <item type="id" name="alpha_animator_end_value_tag"/> <item type="id" name="top_inset_animator_end_value_tag"/> <item type="id" name="height_animator_end_value_tag"/> + <item type="id" name="shadow_alpha_animator_end_value_tag"/> <item type="id" name="translation_y_animator_start_value_tag"/> <item type="id" name="translation_z_animator_start_value_tag"/> - <item type="id" name="scale_animator_start_value_tag"/> <item type="id" name="alpha_animator_start_value_tag"/> <item type="id" name="top_inset_animator_start_value_tag"/> <item type="id" name="height_animator_start_value_tag"/> + <item type="id" name="shadow_alpha_animator_start_value_tag"/> <item type="id" name="doze_saved_filter_tag"/> <item type="id" name="qs_icon_tag"/> <item type="id" name="scrim"/> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index faf36eca176f..02158ac8a732 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1179,11 +1179,6 @@ <!-- Description for the toggle for fast-toggling recents via the recents button. DO NOT TRANSLATE --> <string name="overview_fast_toggle_via_button_desc">Enable launch timeout while paging</string> - <!-- Toggles the fast-toggling indicator. DO NOT TRANSLATE --> - <string name="overview_fast_toggle_indicator">Enable fast toggle indicator</string> - <!-- Description for the fast-toggling indicator. DO NOT TRANSLATE --> - <string name="overview_fast_toggle_indicator_desc">Show an indicator for the launch timeout</string> - <!-- Toggle to set the initial scroll state to be paging or stack. DO NOT TRANSLATE --> <string name="overview_initial_state_paging">Initialize to paging</string> <!-- Description for the toggle to set the initial scroll state to be paging or stack. DO NOT TRANSLATE --> diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml index 919375418964..67a7a2391fe7 100644 --- a/packages/SystemUI/res/xml/tuner_prefs.xml +++ b/packages/SystemUI/res/xml/tuner_prefs.xml @@ -122,12 +122,6 @@ android:title="@string/overview_fast_toggle_via_button" android:summary="@string/overview_fast_toggle_via_button_desc" /> - <com.android.systemui.tuner.TunerSwitch - android:key="overview_fast_toggle_indicator" - android:title="@string/overview_fast_toggle_indicator" - android:summary="@string/overview_fast_toggle_indicator_desc" - android:dependency="overview_fast_toggle_via_button" /> - </PreferenceScreen> <SwitchPreference diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java index 99028a6c403f..001d1f23af62 100644 --- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java +++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java @@ -32,7 +32,7 @@ public interface RecentsComponent { /** * Docks the top-most task and opens recents. */ - boolean dockTopTask(boolean draggingInRecents, int stackCreateMode, Rect initialBounds); + boolean dockTopTask(int dragMode, int stackCreateMode, Rect initialBounds); /** * Called during a drag-from-navbar-in gesture. diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index fd4161fe3e48..5b955a4746d6 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -30,11 +30,9 @@ import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.accessibility.AccessibilityEvent; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; -import android.view.animation.LinearInterpolator; import com.android.systemui.classifier.FalsingManager; +import com.android.systemui.statusbar.Interpolators; public class SwipeHelper implements Gefingerpoken { static final String TAG = "com.android.systemui.SwipeHelper"; @@ -48,9 +46,6 @@ public class SwipeHelper implements Gefingerpoken { public static final int X = 0; public static final int Y = 1; - private static LinearInterpolator sLinearInterpolator = new LinearInterpolator(); - private final Interpolator mFastOutLinearInInterpolator; - private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec private int DEFAULT_ESCAPE_ANIMATION_DURATION = 200; // ms private int MAX_ESCAPE_ANIMATION_DURATION = 400; // ms @@ -97,8 +92,6 @@ public class SwipeHelper implements Gefingerpoken { mPagingTouchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop(); mLongPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5f); // extra long-press! - mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context, - android.R.interpolator.fast_out_linear_in); mFalsingThreshold = context.getResources().getDimensionPixelSize( R.dimen.swipe_helper_falsing_threshold); mFalsingManager = FalsingManager.getInstance(context); @@ -357,9 +350,9 @@ public class SwipeHelper implements Gefingerpoken { } ObjectAnimator anim = createTranslationAnimation(animView, newPos); if (useAccelerateInterpolator) { - anim.setInterpolator(mFastOutLinearInInterpolator); + anim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); } else { - anim.setInterpolator(sLinearInterpolator); + anim.setInterpolator(Interpolators.LINEAR); } anim.setDuration(duration); if (delay > 0) { diff --git a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java index c9ba8858bf37..25f8bb069b02 100644 --- a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java +++ b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java @@ -24,8 +24,8 @@ import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; import android.view.View; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; + +import com.android.systemui.statusbar.Interpolators; import java.util.ArrayList; @@ -35,7 +35,6 @@ import java.util.ArrayList; public class ViewInvertHelper { private final Paint mDarkPaint = new Paint(); - private final Interpolator mLinearOutSlowInInterpolator; private final ColorMatrix mMatrix = new ColorMatrix(); private final ColorMatrix mGrayscaleMatrix = new ColorMatrix(); private final long mFadeDuration; @@ -46,8 +45,6 @@ public class ViewInvertHelper { addTarget(v); } public ViewInvertHelper(Context context, long fadeDuration) { - mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, - android.R.interpolator.linear_out_slow_in); mFadeDuration = fadeDuration; } @@ -89,7 +86,7 @@ public class ViewInvertHelper { } }); animator.setDuration(mFadeDuration); - animator.setInterpolator(mLinearOutSlowInInterpolator); + animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); animator.setStartDelay(delay); animator.start(); } diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java index c3a8f2e73d78..92cd0279d4f6 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java @@ -16,8 +16,6 @@ package com.android.systemui.assist; -import com.android.systemui.R; - import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -35,6 +33,9 @@ import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.animation.AnimationUtils; +import com.android.systemui.R; +import com.android.systemui.statusbar.Interpolators; + /** * Visually discloses that contextual data was provided to an assistant. */ @@ -120,13 +121,11 @@ public class AssistDisclosure { R.interpolator.assist_disclosure_trace)); mAlphaInAnimator = ValueAnimator.ofInt(0, 255).setDuration(ALPHA_IN_ANIMATION_DURATION); mAlphaInAnimator.addUpdateListener(this); - mAlphaInAnimator.setInterpolator(AnimationUtils.loadInterpolator(mContext, - android.R.interpolator.fast_out_slow_in)); + mAlphaInAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); mAlphaOutAnimator = ValueAnimator.ofInt(255, 0).setDuration( ALPHA_OUT_ANIMATION_DURATION); mAlphaOutAnimator.addUpdateListener(this); - mAlphaOutAnimator.setInterpolator(AnimationUtils.loadInterpolator(mContext, - android.R.interpolator.fast_out_linear_in)); + mAlphaOutAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); mAnimator = new AnimatorSet(); mAnimator.play(mAlphaInAnimator).with(mTracingAnimator); mAnimator.play(mAlphaInAnimator).before(mAlphaOutAnimator); diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbContainer.java b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbContainer.java index 67017db823fc..34770c4bf2bb 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbContainer.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbContainer.java @@ -20,19 +20,15 @@ import android.annotation.Nullable; import android.content.Context; import android.util.AttributeSet; import android.view.View; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; import android.widget.FrameLayout; import com.android.systemui.R; +import com.android.systemui.statusbar.Interpolators; public class AssistOrbContainer extends FrameLayout { private static final long EXIT_START_DELAY = 150; - private final Interpolator mLinearOutSlowInInterpolator; - private final Interpolator mFastOutLinearInInterpolator; - private View mScrim; private View mNavbarScrim; private AssistOrbView mOrb; @@ -49,10 +45,6 @@ public class AssistOrbContainer extends FrameLayout { public AssistOrbContainer(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, - android.R.interpolator.linear_out_slow_in); - mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context, - android.R.interpolator.fast_out_slow_in); } @Override @@ -109,12 +101,12 @@ public class AssistOrbContainer extends FrameLayout { .alpha(1f) .setDuration(300) .setStartDelay(0) - .setInterpolator(mLinearOutSlowInInterpolator); + .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); mNavbarScrim.animate() .alpha(1f) .setDuration(300) .setStartDelay(0) - .setInterpolator(mLinearOutSlowInInterpolator); + .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); } }); } @@ -132,12 +124,12 @@ public class AssistOrbContainer extends FrameLayout { .alpha(0f) .setDuration(250) .setStartDelay(EXIT_START_DELAY) - .setInterpolator(mFastOutLinearInInterpolator); + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN); mNavbarScrim.animate() .alpha(0f) .setDuration(250) .setStartDelay(EXIT_START_DELAY) - .setInterpolator(mFastOutLinearInInterpolator) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .withEndAction(endRunnable); } diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbView.java b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbView.java index a3372a818e2f..2d933f633286 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbView.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbView.java @@ -27,13 +27,13 @@ import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; import android.view.ViewOutlineProvider; -import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.view.animation.OvershootInterpolator; import android.widget.FrameLayout; import android.widget.ImageView; import com.android.systemui.R; +import com.android.systemui.statusbar.Interpolators; public class AssistOrbView extends FrameLayout { @@ -43,8 +43,6 @@ public class AssistOrbView extends FrameLayout { private final Paint mBackgroundPaint = new Paint(); private final Rect mCircleRect = new Rect(); private final Rect mStaticRect = new Rect(); - private final Interpolator mAppearInterpolator; - private final Interpolator mDisappearInterpolator; private final Interpolator mOvershootInterpolator = new OvershootInterpolator(); private boolean mClipToOutline; @@ -117,10 +115,6 @@ public class AssistOrbView extends FrameLayout { R.dimen.assist_orb_travel_distance); mMaxElevation = context.getResources().getDimensionPixelSize( R.dimen.assist_orb_elevation); - mAppearInterpolator = AnimationUtils.loadInterpolator(mContext, - android.R.interpolator.linear_out_slow_in); - mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext, - android.R.interpolator.fast_out_linear_in); mBackgroundPaint.setAntiAlias(true); mBackgroundPaint.setColor(getResources().getColor(R.color.assist_orb_color)); } @@ -256,8 +250,8 @@ public class AssistOrbView extends FrameLayout { } public void startExitAnimation(long delay) { - animateCircleSize(0, 200, delay, mDisappearInterpolator); - animateOffset(0, 200, delay, mDisappearInterpolator); + animateCircleSize(0, 200, delay, Interpolators.FAST_OUT_LINEAR_IN); + animateOffset(0, 200, delay, Interpolators.FAST_OUT_LINEAR_IN); } public void startEnterAnimation() { @@ -266,7 +260,7 @@ public class AssistOrbView extends FrameLayout { @Override public void run() { animateCircleSize(mCircleMinSize, 300, 0 /* delay */, mOvershootInterpolator); - animateOffset(mStaticOffset, 400, 0 /* delay */, mAppearInterpolator); + animateOffset(mStaticOffset, 400, 0 /* delay */, Interpolators.LINEAR_OUT_SLOW_IN); } }); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java index 543a2f32b090..8ae2d7bc50e5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java @@ -73,7 +73,7 @@ public class TileAdapter extends BaseAdapter { mCurrentTiles = tileSpecs; final TileGroup group = new TileGroup("com.android.settings", mContext); String possible = mContext.getString(R.string.quick_settings_tiles_default) - + ",user,hotspot,inversion,saver"; + + ",hotspot,inversion,saver"; String[] possibleTiles = possible.split(","); for (int i = 0; i < possibleTiles.length; i++) { final String spec = possibleTiles[i]; diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl index b36b95a5442b..37085c77055b 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl +++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl @@ -16,6 +16,8 @@ package com.android.systemui.recents; +import android.graphics.Rect; + /** * Due to the fact that RecentsActivity is per-user, we need to establish an * interface (this) for the system user to callback to the secondary users in @@ -29,6 +31,8 @@ oneway interface IRecentsNonSystemUserCallbacks { void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey); void toggleRecents(); void onConfigurationChanged(); + void dockTopTask(int topTaskId, int dragMode, int stackCreateMode, + in Rect initialBounds); void onDraggingInRecents(float distanceFromTop); void onDraggingInRecentsEnded(float velocity); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl index 6b491953a45a..cb8f0e7e91de 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl +++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl @@ -26,4 +26,7 @@ oneway interface IRecentsSystemUserCallbacks { void updateRecentsVisibility(boolean visible); void startScreenPinning(); + void sendRecentsDrawnEvent(); + void sendDockingTopTaskEvent(int dragMode); + void sendLaunchRecentsEvent(); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java index 2baefd57b0cc..b8310f20353a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java @@ -16,6 +16,7 @@ package com.android.systemui.recents; +import android.app.ActivityManager; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; @@ -36,8 +37,11 @@ import android.view.View; import com.android.systemui.RecentsComponent; import com.android.systemui.SystemUI; import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.activity.DockingTopTaskEvent; +import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent; import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent; import com.android.systemui.recents.events.component.ScreenPinningRequestEvent; +import com.android.systemui.recents.events.ui.RecentsDrawnEvent; import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.recents.model.RecentsTaskLoader; @@ -366,17 +370,38 @@ public class Recents extends SystemUI } @Override - public boolean dockTopTask(boolean draggingInRecents, int stackCreateMode, Rect initialBounds) { + public boolean dockTopTask(int dragMode, int stackCreateMode, Rect initialBounds) { // Ensure the device has been provisioned before allowing the user to interact with // recents if (!isUserSetup()) { return false; } - if (mImpl.dockTopTask(draggingInRecents, stackCreateMode,initialBounds)) { - if (draggingInRecents) { - mDraggingInRecentsCurrentUser = sSystemServicesProxy.getCurrentUser(); + int currentUser = sSystemServicesProxy.getCurrentUser(); + SystemServicesProxy ssp = Recents.getSystemServices(); + ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask(); + boolean screenPinningActive = ssp.isScreenPinningActive(); + boolean isTopTaskHome = topTask != null && SystemServicesProxy.isHomeStack(topTask.stackId); + if (topTask != null && !isTopTaskHome && !screenPinningActive) { + if (sSystemServicesProxy.isSystemUser(currentUser)) { + mImpl.dockTopTask(topTask.id, dragMode, stackCreateMode, initialBounds); + } else { + if (mSystemUserCallbacks != null) { + IRecentsNonSystemUserCallbacks callbacks = + mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser); + if (callbacks != null) { + try { + callbacks.dockTopTask(topTask.id, dragMode, stackCreateMode, + initialBounds); + } catch (RemoteException e) { + Log.e(TAG, "Callback failed", e); + } + } else { + Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); + } + } } + mDraggingInRecentsCurrentUser = currentUser; return true; } return false; @@ -516,6 +541,54 @@ public class Recents extends SystemUI } } + public final void onBusEvent(final RecentsDrawnEvent event) { + int processUser = sSystemServicesProxy.getProcessUser(); + if (!sSystemServicesProxy.isSystemUser(processUser)) { + postToSystemUser(new Runnable() { + @Override + public void run() { + try { + mCallbacksToSystemUser.sendRecentsDrawnEvent(); + } catch (RemoteException e) { + Log.e(TAG, "Callback failed", e); + } + } + }); + } + } + + public final void onBusEvent(final DockingTopTaskEvent event) { + int processUser = sSystemServicesProxy.getProcessUser(); + if (!sSystemServicesProxy.isSystemUser(processUser)) { + postToSystemUser(new Runnable() { + @Override + public void run() { + try { + mCallbacksToSystemUser.sendDockingTopTaskEvent(event.dragMode); + } catch (RemoteException e) { + Log.e(TAG, "Callback failed", e); + } + } + }); + } + } + + public final void onBusEvent(final RecentsActivityStartingEvent event) { + int processUser = sSystemServicesProxy.getProcessUser(); + if (!sSystemServicesProxy.isSystemUser(processUser)) { + postToSystemUser(new Runnable() { + @Override + public void run() { + try { + mCallbacksToSystemUser.sendLaunchRecentsEvent(); + } catch (RemoteException e) { + Log.e(TAG, "Callback failed", e); + } + } + }); + } + } + /** * Attempts to register with the system user. */ @@ -525,7 +598,8 @@ public class Recents extends SystemUI @Override public void run() { try { - mCallbacksToSystemUser.registerNonSystemUserCallbacks(mImpl, processUser); + mCallbacksToSystemUser.registerNonSystemUserCallbacks( + new RecentsImplProxy(mImpl), processUser); } catch (RemoteException e) { Log.e(TAG, "Failed to register", e); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 3a3b19dc3441..189e8d3568da 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -44,6 +44,7 @@ import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEven import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent; import com.android.systemui.recents.events.activity.DebugFlagsChangedEvent; import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted; +import com.android.systemui.recents.events.activity.EnterRecentsTaskStackAnimationCompletedEvent; import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent; import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent; import com.android.systemui.recents.events.activity.ExitRecentsWindowFirstAnimationFrameEvent; @@ -59,6 +60,7 @@ import com.android.systemui.recents.events.component.RecentsVisibilityChangedEve import com.android.systemui.recents.events.component.ScreenPinningRequestEvent; import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent; import com.android.systemui.recents.events.ui.DeleteTaskDataEvent; +import com.android.systemui.recents.events.ui.RecentsDrawnEvent; import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent; import com.android.systemui.recents.events.ui.StackViewScrolledEvent; import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent; @@ -115,6 +117,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD // The trigger to automatically launch the current task private int mFocusTimerDuration; private DozeTrigger mIterateTrigger; + private final UserInteractionEvent mUserInteractionEvent = new UserInteractionEvent(); /** * A common Runnable to finish Recents by launching Home with an animation depending on the @@ -400,6 +403,17 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, true)); MetricsLogger.visible(this, MetricsLogger.OVERVIEW_ACTIVITY); + + mRecentsView.getViewTreeObserver().addOnPreDrawListener( + new ViewTreeObserver.OnPreDrawListener() { + + @Override + public boolean onPreDraw() { + mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this); + EventBus.getDefault().post(new RecentsDrawnEvent()); + return true; + } + }); } @Override @@ -537,7 +551,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD EventBus.getDefault().send(new FocusPreviousTaskViewEvent()); } else { EventBus.getDefault().send( - new FocusNextTaskViewEvent(false /* showTimerIndicator */)); + new FocusNextTaskViewEvent(0 /* timerIndicatorDuration */)); } mLastTabKeyEventTime = SystemClock.elapsedRealtime(); @@ -550,7 +564,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD } case KeyEvent.KEYCODE_DPAD_UP: { EventBus.getDefault().send( - new FocusNextTaskViewEvent(false /* showTimerIndicator */)); + new FocusNextTaskViewEvent(0 /* timerIndicatorDuration */)); return true; } case KeyEvent.KEYCODE_DPAD_DOWN: { @@ -576,10 +590,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD @Override public void onUserInteraction() { - // TODO: Prevent creating so many events here - final RecentsDebugFlags debugFlags = Recents.getDebugFlags(); - EventBus.getDefault().send(new UserInteractionEvent(debugFlags.isFastToggleRecentsEnabled() - && debugFlags.isFastToggleIndicatorEnabled())); + EventBus.getDefault().send(mUserInteractionEvent); } @Override @@ -605,13 +616,13 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD if (!dismissHistory()) { final RecentsDebugFlags debugFlags = Recents.getDebugFlags(); - // Focus the next task - EventBus.getDefault().send( - new FocusNextTaskViewEvent(debugFlags.isFastToggleRecentsEnabled() - && debugFlags.isFastToggleIndicatorEnabled())); - // Start dozing after the recents button is clicked + int timerIndicatorDuration = 0; if (debugFlags.isFastToggleRecentsEnabled()) { + timerIndicatorDuration = getResources().getInteger( + R.integer.recents_subsequent_auto_advance_duration); + + mIterateTrigger.setDozeDuration(timerIndicatorDuration); if (!mIterateTrigger.isDozing()) { mIterateTrigger.startDozing(); } else { @@ -619,6 +630,9 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD } } + // Focus the next task + EventBus.getDefault().send(new FocusNextTaskViewEvent(timerIndicatorDuration)); + MetricsLogger.action(this, MetricsLogger.ACTION_OVERVIEW_PAGE); } } @@ -648,6 +662,9 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD } else { dismissRecentsToHome(true /* animateTaskViews */); } + + // Cancel any pending dozes + EventBus.getDefault().send(mUserInteractionEvent); } else { // Do nothing } @@ -670,6 +687,21 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD } } + public final void onBusEvent(EnterRecentsTaskStackAnimationCompletedEvent event) { + RecentsDebugFlags debugFlags = Recents.getDebugFlags(); + RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); + if (!launchState.launchedWithAltTab && debugFlags.isFastToggleRecentsEnabled() && + RecentsDebugFlags.Static.EnableFastToggleTimeoutOnEnter) { + mIterateTrigger.setDozeDuration( + getResources().getInteger(R.integer.recents_auto_advance_duration)); + if (!mIterateTrigger.isDozing()) { + mIterateTrigger.startDozing(); + } else { + mIterateTrigger.poke(); + } + } + } + public final void onBusEvent(EnterRecentsWindowLastAnimationFrameEvent event) { EventBus.getDefault().send(new UpdateFreeformTaskViewVisibilityEvent(true)); mRecentsView.getViewTreeObserver().addOnPreDrawListener(this); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java index 4ab0740cf4db..0afa1f6a495f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java @@ -52,27 +52,12 @@ public class RecentsActivityLaunchState { * Returns the task to focus given the current launch state. */ public int getInitialFocusTaskIndex(int numTasks) { - RecentsDebugFlags flags = Recents.getDebugFlags(); - if (launchedWithAltTab) { - if (launchedFromAppWithThumbnail) { - // If alt-tabbing from another app, focus the next task - return numTasks - 2; - } else { - // If alt-tabbing from home, focus the first task - return numTasks - 1; - } + if (launchedFromAppWithThumbnail) { + // If coming from another app, focus the next task + return numTasks - 2; } else { - if (launchedFromHome) { - return numTasks - 1; - } else { - if (flags.isFastToggleRecentsEnabled() || !flags.isInitialStatePaging()) { - // If we are not fast-toggling or are starting in the non-focused mode, then - // we should assume the front most task has focus - return numTasks - 1; - } else { - return numTasks - 2; - } - } + // If coming from home, focus the first task + return numTasks - 1; } } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java index 5cd540bcb789..49f2ab043926 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java @@ -19,6 +19,7 @@ package com.android.systemui.recents; import android.content.Context; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.DebugFlagsChangedEvent; +import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.tuner.TunerService; /** @@ -35,6 +36,10 @@ public class RecentsDebugFlags implements TunerService.Tunable { public static final boolean DisableBackgroundCache = false; // Enables the task affiliations public static final boolean EnableAffiliatedTaskGroups = true; + // Overrides the Tuner flags and enables the fast toggle and timeout + public static final boolean EnableFastToggleTimeoutOverride = true; + // Enables toggling the fast-toggle timeout immediately after entering Recents + public static final boolean EnableFastToggleTimeoutOnEnter = true; // Enables us to create mock recents tasks public static final boolean EnableMockTasks = false; @@ -49,11 +54,9 @@ public class RecentsDebugFlags implements TunerService.Tunable { } private static final String KEY_FAST_TOGGLE = "overview_fast_toggle_via_button"; - private static final String KEY_FAST_TOGGLE_INDICATOR = "overview_fast_toggle_indicator"; private static final String KEY_INITIAL_STATE_PAGING = "overview_initial_state_paging"; private boolean mFastToggleRecents; - private boolean mFastToggleIndicator; private boolean mInitialStatePaging; /** @@ -63,28 +66,32 @@ public class RecentsDebugFlags implements TunerService.Tunable { public RecentsDebugFlags(Context context) { // Register all our flags, this will also call onTuningChanged() for each key, which will // initialize the current state of each flag - TunerService.get(context).addTunable(this, KEY_FAST_TOGGLE, KEY_FAST_TOGGLE_INDICATOR, - KEY_INITIAL_STATE_PAGING); + TunerService.get(context).addTunable(this, KEY_FAST_TOGGLE, KEY_INITIAL_STATE_PAGING); } /** * @return whether we are enabling fast toggling. */ public boolean isFastToggleRecentsEnabled() { + // These checks EnableFastToggleTimeoutOverride + SystemServicesProxy ssp = Recents.getSystemServices(); + if (ssp.hasFreeformWorkspaceSupport() || ssp.hasDockedTask() || + ssp.isTouchExplorationEnabled()) { + return false; + } + if (Static.EnableFastToggleTimeoutOverride) { + return true; + } return mFastToggleRecents; } /** - * @return whether we are enabling the fast toggle indicator. - */ - public boolean isFastToggleIndicatorEnabled() { - return mFastToggleRecents && mFastToggleIndicator; - } - - /** * @return whether the initial stack state is paging. */ public boolean isInitialStatePaging() { + if (Static.EnableFastToggleTimeoutOnEnter) { + return true; + } return mInitialStatePaging; } @@ -95,10 +102,6 @@ public class RecentsDebugFlags implements TunerService.Tunable { mFastToggleRecents = (newValue != null) && (Integer.parseInt(newValue) != 0); break; - case KEY_FAST_TOGGLE_INDICATOR: - mFastToggleIndicator = (newValue != null) && - (Integer.parseInt(newValue) != 0); - break; case KEY_INITIAL_STATE_PAGING: mInitialStatePaging = (newValue != null) && (Integer.parseInt(newValue) != 0); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 5f11bee9249a..b78fd2282441 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -41,9 +41,11 @@ import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.SystemUIApplication; import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.activity.DockingTopTaskEvent; import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent; import com.android.systemui.recents.events.activity.HideRecentsEvent; import com.android.systemui.recents.events.activity.IterateRecentsEvent; +import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent; import com.android.systemui.recents.events.activity.ToggleRecentsEvent; import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent; import com.android.systemui.recents.events.component.ScreenPinningRequestEvent; @@ -62,6 +64,7 @@ import com.android.systemui.recents.views.TaskStackView; import com.android.systemui.recents.views.TaskViewHeader; import com.android.systemui.recents.views.TaskViewTransform; import com.android.systemui.statusbar.BaseStatusBar; +import com.android.systemui.statusbar.phone.NavigationBarGestureHelper; import com.android.systemui.statusbar.phone.PhoneStatusBar; import java.util.ArrayList; @@ -72,8 +75,7 @@ import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; * An implementation of the Recents component for the current user. For secondary users, this can * be called remotely from the system user. */ -public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements - ActivityOptions.OnAnimationFinishedListener { +public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener { private final static String TAG = "RecentsImpl"; // The minimum amount of time between each recents button press that we will handle @@ -532,18 +534,14 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements showRelativeAffiliatedTask(false); } - public boolean dockTopTask(boolean draggingInRecents, int stackCreateMode, Rect initialBounds) { + public void dockTopTask(int topTaskId, int dragMode, + int stackCreateMode, Rect initialBounds) { SystemServicesProxy ssp = Recents.getSystemServices(); - ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask(); - boolean screenPinningActive = ssp.isScreenPinningActive(); - boolean isTopTaskHome = SystemServicesProxy.isHomeStack(topTask.stackId); - if (topTask != null && !isTopTaskHome && !screenPinningActive) { - ssp.moveTaskToDockedStack(topTask.id, stackCreateMode, initialBounds); - showRecents(false /* triggeredFromAltTab */, draggingInRecents, false /* animate */, - true /* reloadTasks*/); - return true; - } - return false; + ssp.moveTaskToDockedStack(topTaskId, stackCreateMode, initialBounds); + showRecents(false /* triggeredFromAltTab */, + dragMode == NavigationBarGestureHelper.DRAG_MODE_RECENTS, false /* animate */, + true /* reloadTasks*/); + EventBus.getDefault().send(new DockingTopTaskEvent(dragMode)); } /** @@ -918,6 +916,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements mContext.startActivityAsUser(intent, UserHandle.CURRENT); } mCanReuseTaskStackViews = true; + EventBus.getDefault().send(new RecentsActivityStartingEvent()); } /**** OnAnimationFinishedListener Implementation ****/ diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java new file mode 100644 index 000000000000..86ec98ae3d4f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.recents; + +import android.graphics.Rect; +import android.os.Handler; +import android.os.Message; +import android.os.RemoteException; + +import com.android.internal.os.SomeArgs; + +/** + * A proxy class which directs all methods from {@link IRecentsNonSystemUserCallbacks} to + * {@link RecentsImpl} and makes sure they are called from the main thread. + */ +public class RecentsImplProxy extends IRecentsNonSystemUserCallbacks.Stub { + + private static final int MSG_PRELOAD_RECENTS = 1; + private static final int MSG_CANCEL_PRELOADING_RECENTS = 2; + private static final int MSG_SHOW_RECENTS = 3; + private static final int MSG_HIDE_RECENTS = 4; + private static final int MSG_TOGGLE_RECENTS = 5; + private static final int MSG_ON_CONFIGURATION_CHANGED = 6; + private static final int MSG_DOCK_TOP_TASK = 7; + private static final int MSG_ON_DRAGGING_IN_RECENTS = 8; + private static final int MSG_ON_DRAGGING_IN_RECENTS_ENDED = 9; + + private RecentsImpl mImpl; + + public RecentsImplProxy(RecentsImpl recentsImpl) { + mImpl = recentsImpl; + } + + @Override + public void preloadRecents() throws RemoteException { + mHandler.sendEmptyMessage(MSG_PRELOAD_RECENTS); + } + + @Override + public void cancelPreloadingRecents() throws RemoteException { + mHandler.sendEmptyMessage(MSG_CANCEL_PRELOADING_RECENTS); + } + + @Override + public void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents, boolean animate, + boolean reloadTasks) throws RemoteException { + SomeArgs args = SomeArgs.obtain(); + args.argi1 = triggeredFromAltTab ? 1 : 0; + args.argi2 = draggingInRecents ? 1 : 0; + args.argi3 = animate ? 1 : 0; + args.argi4 = reloadTasks ? 1 : 0; + mHandler.sendMessage(mHandler.obtainMessage(MSG_SHOW_RECENTS, args)); + } + + @Override + public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) + throws RemoteException { + mHandler.sendMessage(mHandler.obtainMessage(MSG_HIDE_RECENTS, triggeredFromAltTab ? 1 :0, + triggeredFromHomeKey ? 1 : 0)); + } + + @Override + public void toggleRecents() throws RemoteException { + mHandler.sendEmptyMessage(MSG_TOGGLE_RECENTS); + } + + @Override + public void onConfigurationChanged() throws RemoteException { + mHandler.sendEmptyMessage(MSG_ON_CONFIGURATION_CHANGED); + } + + @Override + public void dockTopTask(int topTaskId, int dragMode, int stackCreateMode, + Rect initialBounds) throws RemoteException { + SomeArgs args = SomeArgs.obtain(); + args.argi1 = topTaskId; + args.argi2 = dragMode; + args.argi3 = stackCreateMode; + args.arg1 = initialBounds; + mHandler.sendMessage(mHandler.obtainMessage(MSG_DOCK_TOP_TASK, args)); + } + + @Override + public void onDraggingInRecents(float distanceFromTop) throws RemoteException { + mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_DRAGGING_IN_RECENTS, distanceFromTop)); + } + + @Override + public void onDraggingInRecentsEnded(float velocity) throws RemoteException { + mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_DRAGGING_IN_RECENTS_ENDED, velocity)); + } + + private final Handler mHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_PRELOAD_RECENTS: + mImpl.preloadRecents(); + break; + case MSG_CANCEL_PRELOADING_RECENTS: + mImpl.cancelPreloadingRecents(); + break; + case MSG_SHOW_RECENTS: + SomeArgs args = (SomeArgs) msg.obj; + mImpl.showRecents(args.argi1 != 0, args.argi2 != 0, args.argi3 != 0, + args.argi4 != 0); + break; + case MSG_HIDE_RECENTS: + mImpl.hideRecents(msg.arg1 != 0, msg.arg2 != 0); + break; + case MSG_TOGGLE_RECENTS: + mImpl.toggleRecents(); + break; + case MSG_ON_CONFIGURATION_CHANGED: + mImpl.onConfigurationChanged(); + break; + case MSG_DOCK_TOP_TASK: + args = (SomeArgs) msg.obj; + mImpl.dockTopTask(args.argi1, args.argi2, args.argi3 = 0, + (Rect) args.arg1); + break; + case MSG_ON_DRAGGING_IN_RECENTS: + mImpl.onDraggingInRecents((Float) msg.obj); + break; + case MSG_ON_DRAGGING_IN_RECENTS_ENDED: + mImpl.onDraggingInRecentsEnded((Float) msg.obj); + break; + default: + super.handleMessage(msg); + } + super.handleMessage(msg); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java index fb215001f044..ae0051cba04e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java @@ -22,6 +22,11 @@ import android.os.RemoteException; import android.util.Log; import android.util.SparseArray; +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.activity.DockingTopTaskEvent; +import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent; +import com.android.systemui.recents.events.ui.RecentsDrawnEvent; + /** * An implementation of the system user's Recents interface to be called remotely by secondary * users. @@ -70,4 +75,19 @@ public class RecentsSystemUser extends IRecentsSystemUserCallbacks.Stub { public void startScreenPinning() { mImpl.onStartScreenPinning(mContext); } + + @Override + public void sendRecentsDrawnEvent() { + EventBus.getDefault().post(new RecentsDrawnEvent()); + } + + @Override + public void sendDockingTopTaskEvent(int dragMode) throws RemoteException { + EventBus.getDefault().post(new DockingTopTaskEvent(dragMode)); + } + + @Override + public void sendLaunchRecentsEvent() throws RemoteException { + EventBus.getDefault().post(new RecentsActivityStartingEvent()); + } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockingTopTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockingTopTaskEvent.java new file mode 100644 index 000000000000..264c2c43bd2e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockingTopTaskEvent.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.recents.events.activity; + +import com.android.systemui.recents.events.EventBus; + +/** + * Fires when the user invoked the gesture to dock the top/left task. + */ +public class DockingTopTaskEvent extends EventBus.Event { + + public int dragMode; + + public DockingTopTaskEvent(int dragMode) { + this.dragMode = dragMode; + } +} diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.aidl b/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsTaskStackAnimationCompletedEvent.java index cfd36059063f..ee0df87a6ed5 100644 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.aidl +++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsTaskStackAnimationCompletedEvent.java @@ -1,11 +1,11 @@ -/** - * Copyright (c) 2014, The Android Open Source Project +/* + * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -14,6 +14,13 @@ * limitations under the License. */ -package android.net.wifi.passpoint; +package com.android.systemui.recents.events.activity; + +import com.android.systemui.recents.events.EventBus; -parcelable WifiPasspointCredential; +/** + * This is sent when the in-app animations into Recents completes. + */ +public class EnterRecentsTaskStackAnimationCompletedEvent extends EventBus.AnimatedEvent { + // Simple event +} diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.aidl b/packages/SystemUI/src/com/android/systemui/recents/events/activity/RecentsActivityStartingEvent.java index 27f23bc3dc45..a2ecfe207cf9 100644 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.aidl +++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/RecentsActivityStartingEvent.java @@ -1,19 +1,26 @@ -/** - * Copyright (c) 2014, The Android Open Source Project +/* + * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. + * limitations under the License */ -package android.net.wifi.passpoint; +package com.android.systemui.recents.events.activity; + +import com.android.systemui.recents.events.EventBus; + +/** + * Called after recents activity is being started, i.e. startActivity has just been called. + */ +public class RecentsActivityStartingEvent extends EventBus.Event{ -parcelable WifiPasspointInfo; +} diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.aidl b/packages/SystemUI/src/com/android/systemui/recents/events/ui/RecentsDrawnEvent.java index 088136f5e98c..548316607133 100644 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.aidl +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/RecentsDrawnEvent.java @@ -1,19 +1,26 @@ -/** - * Copyright (c) 2014, The Android Open Source Project +/* + * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. + * limitations under the License */ -package android.net.wifi.passpoint; +package com.android.systemui.recents.events.ui; + +import com.android.systemui.recents.events.EventBus; + +/** + * Fired when recents was launched and has drawn its first frame. + */ +public class RecentsDrawnEvent extends EventBus.Event { -parcelable WifiPasspointOsuProvider; +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/UserInteractionEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/UserInteractionEvent.java index 5a132c2f64e7..39e4c1d7095c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/UserInteractionEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/UserInteractionEvent.java @@ -21,11 +21,6 @@ import com.android.systemui.recents.events.EventBus; /** * This is sent whenever the user interacts with the activity. */ -public class UserInteractionEvent extends EventBus.Event { - - public final boolean showTimerIndicator; - - public UserInteractionEvent(boolean showTimerIndicator) { - this.showTimerIndicator = showTimerIndicator; - } +public class UserInteractionEvent extends EventBus.ReusableEvent { + // Simple Event } diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java index def4ae16a59f..a1e4957a2719 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java @@ -23,9 +23,9 @@ import com.android.systemui.recents.events.EventBus; */ public class FocusNextTaskViewEvent extends EventBus.Event { - public final boolean showTimerIndicator; + public final int timerIndicatorDuration; - public FocusNextTaskViewEvent(boolean showTimerIndicator) { - this.showTimerIndicator = showTimerIndicator; + public FocusNextTaskViewEvent(int timerIndicatorDuration) { + this.timerIndicatorDuration = timerIndicatorDuration; } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java index dc8f5476f254..244c0df30da4 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java @@ -64,6 +64,13 @@ public class DozeTrigger { } /** + * Updates the duration that we have to wait until dozing triggers. + */ + public void setDozeDuration(int duration) { + mDozeDurationMilliseconds = duration; + } + + /** * Poke this dozer to wake it up if it is dozing, delaying the onSleepRunnable from being * called for a for the doze duration. */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index 87cfcff1066e..2882cecc6062 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -378,13 +378,19 @@ public class SystemServicesProxy { ActivityManager.StackInfo stackInfo = null; try { stackInfo = mIam.getStackInfo(DOCKED_STACK_ID); - if (stackInfo != null && stackInfo.userId != getCurrentUser()) { - return false; - } } catch (RemoteException e) { e.printStackTrace(); } - return stackInfo != null; + + if (stackInfo != null) { + int userId = getCurrentUser(); + boolean hasUserTask = false; + for (int i = stackInfo.taskUserIds.length - 1; i >= 0 && !hasUserTask; i--) { + hasUserTask = (stackInfo.taskUserIds[i] == userId); + } + return hasUserTask; + } + return false; } /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index ccc858100d48..7423b78a9cb5 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -44,9 +44,11 @@ import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsActivity; import com.android.systemui.recents.RecentsActivityLaunchState; import com.android.systemui.recents.RecentsConfiguration; +import com.android.systemui.recents.RecentsDebugFlags; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent; import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted; +import com.android.systemui.recents.events.activity.EnterRecentsTaskStackAnimationCompletedEvent; import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent; import com.android.systemui.recents.events.activity.HideHistoryButtonEvent; import com.android.systemui.recents.events.activity.HideHistoryEvent; @@ -115,7 +117,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal GradientDrawable mFreeformWorkspaceBackground; ObjectAnimator mFreeformWorkspaceBackgroundAnimator; ViewPool<TaskView, Task> mViewPool; - boolean mStartTimerIndicator; ArrayList<TaskView> mTaskViews = new ArrayList<>(); ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>(); @@ -127,6 +128,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal int mTaskCornerRadiusPx; private int mDividerSize; + private int mStartTimerIndicatorDuration; boolean mTaskViewsClipDirty = true; boolean mAwaitingFirstLayout = true; @@ -796,7 +798,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal */ private boolean setFocusedTask(int taskIndex, boolean scrollToTask, final boolean requestViewFocus) { - return setFocusedTask(taskIndex, scrollToTask, requestViewFocus, false); + return setFocusedTask(taskIndex, scrollToTask, requestViewFocus, 0); } /** @@ -805,7 +807,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal * @return whether or not the stack will scroll as a part of this focus change */ private boolean setFocusedTask(int taskIndex, boolean scrollToTask, - final boolean requestViewFocus, final boolean showTimerIndicator) { + final boolean requestViewFocus, final int timerIndicatorDuration) { // Find the next task to focus int newFocusedTaskIndex = mStack.getTaskCount() > 0 ? Math.max(0, Math.min(mStack.getTaskCount() - 1, taskIndex)) : -1; @@ -815,7 +817,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Reset the last focused task state if changed if (mFocusedTask != null) { // Cancel the timer indicator, if applicable - if (showTimerIndicator) { + if (timerIndicatorDuration > 0) { final TaskView tv = getChildViewForTask(mFocusedTask); if (tv != null) { tv.getHeaderView().cancelFocusTimerIndicator(); @@ -831,13 +833,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal if (newFocusedTask != null) { // Start the timer indicator, if applicable - if (showTimerIndicator) { + if (timerIndicatorDuration > 0) { final TaskView tv = getChildViewForTask(mFocusedTask); if (tv != null) { - tv.getHeaderView().startFocusTimerIndicator(); + tv.getHeaderView().startFocusTimerIndicator(timerIndicatorDuration); } else { // The view is null; set a flag for later - mStartTimerIndicator = true; + mStartTimerIndicatorDuration = timerIndicatorDuration; } } @@ -902,7 +904,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal */ public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated, boolean cancelWindowAnimations) { - setRelativeFocusedTask(forward, stackTasksOnly, animated, cancelWindowAnimations, false); + setRelativeFocusedTask(forward, stackTasksOnly, animated, cancelWindowAnimations, 0); } /** @@ -916,10 +918,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal * focus. * @param cancelWindowAnimations if set, will attempt to cancel window animations if a scroll * happens. - * @param showTimerIndicator determines whether or not to show an indicator for the task auto-advance. + * @param timerIndicatorDuration the duration to initialize the auto-advance timer indicator */ public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated, - boolean cancelWindowAnimations, boolean showTimerIndicator) { + boolean cancelWindowAnimations, + int timerIndicatorDuration) { int newIndex = mStack.indexOfStackTask(mFocusedTask); if (mFocusedTask != null) { if (stackTasksOnly) { @@ -955,7 +958,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } if (newIndex != -1) { boolean willScroll = setFocusedTask(newIndex, true /* scrollToTask */, - true /* requestViewFocus */, showTimerIndicator); + true /* requestViewFocus */, timerIndicatorDuration); if (willScroll && cancelWindowAnimations) { // As we iterate to the next/previous task, cancel any current/lagging window // transition animations @@ -1223,9 +1226,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal Task launchTask = mStack.getLaunchTarget(); RecentsConfiguration config = Recents.getConfiguration(); RecentsActivityLaunchState launchState = config.getLaunchState(); - int focusedTaskIndex = launchTask != null - ? mStack.indexOfStackTask(launchTask) - : launchState.getInitialFocusTaskIndex(mStack.getTaskCount()); + int focusedTaskIndex = launchState.getInitialFocusTaskIndex(mStack.getTaskCount()); if (focusedTaskIndex != -1) { setFocusedTask(focusedTaskIndex, false /* scrollToTask */, false /* requestViewFocus */); @@ -1419,10 +1420,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal tv.setClipViewInStack(true); if (mFocusedTask == task) { tv.setFocusedState(true, false /* requestViewFocus */); - if (mStartTimerIndicator) { + if (mStartTimerIndicatorDuration > 0) { // The timer indicator couldn't be started before, so start it now - tv.getHeaderView().startFocusTimerIndicator(); - mStartTimerIndicator = false; + tv.getHeaderView().startFocusTimerIndicator(mStartTimerIndicatorDuration); + mStartTimerIndicatorDuration = 0; } } @@ -1549,7 +1550,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal public final void onBusEvent(FocusNextTaskViewEvent event) { setRelativeFocusedTask(true, false /* stackTasksOnly */, true /* animated */, false, - event.showTimerIndicator); + event.timerIndicatorDuration); } public final void onBusEvent(FocusPreviousTaskViewEvent event) { @@ -1559,7 +1560,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal public final void onBusEvent(UserInteractionEvent event) { // Poke the doze trigger on user interaction mUIDozeTrigger.poke(); - if (event.showTimerIndicator && mFocusedTask != null) { + + RecentsDebugFlags debugFlags = Recents.getDebugFlags(); + if (debugFlags.isFastToggleRecentsEnabled() && mFocusedTask != null) { TaskView tv = getChildViewForTask(mFocusedTask); if (tv != null) { tv.getHeaderView().cancelFocusTimerIndicator(); @@ -1715,11 +1718,28 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal setFocusedTask(mStack.indexOfStackTask(mFocusedTask), false /* scrollToTask */, launchState.launchedWithAltTab); } + + EventBus.getDefault().send(new EnterRecentsTaskStackAnimationCompletedEvent()); } }); } } + public final void onBusEvent(EnterRecentsTaskStackAnimationCompletedEvent event) { + RecentsDebugFlags debugFlags = Recents.getDebugFlags(); + RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); + if (!launchState.launchedWithAltTab && debugFlags.isFastToggleRecentsEnabled() && + RecentsDebugFlags.Static.EnableFastToggleTimeoutOnEnter) { + if (mFocusedTask != null) { + int timerIndicatorDuration = getResources().getInteger( + R.integer.recents_auto_advance_duration); + int focusedTaskIndex = mStack.indexOfStackTask(mFocusedTask); + setFocusedTask(focusedTaskIndex, false /* scrollToTask */, + false /* requestViewFocus */, timerIndicatorDuration); + } + } + } + public final void onBusEvent(UpdateFreeformTaskViewVisibilityEvent event) { List<TaskView> taskViews = getTaskViews(); int taskViewCount = taskViews.size(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java index cb108daab3ac..d5aea9ded301 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java @@ -169,7 +169,6 @@ public class TaskViewHeader extends FrameLayout Interpolator mLinearOutSlowInInterpolator; private CountDownTimer mFocusTimerCountDown; - private long mFocusTimerDuration; public TaskViewHeader(Context context) { this(context, null); @@ -216,7 +215,6 @@ public class TaskViewHeader extends FrameLayout mOverlayBackground = new HighlightColorDrawable(); mDimLayerPaint.setColor(Color.argb(255, 0, 0, 0)); mDimLayerPaint.setAntiAlias(true); - mFocusTimerDuration = res.getInteger(R.integer.recents_auto_advance_duration); } /** @@ -304,27 +302,27 @@ public class TaskViewHeader extends FrameLayout } /** Starts the focus timer. */ - public void startFocusTimerIndicator() { + public void startFocusTimerIndicator(int duration) { if (mFocusTimerIndicator == null) { return; } mFocusTimerIndicator.setVisibility(View.VISIBLE); - mFocusTimerIndicator.setMax((int) mFocusTimerDuration); - if (mFocusTimerCountDown == null) { - mFocusTimerCountDown = new CountDownTimer(mFocusTimerDuration, - FOCUS_INDICATOR_INTERVAL_MS) { - public void onTick(long millisUntilFinished) { - mFocusTimerIndicator.setProgress((int) millisUntilFinished); - } - - public void onFinish() { - mFocusTimerIndicator.setProgress((int) mFocusTimerDuration); - } - }.start(); - } else { - mFocusTimerCountDown.start(); + mFocusTimerIndicator.setMax(duration); + mFocusTimerIndicator.setProgress(duration); + if (mFocusTimerCountDown != null) { + mFocusTimerCountDown.cancel(); } + mFocusTimerCountDown = new CountDownTimer(duration, + FOCUS_INDICATOR_INTERVAL_MS) { + public void onTick(long millisUntilFinished) { + mFocusTimerIndicator.setProgress((int) millisUntilFinished); + } + + public void onFinish() { + // Do nothing + } + }.start(); } /** Cancels the focus timer. */ @@ -411,7 +409,7 @@ public class TaskViewHeader extends FrameLayout mMoveTaskButton.setOnClickListener(this); } - if (Recents.getDebugFlags().isFastToggleIndicatorEnabled()) { + if (Recents.getDebugFlags().isFastToggleRecentsEnabled()) { if (mFocusTimerIndicator == null) { mFocusTimerIndicator = (ProgressBar) mFocusTimerIndicatorStub.inflate(); } diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index 08793e8107a4..c0e1e44f6900 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -49,7 +49,12 @@ import com.android.internal.policy.DividerSnapAlgorithm; import com.android.internal.policy.DockedDividerUtils; import com.android.systemui.R; import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget; +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.activity.DockingTopTaskEvent; +import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent; +import com.android.systemui.recents.events.ui.RecentsDrawnEvent; import com.android.systemui.statusbar.FlingAnimationUtils; +import com.android.systemui.statusbar.phone.NavigationBarGestureHelper; import static android.view.PointerIcon.STYLE_HORIZONTAL_DOUBLE_ARROW; import static android.view.PointerIcon.STYLE_VERTICAL_DOUBLE_ARROW; @@ -118,6 +123,10 @@ public class DividerView extends FrameLayout implements OnTouchListener, private DividerSnapAlgorithm mSnapAlgorithm; private final Rect mStableInsets = new Rect(); + private boolean mAnimateAfterRecentsDrawn; + private boolean mGrowAfterRecentsDrawn; + private boolean mGrowRecents; + public DividerView(Context context) { super(context); } @@ -148,6 +157,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, mDividerSize = mDividerWindowWidth - 2 * mDividerInsets; mTouchElevation = getResources().getDimensionPixelSize( R.dimen.docked_stack_divider_lift_elevation); + mGrowRecents = getResources().getBoolean(R.bool.recents_grow_in_multiwindow); mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(), android.R.interpolator.fast_out_slow_in); mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); @@ -161,6 +171,18 @@ public class DividerView extends FrameLayout implements OnTouchListener, } @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + EventBus.getDefault().register(this); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + EventBus.getDefault().unregister(this); + } + + @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { mStableInsets.set(insets.getStableInsetLeft(), insets.getStableInsetTop(), insets.getStableInsetRight(), insets.getStableInsetBottom()); @@ -175,15 +197,18 @@ public class DividerView extends FrameLayout implements OnTouchListener, return mWindowManagerProxy; } - public boolean startDragging(boolean animate) { - mHandle.setTouching(true, animate); + public boolean startDragging(boolean animate, boolean touching) { + if (touching) { + mHandle.setTouching(true, animate); + } mDockSide = mWindowManagerProxy.getDockSide(); - mSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(), mDisplayWidth, - mDisplayHeight, mDividerSize, isHorizontalDivision(), mStableInsets); + getSnapAlgorithm(); if (mDockSide != WindowManager.DOCKED_INVALID) { mWindowManagerProxy.setResizing(true); mWindowManager.setSlippery(false); - liftBackground(); + if (touching) { + liftBackground(); + } return true; } else { return false; @@ -197,10 +222,31 @@ public class DividerView extends FrameLayout implements OnTouchListener, releaseBackground(); } + public void stopDragging(int position, SnapTarget target, long duration, + Interpolator interpolator) { + mHandle.setTouching(false, true /* animate */); + flingTo(position, target, duration, interpolator); + mWindowManager.setSlippery(true); + releaseBackground(); + } + public DividerSnapAlgorithm getSnapAlgorithm() { + if (mSnapAlgorithm == null) { + mSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(), mDisplayWidth, + mDisplayHeight, mDividerSize, isHorizontalDivision(), mStableInsets); + } return mSnapAlgorithm; } + public int getCurrentPosition() { + getLocationOnScreen(mTempInt2); + if (isHorizontalDivision()) { + return mTempInt2[1] + mDividerInsets; + } else { + return mTempInt2[0] + mDividerInsets; + } + } + @Override public boolean onTouch(View v, MotionEvent event) { convertToScreenCoordinates(event); @@ -211,13 +257,8 @@ public class DividerView extends FrameLayout implements OnTouchListener, mVelocityTracker.addMovement(event); mStartX = (int) event.getX(); mStartY = (int) event.getY(); - getLocationOnScreen(mTempInt2); - boolean result = startDragging(true /* animate */); - if (isHorizontalDivision()) { - mStartPosition = mTempInt2[1] + mDividerInsets; - } else { - mStartPosition = mTempInt2[0] + mDividerInsets; - } + boolean result = startDragging(true /* animate */, true /* touching */); + mStartPosition = getCurrentPosition(); mMoving = false; return result; case MotionEvent.ACTION_MOVE: @@ -265,8 +306,20 @@ public class DividerView extends FrameLayout implements OnTouchListener, if (avoidDismissStart && snapTarget == mSnapAlgorithm.getDismissStartTarget()) { snapTarget = mSnapAlgorithm.getFirstSplitTarget(); } - final SnapTarget finalTarget = snapTarget; + ValueAnimator anim = getFlingAnimator(position, snapTarget); + mFlingAnimationUtils.apply(anim, position, snapTarget.position, velocity); + anim.start(); + } + + private void flingTo(int position, SnapTarget target, long duration, + Interpolator interpolator) { + ValueAnimator anim = getFlingAnimator(position, target); + anim.setDuration(duration); + anim.setInterpolator(interpolator); + anim.start(); + } + private ValueAnimator getFlingAnimator(int position, final SnapTarget snapTarget) { ValueAnimator anim = ValueAnimator.ofInt(position, snapTarget.position); anim.addUpdateListener(new AnimatorUpdateListener() { @Override @@ -274,19 +327,18 @@ public class DividerView extends FrameLayout implements OnTouchListener, resizeStack((Integer) animation.getAnimatedValue(), animation.getAnimatedFraction() == 1f ? TASK_POSITION_SAME - : finalTarget.position, finalTarget); + : snapTarget.position, snapTarget); } }); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - commitSnapFlags(finalTarget); + commitSnapFlags(snapTarget); mWindowManagerProxy.setResizing(false); mDockSide = WindowManager.DOCKED_INVALID; } }); - mFlingAnimationUtils.apply(anim, position, snapTarget.position, velocity); - anim.start(); + return anim; } private void commitSnapFlags(SnapTarget target) { @@ -359,6 +411,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, display.getDisplayInfo(info); mDisplayWidth = info.logicalWidth; mDisplayHeight = info.logicalHeight; + mSnapAlgorithm = null; } private int calculatePosition(int touchX, int touchY) { @@ -607,4 +660,33 @@ public class DividerView extends FrameLayout implements OnTouchListener, inoutInfo.touchableRegion.op(mBackground.getLeft(), mBackground.getTop(), mBackground.getRight(), mBackground.getBottom(), Op.UNION); } + + public final void onBusEvent(RecentsActivityStartingEvent recentsActivityStartingEvent) { + if (mGrowRecents && getWindowManagerProxy().getDockSide() == WindowManager.DOCKED_TOP + && getCurrentPosition() == getSnapAlgorithm().getLastSplitTarget().position) { + mGrowAfterRecentsDrawn = true; + startDragging(false /* animate */, false /* touching */); + } + } + + public final void onBusEvent(DockingTopTaskEvent dockingEvent) { + if (dockingEvent.dragMode == NavigationBarGestureHelper.DRAG_MODE_NONE) { + mGrowAfterRecentsDrawn = false; + mAnimateAfterRecentsDrawn = true; + startDragging(false /* animate */, false /* touching */); + } + } + + public final void onBusEvent(RecentsDrawnEvent drawnEvent) { + if (mAnimateAfterRecentsDrawn) { + mAnimateAfterRecentsDrawn = false; + stopDragging(getCurrentPosition(), mSnapAlgorithm.getMiddleTarget(), 250, + TOUCH_RESPONSE_INTERPOLATOR); + } + if (mGrowAfterRecentsDrawn) { + mGrowAfterRecentsDrawn = false; + stopDragging(getCurrentPosition(), mSnapAlgorithm.getMiddleTarget(), 250, + TOUCH_RESPONSE_INTERPOLATOR); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java index 38d24ced806f..01bfcea7db90 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java @@ -19,6 +19,7 @@ package com.android.systemui.statusbar; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; +import android.animation.TimeAnimator; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; @@ -28,13 +29,12 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewAnimationUtils; import android.view.ViewConfiguration; -import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; -import android.view.animation.LinearInterpolator; import android.view.animation.PathInterpolator; import com.android.systemui.R; import com.android.systemui.classifier.FalsingManager; +import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; /** * Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer} @@ -107,11 +107,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private OnActivatedListener mOnActivatedListener; - private final Interpolator mLinearOutSlowInInterpolator; - protected final Interpolator mFastOutSlowInInterpolator; private final Interpolator mSlowOutFastInInterpolator; private final Interpolator mSlowOutLinearInInterpolator; - private final Interpolator mLinearInterpolator; private Interpolator mCurrentAppearInterpolator; private Interpolator mCurrentAlphaInterpolator; @@ -132,16 +129,37 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private FalsingManager mFalsingManager; private boolean mTrackTouch; + private float mNormalBackgroundVisibilityAmount; + private ValueAnimator mFadeInFromDarkAnimator; + private ValueAnimator.AnimatorUpdateListener mBackgroundVisibilityUpdater + = new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + setNormalBackgroundVisibilityAmount(mBackgroundNormal.getAlpha()); + } + }; + private AnimatorListenerAdapter mFadeInEndListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + mFadeInFromDarkAnimator = null; + updateOutlineAlpha(); + } + }; + private ValueAnimator.AnimatorUpdateListener mUpdateOutlineListener + = new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + updateOutlineAlpha(); + } + }; + private float mShadowAlpha = 1.0f; + public ActivatableNotificationView(Context context, AttributeSet attrs) { super(context, attrs); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); - mFastOutSlowInInterpolator = - AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in); mSlowOutFastInInterpolator = new PathInterpolator(0.8f, 0.0f, 0.6f, 1.0f); - mLinearOutSlowInInterpolator = - AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in); mSlowOutLinearInInterpolator = new PathInterpolator(0.8f, 0.0f, 1.0f, 1.0f); - mLinearInterpolator = new LinearInterpolator(); setClipChildren(false); setClipToPadding(false); mLegacyColor = context.getColor(R.color.notification_legacy_background_color); @@ -166,6 +184,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mBackgroundDimmed.setCustomBackground(R.drawable.notification_material_bg_dim); updateBackground(); updateBackgroundTint(); + updateOutlineAlpha(); } private final Runnable mTapTimeoutRunnable = new Runnable() { @@ -272,7 +291,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } } - private void startActivateAnimation(boolean reverse) { + private void startActivateAnimation(final boolean reverse) { if (!isAttachedToWindow()) { return; } @@ -291,8 +310,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView Interpolator interpolator; Interpolator alphaInterpolator; if (!reverse) { - interpolator = mLinearOutSlowInInterpolator; - alphaInterpolator = mLinearOutSlowInInterpolator; + interpolator = Interpolators.LINEAR_OUT_SLOW_IN; + alphaInterpolator = Interpolators.LINEAR_OUT_SLOW_IN; } else { interpolator = ACTIVATE_INVERSE_INTERPOLATOR; alphaInterpolator = ACTIVATE_INVERSE_ALPHA_INTERPOLATOR; @@ -317,6 +336,16 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mBackgroundNormal.animate() .alpha(reverse ? 0f : 1f) .setInterpolator(alphaInterpolator) + .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float animatedFraction = animation.getAnimatedFraction(); + if (reverse) { + animatedFraction = 1.0f - animatedFraction; + } + setNormalBackgroundVisibilityAmount(animatedFraction); + } + }) .setDuration(ACTIVATE_ANIMATION_LENGTH); } @@ -377,8 +406,27 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } else { updateBackground(); } - setOutlineAlpha(dark ? 0f : 1f); - } + updateOutlineAlpha(); + } + + private void updateOutlineAlpha() { + if (mDark) { + setOutlineAlpha(0f); + return; + } + float alpha = NotificationStackScrollLayout.BACKGROUND_ALPHA_DIMMED; + alpha = (alpha + (1.0f - alpha) * mNormalBackgroundVisibilityAmount); + alpha *= mShadowAlpha; + if (mFadeInFromDarkAnimator != null) { + alpha *= mFadeInFromDarkAnimator.getAnimatedFraction(); + } + setOutlineAlpha(alpha); + } + + public void setNormalBackgroundVisibilityAmount(float normalBackgroundVisibilityAmount) { + mNormalBackgroundVisibilityAmount = normalBackgroundVisibilityAmount; + updateOutlineAlpha(); + } public void setShowingLegacyBackground(boolean showing) { mShowingLegacyBackground = showing; @@ -431,7 +479,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView .scaleY(1f) .setDuration(DARK_ANIMATION_LENGTH) .setStartDelay(delay) - .setInterpolator(mLinearOutSlowInInterpolator) + .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationCancel(Animator animation) { @@ -441,7 +489,15 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView background.setAlpha(1f); } }) + .setUpdateListener(mBackgroundVisibilityUpdater) .start(); + mFadeInFromDarkAnimator = TimeAnimator.ofFloat(0.0f, 1.0f); + mFadeInFromDarkAnimator.setDuration(DARK_ANIMATION_LENGTH); + mFadeInFromDarkAnimator.setStartDelay(delay); + mFadeInFromDarkAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); + mFadeInFromDarkAnimator.addListener(mFadeInEndListener); + mFadeInFromDarkAnimator.addUpdateListener(mUpdateOutlineListener); + mFadeInFromDarkAnimator.start(); } /** @@ -474,7 +530,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mBackgroundNormal.setAlpha(startAlpha); mBackgroundAnimator = ObjectAnimator.ofFloat(mBackgroundNormal, View.ALPHA, startAlpha, endAlpha); - mBackgroundAnimator.setInterpolator(mFastOutSlowInInterpolator); + mBackgroundAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); mBackgroundAnimator.setDuration(duration); mBackgroundAnimator.addListener(new AnimatorListenerAdapter() { @Override @@ -487,6 +543,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mBackgroundAnimator = null; } }); + mBackgroundAnimator.addUpdateListener(mBackgroundVisibilityUpdater); mBackgroundAnimator.start(); } @@ -504,6 +561,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mBackgroundNormal.setAlpha(1f); removeCallbacks(mTapTimeoutRunnable); } + setNormalBackgroundVisibilityAmount( + mBackgroundNormal.getVisibility() == View.VISIBLE ? 1.0f : 0.0f); } protected boolean shouldHideBackground() { @@ -577,16 +636,16 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView float targetValue; if (isAppearing) { mCurrentAppearInterpolator = mSlowOutFastInInterpolator; - mCurrentAlphaInterpolator = mLinearOutSlowInInterpolator; + mCurrentAlphaInterpolator = Interpolators.LINEAR_OUT_SLOW_IN; targetValue = 1.0f; } else { - mCurrentAppearInterpolator = mFastOutSlowInInterpolator; + mCurrentAppearInterpolator = Interpolators.FAST_OUT_SLOW_IN; mCurrentAlphaInterpolator = mSlowOutLinearInInterpolator; targetValue = 0.0f; } mAppearAnimator = ValueAnimator.ofFloat(mAppearAnimationFraction, targetValue); - mAppearAnimator.setInterpolator(mLinearInterpolator); + mAppearAnimator.setInterpolator(Interpolators.LINEAR); mAppearAnimator.setDuration( (long) (duration * Math.abs(mAppearAnimationFraction - targetValue))); mAppearAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @@ -772,6 +831,19 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView return getBgColor() == otherView.getBgColor(); } + @Override + public float getShadowAlpha() { + return mShadowAlpha; + } + + @Override + public void setShadowAlpha(float shadowAlpha) { + if (shadowAlpha != mShadowAlpha) { + mShadowAlpha = shadowAlpha; + updateOutlineAlpha(); + } + } + public interface OnActivatedListener { void onActivated(ActivatableNotificationView view); void onActivationReset(ActivatableNotificationView view); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 2592486cde7c..efa56bce6c5a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -186,7 +186,7 @@ public abstract class BaseStatusBar extends SystemUI implements protected DevicePolicyManager mDevicePolicyManager; protected IDreamManager mDreamManager; - PowerManager mPowerManager; + protected PowerManager mPowerManager; protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; // public mode, private notifications, etc @@ -219,8 +219,6 @@ public abstract class BaseStatusBar extends SystemUI implements // which notification is currently being longpress-examined by the user private NotificationGuts mNotificationGutsExposed; - private TimeInterpolator mLinearOutSlowIn, mFastOutLinearIn; - private KeyboardShortcuts mKeyboardShortcuts; /** @@ -638,11 +636,6 @@ public abstract class BaseStatusBar extends SystemUI implements mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - mLinearOutSlowIn = AnimationUtils.loadInterpolator(mContext, - android.R.interpolator.linear_out_slow_in); - mFastOutLinearIn = AnimationUtils.loadInterpolator(mContext, - android.R.interpolator.fast_out_linear_in); - // Connect in to the status bar manager service mCommandQueue = new CommandQueue(this); @@ -1016,7 +1009,7 @@ public abstract class BaseStatusBar extends SystemUI implements final Animator a = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r); a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); - a.setInterpolator(mLinearOutSlowIn); + a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); a.start(); guts.setExposed(true); mStackScroller.onHeightChanged(null, true /* needsAnimation */); @@ -1048,7 +1041,7 @@ public abstract class BaseStatusBar extends SystemUI implements final Animator a = ViewAnimationUtils.createCircularReveal(v, x, y, r, 0); a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); - a.setInterpolator(mFastOutLinearIn); + a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); a.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java index f71f09219e1c..24cd948d415b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java @@ -31,7 +31,7 @@ public class CrossFadeHelper { view.animate() .alpha(0f) .setDuration(ANIMATION_DURATION_LENGTH) - .setInterpolator(PhoneStatusBar.ALPHA_OUT) + .setInterpolator(Interpolators.ALPHA_OUT) .withEndAction(new Runnable() { @Override public void run() { @@ -56,7 +56,7 @@ public class CrossFadeHelper { view.animate() .alpha(1f) .setDuration(ANIMATION_DURATION_LENGTH) - .setInterpolator(PhoneStatusBar.ALPHA_IN) + .setInterpolator(Interpolators.ALPHA_IN) .withEndAction(null); if (view.hasOverlappingRendering()) { view.animate().withLayer(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java index 857019875d9d..5c83f5f2072f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java @@ -24,8 +24,7 @@ import android.content.Context; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; + import com.android.systemui.ExpandHelper; import com.android.systemui.Gefingerpoken; import com.android.systemui.R; @@ -53,7 +52,6 @@ public class DragDownHelper implements Gefingerpoken { private final int[] mTemp2 = new int[2]; private boolean mDraggedFarEnough; private ExpandableView mStartingChild; - private Interpolator mInterpolator; private float mLastHeight; private FalsingManager mFalsingManager; @@ -61,8 +59,6 @@ public class DragDownHelper implements Gefingerpoken { DragDownCallback dragDownCallback) { mMinDragDistance = context.getResources().getDimensionPixelSize( R.dimen.keyguard_drag_down_min_distance); - mInterpolator = - AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mCallback = callback; mDragDownCallback = dragDownCallback; @@ -187,7 +183,7 @@ public class DragDownHelper implements Gefingerpoken { } ObjectAnimator anim = ObjectAnimator.ofInt(child, "actualHeight", child.getActualHeight(), child.getMinHeight()); - anim.setInterpolator(mInterpolator); + anim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); anim.setDuration(SPRING_BACK_ANIMATION_LENGTH_MS); anim.addListener(new AnimatorListenerAdapter() { @Override @@ -200,7 +196,7 @@ public class DragDownHelper implements Gefingerpoken { private void cancelExpansion() { ValueAnimator anim = ValueAnimator.ofFloat(mLastHeight, 0); - anim.setInterpolator(mInterpolator); + anim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); anim.setDuration(SPRING_BACK_ANIMATION_LENGTH_MS); anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 93be009c29fd..6fae3abb513f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -1126,6 +1126,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { } @Override + public boolean needsIncreasedPadding() { + return mIsSummaryWithChildren && isGroupExpanded(); + } + + @Override protected boolean disallowSingleClick(MotionEvent event) { float x = event.getX(); float y = event.getY(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java index a6fc4bbcb11d..44c6a5d36c64 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java @@ -24,22 +24,17 @@ import android.util.AttributeSet; import android.view.View; import android.view.ViewOutlineProvider; -import com.android.systemui.R; - /** * Like {@link ExpandableView}, but setting an outline for the height and clipping. */ public abstract class ExpandableOutlineView extends ExpandableView { private final Rect mOutlineRect = new Rect(); - protected final int mRoundedRectCornerRadius; private boolean mCustomOutline; - private float mOutlineAlpha = 1f; + private float mOutlineAlpha = -1f; public ExpandableOutlineView(Context context, AttributeSet attrs) { super(context, attrs); - mRoundedRectCornerRadius = getResources().getDimensionPixelSize( - R.dimen.notification_material_rounded_rect_radius); setOutlineProvider(new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { @@ -49,7 +44,7 @@ public abstract class ExpandableOutlineView extends ExpandableView { getWidth(), Math.max(getActualHeight(), mClipTopAmount)); } else { - outline.setRoundRect(mOutlineRect, mRoundedRectCornerRadius); + outline.setRect(mOutlineRect); } outline.setAlpha(mOutlineAlpha); } @@ -69,8 +64,10 @@ public abstract class ExpandableOutlineView extends ExpandableView { } protected void setOutlineAlpha(float alpha) { - mOutlineAlpha = alpha; - invalidateOutline(); + if (alpha != mOutlineAlpha) { + mOutlineAlpha = alpha; + invalidateOutline(); + } } protected void setOutlineRect(RectF rect) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java index c190864eff7a..a0fb34aecbf9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java @@ -388,6 +388,17 @@ public abstract class ExpandableView extends FrameLayout { return super.hasOverlappingRendering() && getActualHeight() <= getHeight(); } + public float getShadowAlpha() { + return 0.0f; + } + + public void setShadowAlpha(float shadowAlpha) { + } + + public boolean needsIncreasedPadding() { + return false; + } + /** * A listener notifying when {@link #getActualHeight} changes. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java index 0fa088b703a4..c4ffd7fd1139 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java @@ -19,7 +19,6 @@ package com.android.systemui.statusbar; import android.animation.Animator; import android.content.Context; import android.view.ViewPropertyAnimator; -import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; @@ -41,8 +40,6 @@ public class FlingAnimationUtils { private static final float LINEAR_OUT_SLOW_IN_START_GRADIENT = 1.0f / LINEAR_OUT_SLOW_IN_X2; private Interpolator mLinearOutSlowIn; - private Interpolator mFastOutSlowIn; - private Interpolator mFastOutLinearIn; private float mMinVelocityPxPerSecond; private float mMaxLengthSeconds; @@ -53,10 +50,6 @@ public class FlingAnimationUtils { public FlingAnimationUtils(Context ctx, float maxLengthSeconds) { mMaxLengthSeconds = maxLengthSeconds; mLinearOutSlowIn = new PathInterpolator(0, 0, LINEAR_OUT_SLOW_IN_X2, 1); - mFastOutSlowIn - = AnimationUtils.loadInterpolator(ctx, android.R.interpolator.fast_out_slow_in); - mFastOutLinearIn - = AnimationUtils.loadInterpolator(ctx, android.R.interpolator.fast_out_linear_in); mMinVelocityPxPerSecond = MIN_VELOCITY_DP_PER_SECOND * ctx.getResources().getDisplayMetrics().density; mHighVelocityPxPerSecond @@ -150,7 +143,7 @@ public class FlingAnimationUtils { // Just use a normal interpolator which doesn't take the velocity into account. durationSeconds = maxLengthSeconds; - mAnimatorProperties.interpolator = mFastOutSlowIn; + mAnimatorProperties.interpolator = Interpolators.FAST_OUT_SLOW_IN; } mAnimatorProperties.duration = (long) (durationSeconds * 1000); return mAnimatorProperties; @@ -223,7 +216,7 @@ public class FlingAnimationUtils { // Just use a normal interpolator which doesn't take the velocity into account. durationSeconds = maxLengthSeconds; - mAnimatorProperties.interpolator = mFastOutLinearIn; + mAnimatorProperties.interpolator = Interpolators.FAST_OUT_LINEAR_IN; } mAnimatorProperties.duration = (long) (durationSeconds * 1000); return mAnimatorProperties; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/Interpolators.java b/packages/SystemUI/src/com/android/systemui/statusbar/Interpolators.java new file mode 100644 index 000000000000..5979468c7f80 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/Interpolators.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar; + +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; +import android.view.animation.PathInterpolator; + +/** + * Utility class to receive interpolators from + */ +public class Interpolators { + public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f); + public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f); + public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f); + public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f); + public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f); + public static final Interpolator LINEAR = new LinearInterpolator(); + public static final Interpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator(); +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java index 8058933aab32..841b9d049bf8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java @@ -33,13 +33,11 @@ import android.view.DisplayListCanvas; import android.view.RenderNodeAnimator; import android.view.View; import android.view.ViewAnimationUtils; -import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.ImageView; import com.android.systemui.R; import com.android.systemui.statusbar.phone.KeyguardAffordanceHelper; -import com.android.systemui.statusbar.phone.PhoneStatusBar; /** * An ImageView which does not have overlapping renderings commands and therefore does not need a @@ -55,8 +53,6 @@ public class KeyguardAffordanceView extends ImageView { private final int mMinBackgroundRadius; private final Paint mCirclePaint; - private final Interpolator mAppearInterpolator; - private final Interpolator mDisappearInterpolator; private final int mInverseColor; private final int mNormalColor; private final ArgbEvaluator mColorInterpolator; @@ -136,10 +132,6 @@ public class KeyguardAffordanceView extends ImageView { mInverseColor = 0xff000000; mMinBackgroundRadius = mContext.getResources().getDimensionPixelSize( R.dimen.keyguard_affordance_min_background_radius); - mAppearInterpolator = AnimationUtils.loadInterpolator(mContext, - android.R.interpolator.linear_out_slow_in); - mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext, - android.R.interpolator.fast_out_linear_in); mColorInterpolator = new ArgbEvaluator(); mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.3f); } @@ -261,7 +253,7 @@ public class KeyguardAffordanceView extends ImageView { RenderNodeAnimator animator = new RenderNodeAnimator(mHwCirclePaint, RenderNodeAnimator.PAINT_ALPHA, 255); animator.setTarget(this); - animator.setInterpolator(PhoneStatusBar.ALPHA_IN); + animator.setInterpolator(Interpolators.ALPHA_IN); animator.setDuration(250); animator.start(); } @@ -282,7 +274,7 @@ public class KeyguardAffordanceView extends ImageView { RenderNodeAnimator animator = new RenderNodeAnimator(mHwCirclePaint, RenderNodeAnimator.PAINT_ALPHA, 0); animator.setDuration(duration); - animator.setInterpolator(PhoneStatusBar.ALPHA_OUT); + animator.setInterpolator(Interpolators.ALPHA_OUT); animator.setTarget(this); animator.start(); } @@ -352,8 +344,8 @@ public class KeyguardAffordanceView extends ImageView { cancelAnimator(mPreviewClipper); ValueAnimator animator = getAnimatorToRadius(circleRadius); Interpolator interpolator = circleRadius == 0.0f - ? mDisappearInterpolator - : mAppearInterpolator; + ? Interpolators.FAST_OUT_LINEAR_IN + : Interpolators.LINEAR_OUT_SLOW_IN; animator.setInterpolator(interpolator); long duration = 250; if (!slowAnimation) { @@ -438,8 +430,8 @@ public class KeyguardAffordanceView extends ImageView { animator.addListener(mScaleEndListener); if (interpolator == null) { interpolator = imageScale == 0.0f - ? mDisappearInterpolator - : mAppearInterpolator; + ? Interpolators.FAST_OUT_LINEAR_IN + : Interpolators.LINEAR_OUT_SLOW_IN; } animator.setInterpolator(interpolator); if (duration == -1) { @@ -501,8 +493,8 @@ public class KeyguardAffordanceView extends ImageView { animator.addListener(mAlphaEndListener); if (interpolator == null) { interpolator = alpha == 0.0f - ? mDisappearInterpolator - : mAppearInterpolator; + ? Interpolators.FAST_OUT_LINEAR_IN + : Interpolators.LINEAR_OUT_SLOW_IN; } animator.setInterpolator(interpolator); if (duration == -1) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java index 36cf906c4aa7..635e66dcb07f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java @@ -52,8 +52,6 @@ public class NotificationContentView extends FrameLayout { private static final int VISIBLE_TYPE_SINGLELINE = 3; private final Rect mClipBounds = new Rect(); - private final int mRoundRectRadius; - private final boolean mRoundRectClippingEnabled; private final int mMinContractedHeight; private final OnLayoutChangeListener mLayoutUpdater = new OnLayoutChangeListener() { @Override @@ -100,13 +98,6 @@ public class NotificationContentView extends FrameLayout { } }; - private final ViewOutlineProvider mOutlineProvider = new ViewOutlineProvider() { - @Override - public void getOutline(View view, Outline outline) { - outline.setRoundRect(0, 0, view.getWidth(), mUnrestrictedContentHeight, - mRoundRectRadius); - } - }; private OnClickListener mExpandClickListener; private boolean mBeforeN; private boolean mExpandable; @@ -116,14 +107,9 @@ public class NotificationContentView extends FrameLayout { public NotificationContentView(Context context, AttributeSet attrs) { super(context, attrs); mHybridViewManager = new HybridNotificationViewManager(getContext(), this); - mRoundRectRadius = getResources().getDimensionPixelSize( - R.dimen.notification_material_rounded_rect_radius); - mRoundRectClippingEnabled = getResources().getBoolean( - R.bool.config_notifications_round_rect_clipping); mMinContractedHeight = getResources().getDimensionPixelSize( R.dimen.min_notification_layout_height); reset(true); - setOutlineProvider(mOutlineProvider); } public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight) { @@ -253,7 +239,6 @@ public class NotificationContentView extends FrameLayout { mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child); selectLayout(false /* animate */, true /* force */); mContractedWrapper.setDark(mDark, false /* animate */, 0 /* delay */); - updateRoundRectClipping(); } public void setExpandedChild(View child) { @@ -267,7 +252,6 @@ public class NotificationContentView extends FrameLayout { mExpandedChild.addOnLayoutChangeListener(mLayoutUpdater); mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child); selectLayout(false /* animate */, true /* force */); - updateRoundRectClipping(); } public void setHeadsUpChild(View child) { @@ -281,7 +265,6 @@ public class NotificationContentView extends FrameLayout { mHeadsUpChild.addOnLayoutChangeListener(mLayoutUpdater); mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child); selectLayout(false /* animate */, true /* force */); - updateRoundRectClipping(); } @Override @@ -344,27 +327,6 @@ public class NotificationContentView extends FrameLayout { updateClipping(); } - private void updateRoundRectClipping() { - boolean enabled = needsRoundRectClipping(); - setClipToOutline(enabled); - } - - private boolean needsRoundRectClipping() { - if (!mRoundRectClippingEnabled) { - return false; - } - boolean needsForContracted = mContractedChild != null - && mContractedChild.getVisibility() == View.VISIBLE - && mContractedWrapper.needsRoundRectClipping(); - boolean needsForExpanded = mExpandedChild != null - && mExpandedChild.getVisibility() == View.VISIBLE - && mExpandedWrapper.needsRoundRectClipping(); - boolean needsForHeadsUp = mExpandedChild != null - && mExpandedChild.getVisibility() == View.VISIBLE - && mExpandedWrapper.needsRoundRectClipping(); - return needsForContracted || needsForExpanded || needsForHeadsUp; - } - private void updateClipping() { if (mClipToActualHeight) { mClipBounds.set(0, mClipTopAmount, getWidth(), mContentHeight); @@ -412,7 +374,6 @@ public class NotificationContentView extends FrameLayout { boolean singleLineVisible = visibleType == VISIBLE_TYPE_SINGLELINE; mSingleLineView.setVisible(singleLineVisible); } - updateRoundRectClipping(); } private void animateToVisibleType(int visibleType) { @@ -426,7 +387,6 @@ public class NotificationContentView extends FrameLayout { hiddenView.setVisible(false); } }); - updateRoundRectClipping(); } /** @@ -553,7 +513,6 @@ public class NotificationContentView extends FrameLayout { if (mHeadsUpChild != null) { mHeadsUpWrapper.notifyContentUpdated(entry.notification); } - updateRoundRectClipping(); } private void updateSingleLineView() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java index e4cd7d9f5276..5abd1d5c4b03 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java @@ -206,13 +206,8 @@ public class NotificationGuts extends LinearLayout { void saveImportance(final StatusBarNotification sbn) { int progress = mSeekBar.getProgress(); try { - if (mApplyToTopic.isChecked()) { - mINotificationManager.setTopicImportance(sbn.getPackageName(), sbn.getUid(), mTopic, - progress); - } else { - mINotificationManager.setAppImportance( - sbn.getPackageName(), sbn.getUid(), progress); - } + mINotificationManager.setImportance(sbn.getPackageName(), sbn.getUid(), + mApplyToTopic.isChecked() ? mTopic : null, progress); } catch (RemoteException e) { // :( } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java index 682676b4cc5f..4e3ecb1c63b0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java @@ -22,7 +22,10 @@ import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Paint; import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; import android.view.animation.Interpolator; @@ -32,11 +35,14 @@ import android.view.animation.Interpolator; */ public class ScrimView extends View { + private final Paint mPaint = new Paint(); private int mScrimColor; private boolean mIsEmpty = true; private boolean mDrawAsSrc; private float mViewAlpha = 1.0f; private ValueAnimator mAlphaAnimator; + private Rect mExcludedRect = new Rect(); + private boolean mHasExcludedArea; private ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener = new ValueAnimator.AnimatorUpdateListener() { @Override @@ -51,6 +57,7 @@ public class ScrimView extends View mAlphaAnimator = null; } }; + private Runnable mChangeRunnable; public ScrimView(Context context) { this(context, null); @@ -72,15 +79,43 @@ public class ScrimView extends View protected void onDraw(Canvas canvas) { if (mDrawAsSrc || (!mIsEmpty && mViewAlpha > 0f)) { PorterDuff.Mode mode = mDrawAsSrc ? PorterDuff.Mode.SRC : PorterDuff.Mode.SRC_OVER; - int color = mScrimColor; - color = Color.argb((int) (Color.alpha(color) * mViewAlpha), Color.red(color), - Color.green(color), Color.blue(color)); - canvas.drawColor(color, mode); + int color = getScrimColorWithAlpha(); + if (!mHasExcludedArea) { + canvas.drawColor(color, mode); + } else { + mPaint.setColor(color); + if (mExcludedRect.top > 0) { + canvas.drawRect(0, 0, getWidth(), mExcludedRect.top, mPaint); + } + if (mExcludedRect.left > 0) { + canvas.drawRect(0, mExcludedRect.top, mExcludedRect.left, mExcludedRect.bottom, + mPaint); + } + if (mExcludedRect.right < getWidth()) { + canvas.drawRect(mExcludedRect.right, + mExcludedRect.top, + getWidth(), + mExcludedRect.bottom, + mPaint); + } + if (mExcludedRect.bottom < getHeight()) { + canvas.drawRect(0, mExcludedRect.bottom, getWidth(), getHeight(), mPaint); + } + } } } + public int getScrimColorWithAlpha() { + int color = mScrimColor; + color = Color.argb((int) (Color.alpha(color) * mViewAlpha), Color.red(color), + Color.green(color), Color.blue(color)); + return color; + } + public void setDrawAsSrc(boolean asSrc) { mDrawAsSrc = asSrc; + mPaint.setXfermode(new PorterDuffXfermode(mDrawAsSrc ? PorterDuff.Mode.SRC + : PorterDuff.Mode.SRC_OVER)); invalidate(); } @@ -89,6 +124,9 @@ public class ScrimView extends View mIsEmpty = Color.alpha(color) == 0; mScrimColor = color; invalidate(); + if (mChangeRunnable != null) { + mChangeRunnable.run(); + } } } @@ -105,8 +143,13 @@ public class ScrimView extends View if (mAlphaAnimator != null) { mAlphaAnimator.cancel(); } - mViewAlpha = alpha; - invalidate(); + if (alpha != mViewAlpha) { + mViewAlpha = alpha; + invalidate(); + if (mChangeRunnable != null) { + mChangeRunnable.run(); + } + } } public void animateViewAlpha(float alpha, long durationOut, Interpolator interpolator) { @@ -120,4 +163,23 @@ public class ScrimView extends View mAlphaAnimator.setDuration(durationOut); mAlphaAnimator.start(); } + + public void setExcludedArea(Rect area) { + if (area == null) { + mHasExcludedArea = false; + invalidate(); + return; + } + area.left = Math.max(area.left, 0); + area.top = Math.max(area.top, 0); + area.right = Math.min(area.right, getWidth()); + area.bottom = Math.min(area.bottom, getHeight()); + mExcludedRect.set(area); + mHasExcludedArea = area.left < area.right && area.top < area.bottom; + invalidate(); + } + + public void setChangeRunnable(Runnable changeRunnable) { + mChangeRunnable = changeRunnable; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java deleted file mode 100644 index 1fc874479f72..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.statusbar; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; -import com.android.systemui.R; - -/** - * The view representing the separation between important and less important notifications - */ -public class SpeedBumpView extends ExpandableView { - - private final int mSpeedBumpHeight; - private AlphaOptimizedView mLine; - private boolean mIsVisible = true; - private final Interpolator mFastOutSlowInInterpolator; - - public SpeedBumpView(Context context, AttributeSet attrs) { - super(context, attrs); - mSpeedBumpHeight = getResources() - .getDimensionPixelSize(R.dimen.speed_bump_height); - mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(), - android.R.interpolator.fast_out_slow_in); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - mLine = (AlphaOptimizedView) findViewById(R.id.speedbump_line); - } - - @Override - protected int getInitialHeight() { - return mSpeedBumpHeight; - } - - @Override - public int getIntrinsicHeight() { - return mSpeedBumpHeight; - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - mLine.setPivotX(mLine.getWidth() / 2); - mLine.setPivotY(mLine.getHeight() / 2); - setOutlineProvider(null); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - measureChildren(widthMeasureSpec, heightMeasureSpec); - int height = mSpeedBumpHeight; - setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), height); - } - - @Override - public boolean isTransparent() { - return true; - } - - public void performVisibilityAnimation(boolean nowVisible, long delay) { - animateDivider(nowVisible, delay, null /* onFinishedRunnable */); - } - - /** - * Animate the divider to a new visibility. - * - * @param nowVisible should it now be visible - * @param delay the delay after the animation should start - * @param onFinishedRunnable A runnable which should be run when the animation is - * finished. - */ - public void animateDivider(boolean nowVisible, long delay, Runnable onFinishedRunnable) { - if (nowVisible != mIsVisible) { - // Animate dividers - float endValue = nowVisible ? 1.0f : 0.0f; - mLine.animate() - .alpha(endValue) - .setStartDelay(delay) - .scaleX(endValue) - .scaleY(endValue) - .setInterpolator(mFastOutSlowInInterpolator) - .withEndAction(onFinishedRunnable); - mIsVisible = nowVisible; - } else { - if (onFinishedRunnable != null) { - onFinishedRunnable.run(); - } - } - } - - public void setInvisible() { - mLine.setAlpha(0.0f); - mLine.setScaleX(0.0f); - mLine.setScaleY(0.0f); - mIsVisible = false; - } - - @Override - public void performRemoveAnimation(long duration, float translationDirection, - Runnable onFinishedRunnable) { - // TODO: Use duration - performVisibilityAnimation(false, 0 /* delay */); - } - - @Override - public void performAddAnimation(long delay, long duration) { - // TODO: Use duration - performVisibilityAnimation(true, delay); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java index 2f66c41ada8f..c8366371a58c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java @@ -21,8 +21,6 @@ import android.util.AttributeSet; import android.view.View; import android.view.animation.Interpolator; -import com.android.systemui.statusbar.phone.PhoneStatusBar; - /** * A common base class for all views in the notification stack scroller which don't have a * background. @@ -80,9 +78,9 @@ public abstract class StackScrollerDecorView extends ExpandableView { float endValue = nowVisible ? 1.0f : 0.0f; Interpolator interpolator; if (nowVisible) { - interpolator = PhoneStatusBar.ALPHA_IN; + interpolator = Interpolators.ALPHA_IN; } else { - interpolator = PhoneStatusBar.ALPHA_OUT; + interpolator = Interpolators.ALPHA_OUT; } mAnimating = true; mContent.animate() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java index 3e2c4c62ef74..bed64a3d1b34 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java @@ -39,6 +39,10 @@ import java.util.List; * the navigation buttons by updating arrays_car.xml appropriately in an overlay. */ class CarNavigationBarController { + private static final String EXTRA_FACET_CATEGORIES = "categories"; + private static final String EXTRA_FACET_PACKAGES = "packages"; + private static final String EXTRA_FACET_ID = "filter_id"; + private static final String EXTRA_FACET_LAUNCH_PICKER = "launch_picker"; // Each facet of the navigation bar maps to a set of package names or categories defined in // arrays_car.xml. Package names for a given facet are delimited by ";" @@ -64,6 +68,7 @@ class CarNavigationBarController { private List<CarNavigationButton> mNavButtons = new ArrayList<CarNavigationButton>(); private int mCurrentFacetIndex; + private String mCurrentPackageName; public CarNavigationBarController(Context context, CarNavigationBarView navBar, @@ -75,6 +80,7 @@ class CarNavigationBarController { } public void taskChanged(String packageName) { + mCurrentPackageName = packageName; // If the package name belongs to a filter, then highlight appropriate button in // the navigation bar. if (mFacetPackageMap.containsKey(packageName)) { @@ -121,7 +127,8 @@ class CarNavigationBarController { CarNavigationButton button = createNavButton(icon, i, hasLongpress); mNavButtons.add(button); - mNavBar.addButton(button, createNavButton(icon, i, hasLongpress)); + mNavBar.addButton(button, + createNavButton(icon, i, hasLongpress) /* lightsOutButton */); initFacetFilterMaps(i, facetPackageNames.getString(i).split(FACET_FILTER_DEMILITER), @@ -132,7 +139,7 @@ class CarNavigationBarController { } } - private void initFacetFilterMaps(int id, String[] packageNames, String[] categories){ + private void initFacetFilterMaps(int id, String[] packageNames, String[] categories) { mFacetCategories.add(categories); for (int i = 0; i < categories.length; i++) { mFacetCategoryMap.put(categories[i], id); @@ -234,7 +241,6 @@ class CarNavigationBarController { button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - setCurrentFacet(id); onFacetClicked(id); } }); @@ -245,13 +251,13 @@ class CarNavigationBarController { @Override public boolean onLongClick(View v) { onFacetLongClicked(id); - setCurrentFacet(id); return true; } }); } else { button.setLongClickable(false); } + return button; } @@ -262,14 +268,35 @@ class CarNavigationBarController { } private void onFacetClicked(int index) { - // TODO: determine what data to pass to the trampoline, so it can start - // the default app or the lens picker. - startActivity(mIntents.get(index)); + Intent intent = mIntents.get(index); + String packageName = intent.getPackage(); + + if (packageName == null) { + return; + } + + // Don't launch the lens picker if it's already running and the + // user clicks the same facet + if (packageName.equals(mCurrentPackageName) && index == mCurrentFacetIndex) { + return; + } + + intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories.get(index)); + intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages.get(index)); + // The facet is identified by the index in which it was added to the nav bar. + // This value can be used to determine which facet was selected + intent.putExtra(EXTRA_FACET_ID, Integer.toString(index)); + + // If the current facet is clicked, we want to launch the picker by default + // rather than the "preferred/last run" app. + intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, index == mCurrentFacetIndex); + + setCurrentFacet(index); + startActivity(intent); } private void onFacetLongClicked(int index) { - // TODO: determine what data to pass to the trampoline, so it can start - // the default app or the lens picker. + setCurrentFacet(index); startActivity(mLongPressIntents.get(index)); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java index 36b3a8ae816e..504f0595b48f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java @@ -18,8 +18,6 @@ package com.android.systemui.statusbar.car; import android.content.Context; import android.graphics.drawable.Drawable; import android.util.AttributeSet; -import android.widget.ImageButton; -import android.widget.ImageView; import android.widget.RelativeLayout; import com.android.keyguard.AlphaOptimizedImageButton; import com.android.systemui.R; @@ -43,13 +41,11 @@ public class CarNavigationButton extends RelativeLayout { super.onFinishInflate(); mIcon = (AlphaOptimizedImageButton) findViewById(R.id.car_nav_button_icon); mIcon.setClickable(false); - mIcon.setScaleType(ImageView.ScaleType.CENTER); mIcon.setBackgroundColor(android.R.color.transparent); mIcon.setAlpha(UNSELECTED_ALPHA); mMoreIcon = (AlphaOptimizedImageButton) findViewById(R.id.car_nav_button_more_icon); mMoreIcon.setClickable(false); - mMoreIcon.setScaleType(ImageView.ScaleType.CENTER); mMoreIcon.setBackgroundColor(android.R.color.transparent); mMoreIcon.setVisibility(INVISIBLE); mMoreIcon.setImageDrawable(getContext().getDrawable(R.drawable.car_ic_arrow)); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java index fd65aacdc320..97bf4b4c53ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java @@ -41,9 +41,4 @@ public class NotificationCustomViewWrapper extends NotificationViewWrapper { mInvertHelper.update(dark); } } - - @Override - public boolean needsRoundRectClipping() { - return true; - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java index 4fd4cab052e1..f43a5d0cfccd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java @@ -38,6 +38,7 @@ import android.widget.ImageView; import com.android.systemui.R; import com.android.systemui.ViewInvertHelper; +import com.android.systemui.statusbar.Interpolators; import com.android.systemui.statusbar.TransformableView; import com.android.systemui.statusbar.ViewTransformationHelper; import com.android.systemui.statusbar.phone.NotificationPanelView; @@ -55,7 +56,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { 0, PorterDuff.Mode.SRC_ATOP); private final int mIconDarkAlpha; private final int mIconDarkColor = 0xffffffff; - protected final Interpolator mLinearOutSlowInInterpolator; protected final ViewInvertHelper mInvertHelper; protected final ViewTransformationHelper mTransformationHelper; @@ -69,8 +69,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { protected NotificationHeaderViewWrapper(Context ctx, View view) { super(view); mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha); - mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(ctx, - android.R.interpolator.linear_out_slow_in); mInvertHelper = new ViewInvertHelper(ctx, NotificationPanelView.DOZE_ANIMATION_DURATION); mTransformationHelper = new ViewTransformationHelper(); resolveHeaderViews(); @@ -185,7 +183,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity); animator.addUpdateListener(updateListener); animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION); - animator.setInterpolator(mLinearOutSlowInInterpolator); + animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); animator.setStartDelay(delay); if (listener != null) { animator.addListener(listener); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java index a959e071619a..3475d13188fb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java @@ -26,6 +26,7 @@ import android.widget.ProgressBar; import android.widget.TextView; import com.android.systemui.statusbar.CrossFadeHelper; +import com.android.systemui.statusbar.Interpolators; import com.android.systemui.statusbar.TransformableView; import com.android.systemui.statusbar.ViewTransformationHelper; import com.android.systemui.statusbar.stack.StackStateAnimator; @@ -64,7 +65,7 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp - ownPosition[1]) * 0.33f) .setDuration( StackStateAnimator.ANIMATION_DURATION_STANDARD) - .setInterpolator(TransformState.FAST_OUT_SLOW_IN) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .withEndAction(new Runnable() { @Override public void run() { @@ -103,7 +104,7 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp .translationY(0) .setDuration( StackStateAnimator.ANIMATION_DURATION_STANDARD) - .setInterpolator(TransformState.FAST_OUT_SLOW_IN) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .withEndAction(new Runnable() { @Override public void run() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java index 0ceba7890786..a1cf07e83af5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java @@ -65,14 +65,6 @@ public abstract class NotificationViewWrapper implements TransformableView { public void notifyContentUpdated(StatusBarNotification notification) {}; /** - * @return true if this template might need to be clipped with a round rect to make it look - * nice, false otherwise - */ - public boolean needsRoundRectClipping() { - return false; - } - - /** * Update the appearance of the expand button. * * @param expandable should this view be expandable diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java index 388ba0e70bc9..3e1c40aa847d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java @@ -22,8 +22,6 @@ import android.view.NotificationHeaderView; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; -import android.view.animation.Interpolator; -import android.view.animation.PathInterpolator; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; @@ -31,6 +29,7 @@ import android.widget.TextView; import com.android.systemui.R; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.ExpandableNotificationRow; +import com.android.systemui.statusbar.Interpolators; import com.android.systemui.statusbar.stack.StackStateAnimator; /** @@ -44,7 +43,6 @@ public class TransformState { private static final int CLIP_CLIPPING_SET = R.id.clip_children_set_tag; private static final int CLIP_CHILDREN_TAG = R.id.clip_children_tag; private static final int CLIP_TO_PADDING = R.id.clip_to_padding_tag; - public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f); private static Pools.SimplePool<TransformState> sInstancePool = new Pools.SimplePool<>(40); protected View mTransformedView; @@ -110,7 +108,7 @@ public class TransformState { } } transformedView.animate() - .setInterpolator(TransformState.FAST_OUT_SLOW_IN) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD) .withEndAction(new Runnable() { @Override @@ -168,7 +166,7 @@ public class TransformState { .translationY(otherStablePosition[1] - ownPosition[1]); } transformedView.animate() - .setInterpolator(TransformState.FAST_OUT_SLOW_IN) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD) .withEndAction(new Runnable() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java index a3f404a84314..37e555834c5c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java @@ -32,6 +32,7 @@ import android.view.View; import android.view.animation.LinearInterpolator; import com.android.systemui.R; +import com.android.systemui.statusbar.Interpolators; public class BarTransitions { private static final boolean DEBUG = false; @@ -124,7 +125,6 @@ public class BarTransitions { private final int mTransparent; private final int mWarning; private final Drawable mGradient; - private final TimeInterpolator mInterpolator; private int mMode = -1; private boolean mAnimating; @@ -152,7 +152,6 @@ public class BarTransitions { mWarning = context.getColor(com.android.internal.R.color.battery_saver_mode_color); } mGradient = context.getDrawable(gradientResourceId); - mInterpolator = new LinearInterpolator(); } @Override @@ -222,7 +221,8 @@ public class BarTransitions { mGradientAlpha = targetGradientAlpha; } else { final float t = (now - mStartTime) / (float)(mEndTime - mStartTime); - final float v = Math.max(0, Math.min(mInterpolator.getInterpolation(t), 1)); + final float v = Math.max(0, Math.min( + Interpolators.LINEAR.getInterpolation(t), 1)); mGradientAlpha = (int)(v * targetGradientAlpha + mGradientAlphaStart * (1 - v)); mColor = Color.argb( (int)(v * Color.alpha(targetColor) + Color.alpha(mColorStart) * (1 - v)), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java index 3ff69c9d2fb6..b5dba18faf3c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java @@ -23,11 +23,11 @@ import android.annotation.NonNull; import android.content.Context; import android.os.Handler; import android.util.Log; -import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import com.android.systemui.doze.DozeHost; import com.android.systemui.doze.DozeLog; +import com.android.systemui.statusbar.Interpolators; /** * Controller which handles all the doze animations of the scrims. @@ -37,10 +37,6 @@ public class DozeScrimController { private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final DozeParameters mDozeParameters; - private final Interpolator mPulseInInterpolator = PhoneStatusBar.ALPHA_OUT; - private final Interpolator mPulseInInterpolatorPickup; - private final Interpolator mPulseOutInterpolator = PhoneStatusBar.ALPHA_IN; - private final Interpolator mDozeAnimationInterpolator; private final Handler mHandler = new Handler(); private final ScrimController mScrimController; @@ -55,8 +51,6 @@ public class DozeScrimController { public DozeScrimController(ScrimController scrimController, Context context) { mScrimController = scrimController; mDozeParameters = new DozeParameters(context); - mDozeAnimationInterpolator = mPulseInInterpolatorPickup = - AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in); } public void setDozing(boolean dozing, boolean animate) { @@ -70,9 +64,11 @@ public class DozeScrimController { cancelPulsing(); if (animate) { startScrimAnimation(false /* inFront */, 0f /* target */, - NotificationPanelView.DOZE_ANIMATION_DURATION, mDozeAnimationInterpolator); + NotificationPanelView.DOZE_ANIMATION_DURATION, + Interpolators.LINEAR_OUT_SLOW_IN); startScrimAnimation(true /* inFront */, 0f /* target */, - NotificationPanelView.DOZE_ANIMATION_DURATION, mDozeAnimationInterpolator); + NotificationPanelView.DOZE_ANIMATION_DURATION, + Interpolators.LINEAR_OUT_SLOW_IN); } else { abortAnimations(); mScrimController.setDozeBehindAlpha(0f); @@ -116,7 +112,7 @@ public class DozeScrimController { final boolean pickup = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP; startScrimAnimation(true /* inFront */, 0f, mDozeParameters.getPulseInDuration(pickup), - pickup ? mPulseInInterpolatorPickup : mPulseInInterpolator, + pickup ? Interpolators.LINEAR_OUT_SLOW_IN : Interpolators.ALPHA_OUT, mPulseInFinished); } } @@ -266,7 +262,7 @@ public class DozeScrimController { if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing); if (!mDozing) return; startScrimAnimation(true /* inFront */, 1f, mDozeParameters.getPulseOutDuration(), - mPulseOutInterpolator, mPulseOutFinished); + Interpolators.ALPHA_IN, mPulseOutFinished); } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/GetActivityIconTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/GetActivityIconTask.java deleted file mode 100644 index d2bec7c3be0d..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/GetActivityIconTask.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.phone; - -import android.app.AppGlobals; -import android.content.pm.ActivityInfo; -import android.content.pm.IPackageManager; -import android.content.pm.PackageManager; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Typeface; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.os.AsyncTask; -import android.os.RemoteException; -import android.util.Slog; -import android.widget.ImageView; - -/** - * Retrieves the icon for an activity and sets it as the Drawable on an ImageView. The ImageView - * is hidden if the activity isn't recognized or if there is no icon. - */ -class GetActivityIconTask extends AsyncTask<AppButtonData, Void, Drawable> { - private final static String TAG = "GetActivityIconTask"; - - private final PackageManager mPackageManager; - - // The ImageView that will receive the icon. - private final ImageView mImageView; - - public GetActivityIconTask(PackageManager packageManager, ImageView imageView) { - mPackageManager = packageManager; - mImageView = imageView; - } - - @Override - protected Drawable doInBackground(AppButtonData... params) { - if (params.length != 1) { - throw new IllegalArgumentException("Expected one parameter"); - } - AppButtonData buttonData = params[0]; - AppInfo appInfo = buttonData.appInfo; - try { - IPackageManager mPM = AppGlobals.getPackageManager(); - ActivityInfo ai = mPM.getActivityInfo( - appInfo.getComponentName(), - 0, - appInfo.getUser().getIdentifier()); - - if (ai == null) { - Slog.w(TAG, "Icon not found for " + appInfo); - return null; - } - - Drawable unbadgedIcon = ai.loadIcon(mPackageManager); - Drawable badgedIcon = - mPackageManager.getUserBadgedIcon(unbadgedIcon, appInfo.getUser()); - - if (NavigationBarApps.DEBUG) { - // Draw pinned indicator and number of running tasks. - Bitmap bitmap = Bitmap.createBitmap( - badgedIcon.getIntrinsicWidth(), - badgedIcon.getIntrinsicHeight(), - Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - badgedIcon.setBounds( - 0, 0, badgedIcon.getIntrinsicWidth(), badgedIcon.getIntrinsicHeight()); - badgedIcon.draw(canvas); - Paint paint = new Paint(); - paint.setStyle(Paint.Style.FILL); - if (buttonData.pinned) { - paint.setColor(Color.WHITE); - canvas.drawCircle(10, 10, 10, paint); - } - if (buttonData.tasks != null && buttonData.tasks.size() > 0) { - paint.setColor(Color.BLACK); - canvas.drawCircle(60, 30, 30, paint); - paint.setColor(Color.WHITE); - paint.setTextSize(50); - paint.setTypeface(Typeface.create("sans-serif", Typeface.BOLD)); - canvas.drawText(Integer.toString(buttonData.tasks.size()), 50, 50, paint); - } - badgedIcon = new BitmapDrawable(null, bitmap); - } - - return badgedIcon; - } catch (RemoteException e) { - Slog.w(TAG, "Icon not found for " + appInfo, e); - return null; - } - } - - @Override - protected void onPostExecute(Drawable icon) { - mImageView.setImageDrawable(icon); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java index 26abc488db2f..e7064e529848 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java @@ -43,7 +43,6 @@ public class HeadsUpTouchHelper implements Gefingerpoken { private boolean mCollapseSnoozes; private NotificationPanelView mPanel; private ExpandableNotificationRow mPickedChild; - private final int mNotificationsTopPadding; public HeadsUpTouchHelper(HeadsUpManager headsUpManager, NotificationStackScrollLayout stackScroller, @@ -54,8 +53,6 @@ public class HeadsUpTouchHelper implements Gefingerpoken { Context context = stackScroller.getContext(); final ViewConfiguration configuration = ViewConfiguration.get(context); mTouchSlop = configuration.getScaledTouchSlop(); - mNotificationsTopPadding = context.getResources() - .getDimensionPixelSize(R.dimen.notifications_top_padding); } public boolean isTrackingHeadsUp() { @@ -80,10 +77,6 @@ public class HeadsUpTouchHelper implements Gefingerpoken { mInitialTouchX = x; setTrackingHeadsUp(false); ExpandableView child = mStackScroller.getChildAtRawPosition(x, y); - if (child == null && y < mNotificationsTopPadding) { - // We should also allow drags from the margin above the heads up - child = mStackScroller.getChildAtRawPosition(x, y + mNotificationsTopPadding); - } mTouchingHeadsUpView = false; if (child instanceof ExpandableNotificationRow) { mPickedChild = (ExpandableNotificationRow) child; @@ -113,8 +106,7 @@ public class HeadsUpTouchHelper implements Gefingerpoken { mHeadsUpManager.unpinAll(); mPanel.setPanelScrimMinFraction((float) expandedHeight / mPanel.getMaxPanelHeight()); - mPanel.startExpandMotion(x, y, true /* startTracking */, expandedHeight - + mNotificationsTopPadding); + mPanel.startExpandMotion(x, y, true /* startTracking */, expandedHeight); mPanel.clearNotificattonEffects(); return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java index 41adeb587736..c220efede40b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java @@ -24,12 +24,11 @@ import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; import com.android.systemui.R; import com.android.systemui.classifier.FalsingManager; import com.android.systemui.statusbar.FlingAnimationUtils; +import com.android.systemui.statusbar.Interpolators; import com.android.systemui.statusbar.KeyguardAffordanceView; /** @@ -60,8 +59,6 @@ public class KeyguardAffordanceHelper { private KeyguardAffordanceView mLeftIcon; private KeyguardAffordanceView mCenterIcon; private KeyguardAffordanceView mRightIcon; - private Interpolator mAppearInterpolator; - private Interpolator mDisappearInterpolator; private Animator mSwipeAnimator; private FalsingManager mFalsingManager; private int mMinBackgroundRadius; @@ -107,10 +104,6 @@ public class KeyguardAffordanceHelper { mHintGrowAmount = mContext.getResources().getDimensionPixelSize(R.dimen.hint_grow_amount_sideways); mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.4f); - mAppearInterpolator = AnimationUtils.loadInterpolator(mContext, - android.R.interpolator.linear_out_slow_in); - mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext, - android.R.interpolator.fast_out_linear_in); mFalsingManager = FalsingManager.getInstance(mContext); } @@ -272,7 +265,7 @@ public class KeyguardAffordanceHelper { } } }); - animator.setInterpolator(mAppearInterpolator); + animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); animator.setDuration(HINT_PHASE1_DURATION); animator.start(); mSwipeAnimator = animator; @@ -292,7 +285,7 @@ public class KeyguardAffordanceHelper { onFinishedListener.run(); } }); - animator.setInterpolator(mDisappearInterpolator); + animator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); animator.setDuration(HINT_PHASE2_DURATION); animator.setStartDelay(HINT_CIRCLE_OPEN_DURATION); animator.start(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index 14176a6f5b3c..94d382902322 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -58,6 +58,7 @@ import com.android.systemui.EventLogTags; import com.android.systemui.R; import com.android.systemui.assist.AssistManager; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.Interpolators; import com.android.systemui.statusbar.KeyguardAffordanceView; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.policy.AccessibilityController; @@ -111,7 +112,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private AccessibilityController mAccessibilityController; private PhoneStatusBar mPhoneStatusBar; - private final Interpolator mLinearOutSlowInInterpolator; private boolean mUserSetupComplete; private boolean mPrewarmBound; private Messenger mPrewarmMessenger; @@ -146,8 +146,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - mLinearOutSlowInInterpolator = - AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in); } private AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() { @@ -600,7 +598,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mIndicationText.setAlpha(0f); mIndicationText.animate() .alpha(1f) - .setInterpolator(mLinearOutSlowInInterpolator) + .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN) .setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION); } @@ -610,7 +608,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL element.animate() .alpha(1f) .translationY(0f) - .setInterpolator(mLinearOutSlowInInterpolator) + .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN) .setStartDelay(delay) .setDuration(DOZE_ANIMATION_ELEMENT_DURATION); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index f41e47bf9e0e..e67aa8499e63 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -23,14 +23,13 @@ import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; import android.view.ViewTreeObserver; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import com.android.systemui.BatteryMeterView; import com.android.systemui.R; +import com.android.systemui.statusbar.Interpolators; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; import com.android.systemui.statusbar.policy.UserInfoController; @@ -58,7 +57,6 @@ public class KeyguardStatusBarView extends RelativeLayout private KeyguardUserSwitcher mKeyguardUserSwitcher; private int mSystemIconsSwitcherHiddenExpandedMargin; - private Interpolator mFastOutSlowInInterpolator; public KeyguardStatusBarView(Context context, AttributeSet attrs) { super(context, attrs); @@ -73,8 +71,6 @@ public class KeyguardStatusBarView extends RelativeLayout mBatteryLevel = (TextView) findViewById(R.id.battery_level); mCarrierLabel = (TextView) findViewById(R.id.keyguard_carrier_text); loadDimens(); - mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(), - android.R.interpolator.fast_out_slow_in); updateUserSwitcher(); } @@ -199,7 +195,7 @@ public class KeyguardStatusBarView extends RelativeLayout .translationX(0) .setDuration(400) .setStartDelay(userSwitcherHiding ? 300 : 0) - .setInterpolator(mFastOutSlowInInterpolator) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .start(); if (userSwitcherHiding) { getOverlay().add(mMultiUserSwitch); @@ -207,7 +203,7 @@ public class KeyguardStatusBarView extends RelativeLayout .alpha(0f) .setDuration(300) .setStartDelay(0) - .setInterpolator(PhoneStatusBar.ALPHA_OUT) + .setInterpolator(Interpolators.ALPHA_OUT) .withEndAction(new Runnable() { @Override public void run() { @@ -223,7 +219,7 @@ public class KeyguardStatusBarView extends RelativeLayout .alpha(1f) .setDuration(300) .setStartDelay(200) - .setInterpolator(PhoneStatusBar.ALPHA_IN); + .setInterpolator(Interpolators.ALPHA_IN); } return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java index 71267cd7c582..8717a15aeef1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java @@ -65,6 +65,10 @@ public class MultiUserSwitch extends FrameLayout implements View.OnClickListener setUserSwitcherController(qsPanel.getHost().getUserSwitcherController()); } + public boolean hasMultipleUsers() { + return mUserListener.getCount() != 0; + } + public void setUserSwitcherController(UserSwitcherController userSwitcherController) { mUserSwitcherController = userSwitcherController; registerListener(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java deleted file mode 100644 index ed6d94004ad8..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java +++ /dev/null @@ -1,1123 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.phone; - -import static android.app.ActivityManager.StackId.INVALID_STACK_ID; - -import android.animation.LayoutTransition; -import android.annotation.Nullable; -import android.app.ActivityManager; -import android.app.ActivityManager.RecentTaskInfo; -import android.app.ActivityManagerNative; -import android.app.ActivityOptions; -import android.app.IActivityManager; -import android.app.ITaskStackListener; -import android.content.BroadcastReceiver; -import android.content.ClipData; -import android.content.ClipDescription; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.os.Bundle; -import android.os.RemoteException; -import android.os.UserHandle; -import android.os.UserManager; -import android.util.AttributeSet; -import android.util.Slog; -import android.view.DragEvent; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.PopupMenu; -import android.widget.Toast; - -import com.android.internal.content.PackageMonitor; -import com.android.systemui.R; - -import java.util.ArrayList; -import java.util.List; - -/** - * Container for application icons that appear in the navigation bar. Their appearance is similar - * to the launcher hotseat. Clicking an icon launches or activates the associated activity. A long - * click will trigger a drag to allow the icons to be reordered. As an icon is dragged the other - * icons shift to make space for it to be dropped. These layout changes are animated. - * Navigation bar contains both pinned and unpinned apps: pinned in the left part, unpinned in the - * right part, with no separator in between. - */ -class NavigationBarApps extends LinearLayout - implements NavigationBarAppsModel.OnAppsChangedListener { - public final static boolean DEBUG = false; - private final static String TAG = "NavigationBarApps"; - - /** - * Intent extra to store user serial number. - */ - static final String EXTRA_PROFILE = "profile"; - - // There are separate NavigationBarApps view instances for landscape vs. portrait, but they - // share the data model. - private static NavigationBarAppsModel sAppsModel; - - private final PackageManager mPackageManager; - private final UserManager mUserManager; - private final LayoutInflater mLayoutInflater; - private final AppPackageMonitor mAppPackageMonitor; - private final WindowManager mWindowManager; - - - // This view has two roles: - // 1) If the drag started outside the pinned apps list, it is a placeholder icon with a null - // tag. - // 2) If the drag started inside the pinned apps list, it is the icon for the app being dragged - // with the associated AppInfo tag. - // The icon is set invisible for the duration of the drag, creating a visual space for a drop. - // When the user is not dragging this member is null. - private ImageView mDragView; - - private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (Intent.ACTION_USER_SWITCHED.equals(action)) { - int currentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); - onUserSwitched(currentUserId); - } else if (Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) { - UserHandle removedProfile = intent.getParcelableExtra(Intent.EXTRA_USER); - onManagedProfileRemoved(removedProfile); - } - } - }; - - // Layout params for the window that contains the anchor for the popup menus. - // We need to create a window for a popup menu because the NavBar window is too narrow and can't - // contain the menu. - private final WindowManager.LayoutParams mPopupAnchorLayoutParams; - // View that contains the anchor for popup menus. The view occupies the whole screen, and - // has a child that will be moved to make the menu to appear where we need it. - private final ViewGroup mPopupAnchor; - private final PopupMenu mPopupMenu; - - /** - * True if popup menu code is busy with a popup operation. - * Attempting to show a popup menu or to add menu items while it's returning true will - * corrupt/crash the app. - */ - private boolean mIsPopupInUse = false; - private final int [] mClickedIconLocation = new int[2]; - - public NavigationBarApps(Context context, AttributeSet attrs) { - super(context, attrs); - if (sAppsModel == null) { - sAppsModel = new NavigationBarAppsModel(context); - } - mPackageManager = context.getPackageManager(); - mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); - mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - mLayoutInflater = LayoutInflater.from(context); - mAppPackageMonitor = new AppPackageMonitor(); - - // Dragging an icon removes and adds back the dragged icon. Use the layout transitions to - // trigger animation. By default all transitions animate, so turn off the unneeded ones. - LayoutTransition transition = new LayoutTransition(); - // Don't trigger on disappear. Adding the view will trigger the layout animation. - transition.disableTransitionType(LayoutTransition.DISAPPEARING); - // Don't animate the dragged icon itself. - transition.disableTransitionType(LayoutTransition.APPEARING); - // When an icon is dragged off the shelf, start sliding the other icons over immediately - // to match the parent view's animation. - transition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0); - transition.setStagger(LayoutTransition.CHANGE_DISAPPEARING, 0); - setLayoutTransition(transition); - - TaskStackListener taskStackListener = new TaskStackListener(); - IActivityManager iam = ActivityManagerNative.getDefault(); - try { - iam.registerTaskStackListener(taskStackListener); - } catch (RemoteException e) { - Slog.e(TAG, "registerTaskStackListener failed", e); - } - - mPopupAnchorLayoutParams = - new WindowManager.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT, 0, 0, - WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY, - WindowManager.LayoutParams.FLAG_FULLSCREEN - | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED - | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, - PixelFormat.TRANSLUCENT); - mPopupAnchorLayoutParams.setTitle("ShelfMenuAnchor"); - - mPopupAnchor = (ViewGroup) mLayoutInflater.inflate(R.layout.shelf_menu_anchor, null); - - ImageView anchorButton = - (ImageView) mPopupAnchor.findViewById(R.id.shelf_menu_anchor_anchor); - mPopupMenu = new PopupMenu(context, anchorButton); - } - - // Monitor that catches events like "app uninstalled". - private class AppPackageMonitor extends PackageMonitor { - @Override - public void onPackageRemoved(String packageName, int uid) { - postUnpinIfUnlauncheable(packageName, new UserHandle(getChangingUserId())); - super.onPackageRemoved(packageName, uid); - } - - @Override - public void onPackageModified(String packageName) { - postUnpinIfUnlauncheable(packageName, new UserHandle(getChangingUserId())); - super.onPackageModified(packageName); - } - - @Override - public void onPackagesAvailable(String[] packages) { - if (isReplacing()) { - UserHandle user = new UserHandle(getChangingUserId()); - - for (String packageName : packages) { - postUnpinIfUnlauncheable(packageName, user); - } - } - super.onPackagesAvailable(packages); - } - - @Override - public void onPackagesUnavailable(String[] packages) { - if (!isReplacing()) { - UserHandle user = new UserHandle(getChangingUserId()); - - for (String packageName : packages) { - postUnpinIfUnlauncheable(packageName, user); - } - } - super.onPackagesUnavailable(packages); - } - } - - private void postUnpinIfUnlauncheable(final String packageName, final UserHandle user) { - // This method doesn't necessarily get called in the main thread. Redirect the call into - // the main thread. - post(new Runnable() { - @Override - public void run() { - if (!isAttachedToWindow()) return; - unpinIfUnlauncheable(packageName, user); - } - }); - } - - private void unpinIfUnlauncheable(String packageName, UserHandle user) { - // Unpin icons for all apps that match a package that perhaps became unlauncheable. - boolean appsWereUnpinned = false; - for(int i = getChildCount() - 1; i >= 0; --i) { - View child = getChildAt(i); - AppButtonData appButtonData = (AppButtonData)child.getTag(); - if (appButtonData == null) continue; // Skip the drag placeholder. - - if (!appButtonData.pinned) continue; - - AppInfo appInfo = appButtonData.appInfo; - if (!appInfo.getUser().equals(user)) continue; - - ComponentName appComponentName = appInfo.getComponentName(); - if (!appComponentName.getPackageName().equals(packageName)) continue; - - if (sAppsModel.resolveApp(appInfo) != null) { - continue; - } - - appButtonData.pinned = false; - appsWereUnpinned = true; - - if (appButtonData.isEmpty()) { - removeViewAt(i); - } - } - if (appsWereUnpinned) { - savePinnedApps(); - } - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - // When an icon is dragged out of the pinned area this view's width changes, which causes - // the parent container's layout to change and the divider and recents icons to shift left. - // Animate the parent's CHANGING transition. - ViewGroup parent = (ViewGroup) getParent(); - LayoutTransition transition = new LayoutTransition(); - transition.disableTransitionType(LayoutTransition.APPEARING); - transition.disableTransitionType(LayoutTransition.DISAPPEARING); - transition.disableTransitionType(LayoutTransition.CHANGE_APPEARING); - transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); - transition.enableTransitionType(LayoutTransition.CHANGING); - parent.setLayoutTransition(transition); - - sAppsModel.setCurrentUser(ActivityManager.getCurrentUser()); - recreatePinnedAppButtons(); - updateRecentApps(); - - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_USER_SWITCHED); - filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); - mContext.registerReceiver(mBroadcastReceiver, filter); - - mAppPackageMonitor.register(mContext, null, UserHandle.ALL, true); - sAppsModel.addOnAppsChangedListener(this); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - mContext.unregisterReceiver(mBroadcastReceiver); - mAppPackageMonitor.unregister(); - sAppsModel.removeOnAppsChangedListener(this); - } - - @Override - protected void onVisibilityChanged(View changedView, int visibility) { - super.onVisibilityChanged(changedView, visibility); - if (mIsPopupInUse && !isShown()) { - // Hide the popup if current view became invisible. - shutdownPopupMenu(); - } - } - - private void addAppButton(AppButtonData appButtonData) { - ImageView button = createAppButton(); - updateApp(button, appButtonData); - addView(button); - } - - private List<AppInfo> getPinnedApps() { - List<AppInfo> apps = new ArrayList<AppInfo>(); - int childCount = getChildCount(); - for (int i = 0; i != childCount; ++i) { - View child = getChildAt(i); - AppButtonData appButtonData = (AppButtonData)child.getTag(); - if (appButtonData == null) continue; // Skip the drag placeholder. - if(!appButtonData.pinned) continue; - apps.add(appButtonData.appInfo); - } - return apps; - } - - /** - * Creates an ImageView icon for each pinned app. Removes any existing icons. May be called - * to synchronize the current view with the shared data mode. - */ - private void recreatePinnedAppButtons() { - // Remove any existing icon buttons. - removeAllViews(); - - List<AppInfo> apps = sAppsModel.getApps(); - int appCount = apps.size(); - for (int i = 0; i < appCount; i++) { - AppInfo app = apps.get(i); - addAppButton(new AppButtonData(app, true /* pinned */)); - } - } - - /** - * Saves pinned apps stored in app icons into the data model. - */ - private void savePinnedApps() { - sAppsModel.setApps(getPinnedApps()); - } - - /** - * Creates a new ImageView for an app, inflated from R.layout.navigation_bar_app_item. - */ - private ImageView createAppButton() { - ImageView button = (ImageView) mLayoutInflater.inflate( - R.layout.navigation_bar_app_item, this, false /* attachToRoot */); - button.setOnHoverListener(new AppHoverListener()); - button.setOnClickListener(new AppClickListener()); - button.setOnContextClickListener(new AppContextClickListener()); - // TODO: Ripple effect. Use either KeyButtonRipple or the default ripple background. - button.setOnLongClickListener(new AppLongClickListener()); - button.setOnDragListener(new AppIconDragListener()); - return button; - } - - private class AppLongClickListener implements View.OnLongClickListener { - @Override - public boolean onLongClick(View v) { - mDragView = (ImageView) v; - AppButtonData appButtonData = (AppButtonData) v.getTag(); - startAppDrag(mDragView, appButtonData.appInfo); - return true; - } - } - - /** - * Returns the human-readable name for an activity's package or null. - * TODO: Cache the labels, perhaps in an LruCache. - */ - @Nullable - private CharSequence getAppLabel(AppInfo appInfo) { - NavigationBarAppsModel.ResolvedApp resolvedApp = sAppsModel.resolveApp(appInfo); - if (resolvedApp == null) return null; - - CharSequence unbadgedLabel = resolvedApp.ri.loadLabel(mPackageManager); - return mUserManager.getBadgedLabelForUser(unbadgedLabel, appInfo.getUser()); - } - - /** Helper function to start dragging an app icon (either pinned or recent). */ - static void startAppDrag(ImageView icon, AppInfo appInfo) { - // The drag data is an Intent to launch the activity. - Intent mainIntent = Intent.makeMainActivity(appInfo.getComponentName()); - UserManager userManager = - (UserManager) icon.getContext().getSystemService(Context.USER_SERVICE); - long userSerialNumber = userManager.getSerialNumberForUser(appInfo.getUser()); - mainIntent.putExtra(EXTRA_PROFILE, userSerialNumber); - ClipData dragData = ClipData.newIntent("", mainIntent); - // Use the ImageView to create the shadow. - View.DragShadowBuilder shadow = new AppIconDragShadowBuilder(icon); - // Use a global drag because the icon might be dragged into the launcher. - icon.startDrag(dragData, shadow, null /* myLocalState */, View.DRAG_FLAG_GLOBAL); - } - - @Override - public boolean dispatchDragEvent(DragEvent event) { - // ACTION_DRAG_ENTERED is handled by each individual app icon drag listener. - boolean childHandled = super.dispatchDragEvent(event); - - // Other drag types are handled once per drag by this view. This is handled explicitly - // because attaching a DragListener to this ViewGroup does not work -- the DragListener in - // the children consumes the drag events. - boolean handled = false; - switch (event.getAction()) { - case DragEvent.ACTION_DRAG_STARTED: - handled = onDragStarted(event); - break; - case DragEvent.ACTION_DRAG_ENDED: - handled = onDragEnded(); - break; - case DragEvent.ACTION_DROP: - handled = onDrop(event); - break; - case DragEvent.ACTION_DRAG_EXITED: - handled = onDragExited(); - break; - } - - return handled || childHandled; - } - - /** Returns true if a drag should be handled. */ - private static boolean canAcceptDrag(DragEvent event) { - // Poorly behaved apps might not provide a clip description. - if (event.getClipDescription() == null) { - return false; - } - // The event must contain an intent. - return event.getClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_INTENT); - } - - /** - * Sets up for a drag. Runs once per drag operation. Returns true if the data represents - * an app shortcut and will be accepted for a drop. - */ - private boolean onDragStarted(DragEvent event) { - if (DEBUG) Slog.d(TAG, "onDragStarted"); - - // Ensure that an app shortcut is being dragged. - if (!canAcceptDrag(event)) { - return false; - } - - // If there are no pinned apps this view will be collapsed, but the user still needs some - // empty space to use as a drag target. - if (getChildCount() == 0) { - mDragView = createPlaceholderDragView(0); - } - - // If this is an existing icon being reordered, hide the app icon. The drag shadow will - // continue to draw. - if (mDragView != null) { - mDragView.setVisibility(View.INVISIBLE); - } - - // Listen for the drag end event. - return true; - } - - /** - * Creates a blank icon-sized View to create an empty space during a drag. - */ - private ImageView createPlaceholderDragView(int index) { - ImageView button = createAppButton(); - addView(button, index); - return button; - } - - /** - * Returns initial index for a new app that doesn't exist in Shelf. - * Such apps get created by dragging them into Shelf from other apps or by dragging from Shelf - * and then back, or by removing from shelf as an intermediate step of pinning an app via menu. - * @param indexHint Initial proposed position for the item. - * @param isAppPinned True if the app being dragged is pinned. - */ - int getNewAppIndex(int indexHint, boolean isAppPinned) { - int i; - if (isAppPinned) { - // For a pinned app, find the rightmost position to the left of the target that has a - // pinned app. We'll insert to the right of that position. - for (i = indexHint; i > 0; --i) { - View v = getChildAt(i - 1); - AppButtonData targetButtonData = (AppButtonData) v.getTag(); - if (targetButtonData.pinned) break; - } - } else { - // For an unpinned app, find the leftmost position to the right of the target that has - // an unpinned app. We'll insert to the left of that position. - int childCount = getChildCount(); - for (i = indexHint; i < childCount; ++i) { - View v = getChildAt(i); - AppButtonData targetButtonData = (AppButtonData) v.getTag(); - if (!targetButtonData.pinned) break; - } - } - return i; - } - - /** - * Handles a drag entering an existing icon. Not implemented in the drag listener because it - * needs to use LinearLayout/ViewGroup methods. - */ - private void onDragEnteredIcon(View target) { - if (DEBUG) Slog.d(TAG, "onDragEntered " + indexOfChild(target)); - - int targetIndex = indexOfChild(target); - - // If the drag didn't start from an existing shelf icon, add an invisible placeholder to - // create empty space for the user to drag into. - if (mDragView == null) { - mDragView = createPlaceholderDragView(getNewAppIndex(targetIndex, true)); - return; - } - - // If the user is dragging on top of the original icon location, do nothing. - if (target == mDragView) { - return; - } - - // "Move" the dragged app by removing it and adding it back at the target location. - AppButtonData targetButtonData = (AppButtonData) target.getTag(); - int dragViewIndex = indexOfChild(mDragView); - AppButtonData dragViewButtonData = (AppButtonData) mDragView.getTag(); - // Calculating whether the dragged app is pinned. If the app came from outside if the shelf, - // in which case dragViewButtonData == null, it's a new app that we'll pin. Otherwise, the - // button data is defined, and we look whether that existing app is pinned. - boolean isAppPinned = dragViewButtonData == null || dragViewButtonData.pinned; - - if (dragViewIndex == -1) { - // Drag view exists, but is not a child, which means that the drag has started at or - // already visited shelf, then left it, and now is entering it again. - targetIndex = getNewAppIndex(targetIndex, isAppPinned); - } else if (dragViewIndex < targetIndex) { - // The dragged app is currently at the left of the view where the drag is. - // We shouldn't allow moving a pinned app to the right of the unpinned app. - if (!targetButtonData.pinned && isAppPinned) return; - } else { - // The dragged app is currently at the right of the view where the drag is. - // We shouldn't allow moving a unpinned app to the left of the pinned app. - if (targetButtonData.pinned && !isAppPinned) return; - } - - // This works, but is subtle: - // * If dragViewIndex > targetIndex then the dragged app is moving from right to left and - // the dragged app will be added in front of the target. - // * If dragViewIndex < targetIndex then the dragged app is moving from left to right. - // Removing the drag view will shift the later views one position to the left. Adding - // the view at targetIndex will therefore place the app *after* the target. - removeView(mDragView); - addView(mDragView, targetIndex); - } - - private boolean onDrop(DragEvent event) { - if (DEBUG) Slog.d(TAG, "onDrop"); - - // An earlier drag event might have canceled the drag. If so, there is nothing to do. - if (mDragView == null) { - return true; - } - - boolean dragResult = true; - AppInfo appInfo = getAppFromDragEvent(event); - if (appInfo == null) { - // This wasn't a valid drop. Clean up the placeholder. - removePlaceholderDragViewIfNeeded(); - dragResult = false; - } else if (mDragView.getTag() == null) { - // This is a drag that adds a new app. Convert the placeholder to a real icon. - updateApp(mDragView, new AppButtonData(appInfo, true /* pinned */)); - } - endDrag(); - return dragResult; - } - - /** Cleans up at the end of a drag. */ - private void endDrag() { - // An earlier drag event might have canceled the drag. If so, there is nothing to do. - if (mDragView == null) return; - - mDragView.setVisibility(View.VISIBLE); - mDragView = null; - savePinnedApps(); - // Add recent tasks to the info of the potentially added app. - updateRecentApps(); - } - - /** Returns an app info from a DragEvent, or null if the data wasn't valid. */ - private AppInfo getAppFromDragEvent(DragEvent event) { - ClipData data = event.getClipData(); - if (data == null) { - return null; - } - if (data.getItemCount() != 1) { - return null; - } - ClipData.Item item = data.getItemAt(0); - if (item == null) { - return null; - } - Intent intent = item.getIntent(); - if (intent == null) { - return null; - } - long userSerialNumber = intent.getLongExtra(EXTRA_PROFILE, -1); - if (userSerialNumber == -1) { - return null; - } - UserHandle appUser = mUserManager.getUserForSerialNumber(userSerialNumber); - if (appUser == null) { - return null; - } - ComponentName componentName = intent.getComponent(); - if (componentName == null) { - return null; - } - AppInfo appInfo = new AppInfo(componentName, appUser); - if (sAppsModel.resolveApp(appInfo) == null) { - return null; - } - return appInfo; - } - - /** Updates the app at a given view index. */ - private void updateApp(ImageView button, AppButtonData appButtonData) { - CharSequence appLabel = getAppLabel(appButtonData.appInfo); - button.setContentDescription(appLabel); - - button.setTag(appButtonData); - new GetActivityIconTask(mPackageManager, button).execute(appButtonData); - } - - /** Removes the empty placeholder view. */ - private void removePlaceholderDragViewIfNeeded() { - // If the drag has ended already there is nothing to do. - if (mDragView == null) { - return; - } - removeView(mDragView); - } - - /** Cleans up at the end of the drag. */ - private boolean onDragEnded() { - if (DEBUG) Slog.d(TAG, "onDragEnded"); - // If the icon wasn't already dropped into the app list then remove the placeholder. - removePlaceholderDragViewIfNeeded(); - endDrag(); - return true; - } - - /** Handles the dragged icon exiting the bounds of this view during the drag. */ - private boolean onDragExited() { - if (DEBUG) Slog.d(TAG, "onDragExited"); - // Remove the placeholder. It will be added again if the user drags the icon back over - // the shelf. - removePlaceholderDragViewIfNeeded(); - return true; - } - - /** Drag listener for individual app icons. */ - private class AppIconDragListener implements View.OnDragListener { - @Override - public boolean onDrag(View v, DragEvent event) { - switch (event.getAction()) { - case DragEvent.ACTION_DRAG_STARTED: { - // Every button listens for drag events in order to detect enter/exit. - return canAcceptDrag(event); - } - case DragEvent.ACTION_DRAG_ENTERED: { - // Forward to NavigationBarApps. - onDragEnteredIcon(v); - return false; - } - } - return false; - } - } - - /** - * Brings the menu popup to closed state. - * Can be called at any stage of the asynchronous process of showing a menu. - */ - private void shutdownPopupMenu() { - mWindowManager.removeView(mPopupAnchor); - mPopupMenu.dismiss(); - } - - /** - * Shows already prepopulated popup menu using appIcon for anchor location. - */ - private void showPopupMenu(ImageView appIcon) { - // Movable view inside the popup anchor view. It serves as the actual anchor for the - // menu. - final ImageView anchorButton = - (ImageView) mPopupAnchor.findViewById(R.id.shelf_menu_anchor_anchor); - // Set same drawable as for the clicked button to have same size. - anchorButton.setImageDrawable(appIcon.getDrawable()); - - // Move the anchor button to the position of the app button. - appIcon.getLocationOnScreen(mClickedIconLocation); - anchorButton.setTranslationX(mClickedIconLocation[0]); - anchorButton.setTranslationY(mClickedIconLocation[1]); - - final OnAttachStateChangeListener onAttachStateChangeListener = - new OnAttachStateChangeListener() { - @Override - public void onViewAttachedToWindow(View v) { - mPopupMenu.show(); - } - - @Override - public void onViewDetachedFromWindow(View v) {} - }; - anchorButton.addOnAttachStateChangeListener(onAttachStateChangeListener); - - mPopupMenu.setOnDismissListener(new PopupMenu.OnDismissListener() { - @Override - public void onDismiss(PopupMenu menu) { - // FYU: thorough testing for closing menu either by the user or via - // shutdownPopupMenu() called at various moments of the menu creation, revealed that - // 'onDismiss' is guaranteed to be called after each invocation of showPopupMenu. - mWindowManager.removeView(mPopupAnchor); - anchorButton.removeOnAttachStateChangeListener(onAttachStateChangeListener); - mPopupMenu.setOnDismissListener(null); - mPopupMenu.getMenu().clear(); - mIsPopupInUse = false; - } - }); - - mWindowManager.addView(mPopupAnchor, mPopupAnchorLayoutParams); - mIsPopupInUse = true; - } - - private void activateTask(int taskPersistentId) { - // Launch or bring the activity to front. - final IActivityManager iAm = ActivityManagerNative.getDefault(); - try { - iAm.startActivityFromRecents(taskPersistentId, null /* options */); - } catch (RemoteException e) { - Slog.e(TAG, "Exception when activating a recent task", e); - } catch (IllegalArgumentException e) { - Slog.e(TAG, "Exception when activating a recent task", e); - } - } - - /** - * Adds to the popup menu items for activating each of tasks in the specified list. - */ - private void populateLaunchMenu(AppButtonData appButtonData) { - Menu menu = mPopupMenu.getMenu(); - int taskCount = appButtonData.getTaskCount(); - for (int i = 0; i < taskCount; ++i) { - final RecentTaskInfo taskInfo = appButtonData.tasks.get(i); - MenuItem item = menu.add(getActivityForTask(taskInfo).flattenToShortString()); - item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - activateTask(taskInfo.persistentId); - return true; - } - }); - } - } - - /** - * Shows a task selection menu for clicked or hovered-over apps that have more than 1 running - * tasks. - */ - void maybeShowLaunchMenu(ImageView appIcon) { - if (mIsPopupInUse) return; - AppButtonData appButtonData = (AppButtonData) appIcon.getTag(); - if (appButtonData.getTaskCount() <= 1) return; - - populateLaunchMenu(appButtonData); - showPopupMenu(appIcon); - } - - /** - * A listener for hovering over an app icon. - */ - private class AppHoverListener implements View.OnHoverListener { - private final long DELAY_MILLIS = 1000; - private Runnable mShowMenuCallback; - - @Override - public boolean onHover(final View v, MotionEvent event) { - if (mShowMenuCallback == null) { - mShowMenuCallback = new Runnable() { - @Override - public void run() { - maybeShowLaunchMenu((ImageView) v); - } - }; - } - - switch (event.getAction()) { - case MotionEvent.ACTION_HOVER_ENTER: - postDelayed(mShowMenuCallback, DELAY_MILLIS); - break; - case MotionEvent.ACTION_HOVER_EXIT: - removeCallbacks(mShowMenuCallback); - break; - } - return false; - } - } - - /** - * A click listener that launches an activity. - */ - private class AppClickListener implements View.OnClickListener { - private void launchApp(AppInfo appInfo, View anchor) { - NavigationBarAppsModel.ResolvedApp resolvedApp = sAppsModel.resolveApp(appInfo); - if (resolvedApp == null) { - Toast.makeText( - getContext(), R.string.activity_not_found, Toast.LENGTH_SHORT).show(); - return; - } - - Intent launchIntent = resolvedApp.launchIntent; - - // Play a scale-up animation while launching the activity. - // TODO: Consider playing a different animation, or no animation, if the activity is - // already open in a visible window. In that case we should move the task to front - // with minimal animation, perhaps using ActivityManager.moveTaskToFront(). - Rect sourceBounds = new Rect(); - anchor.getBoundsOnScreen(sourceBounds); - ActivityOptions opts = - ActivityOptions.makeScaleUpAnimation( - anchor, 0, 0, anchor.getWidth(), anchor.getHeight()); - Bundle optsBundle = opts.toBundle(); - launchIntent.setSourceBounds(sourceBounds); - - mContext.startActivityAsUser(launchIntent, optsBundle, appInfo.getUser()); - } - - @Override - public void onClick(View v) { - AppButtonData appButtonData = (AppButtonData) v.getTag(); - - if (appButtonData.getTaskCount() == 0) { - launchApp(appButtonData.appInfo, v); - } else { - // Activate latest task. - activateTask(appButtonData.tasks.get(0).persistentId); - - maybeShowLaunchMenu((ImageView) v); - } - } - } - - /** - * Context click listener that shows app's context menu. - */ - private class AppContextClickListener implements View.OnContextClickListener { - void updateState(ImageView appIcon) { - savePinnedApps(); - if (DEBUG) { - AppButtonData appButtonData = (AppButtonData) appIcon.getTag(); - new GetActivityIconTask(mPackageManager, appIcon).execute(appButtonData); - } - } - - /** - * Adds to the popup menu items for pinning and unpinning the app in the shelf. - */ - void populateContextMenu(final ImageView appIcon) { - final AppButtonData appButtonData = (AppButtonData) appIcon.getTag(); - Menu menu = mPopupMenu.getMenu(); - if (appButtonData.pinned) { - menu.add("Unpin"). - setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - appButtonData.pinned = false; - removeView(appIcon); - if (!appButtonData.isEmpty()) { - // If the app has running tasks, re-add it to the end of shelf - // after unpinning. - addView(appIcon); - } - updateState(appIcon); - return true; - } - }); - } else { - menu.add("Pin").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - appButtonData.pinned = true; - removeView(appIcon); - // Re-add the pinned icon to the end of the pinned list. - addView(appIcon, getNewAppIndex(getChildCount(), true)); - updateState(appIcon); - return true; - } - }); - } - } - - @Override - public boolean onContextClick(View v) { - if (mIsPopupInUse) return true; - ImageView appIcon = (ImageView) v; - populateContextMenu(appIcon); - showPopupMenu(appIcon); - return true; - } - } - - private void onUserSwitched(int currentUserId) { - sAppsModel.setCurrentUser(currentUserId); - recreatePinnedAppButtons(); - } - - private void onManagedProfileRemoved(UserHandle removedProfile) { - // Unpin apps from the removed profile. - boolean itemsWereUnpinned = false; - for(int i = getChildCount() - 1; i >= 0; --i) { - View view = getChildAt(i); - AppButtonData appButtonData = (AppButtonData)view.getTag(); - if (appButtonData == null) return; // Skip the drag placeholder. - if (!appButtonData.pinned) continue; - if (!appButtonData.appInfo.getUser().equals(removedProfile)) continue; - - appButtonData.pinned = false; - itemsWereUnpinned = true; - if (appButtonData.isEmpty()) { - removeViewAt(i); - } - } - if (itemsWereUnpinned) { - savePinnedApps(); - } - } - - /** - * Returns app data for a button that matches the provided app info, if it exists, or null - * otherwise. - */ - private AppButtonData findAppButtonData(AppInfo appInfo) { - int size = getChildCount(); - for (int i = 0; i < size; ++i) { - View view = getChildAt(i); - AppButtonData appButtonData = (AppButtonData)view.getTag(); - if (appButtonData == null) continue; // Skip the drag placeholder. - if (appButtonData.appInfo.equals(appInfo)) { - return appButtonData; - } - } - return null; - } - - private void updateTasks(List<RecentTaskInfo> tasks) { - // Remove tasks from all app buttons. - for (int i = getChildCount() - 1; i >= 0; --i) { - View view = getChildAt(i); - AppButtonData appButtonData = (AppButtonData)view.getTag(); - if (appButtonData == null) return; // Skip the drag placeholder. - appButtonData.clearTasks(); - } - - // Re-add tasks to app buttons, adding new buttons if needed. - int size = tasks.size(); - for (int i = 0; i != size; ++i) { - RecentTaskInfo task = tasks.get(i); - AppInfo taskAppInfo = taskToAppInfo(task); - if (taskAppInfo == null) continue; - AppButtonData appButtonData = findAppButtonData(taskAppInfo); - if (appButtonData == null) { - appButtonData = new AppButtonData(taskAppInfo, false); - addAppButton(appButtonData); - } - appButtonData.addTask(task); - } - - // Remove unpinned apps that now have no tasks. - for (int i = getChildCount() - 1; i >= 0; --i) { - View view = getChildAt(i); - AppButtonData appButtonData = (AppButtonData)view.getTag(); - if (appButtonData == null) return; // Skip the drag placeholder. - if (appButtonData.isEmpty()) { - removeViewAt(i); - } - } - - if (DEBUG) { - for (int i = getChildCount() - 1; i >= 0; --i) { - View view = getChildAt(i); - AppButtonData appButtonData = (AppButtonData)view.getTag(); - if (appButtonData == null) return; // Skip the drag placeholder. - new GetActivityIconTask(mPackageManager, (ImageView )view).execute(appButtonData); - - } - } - } - - private void updateRecentApps() { - ActivityManager activityManager = - (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); - // TODO: Should this be getRunningTasks? - List<RecentTaskInfo> recentTasks = activityManager.getRecentTasksForUser( - ActivityManager.getMaxAppRecentsLimitStatic(), - ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS | - ActivityManager.RECENT_IGNORE_UNAVAILABLE | - ActivityManager.RECENT_INCLUDE_PROFILES, - UserHandle.USER_CURRENT); - if (DEBUG) Slog.d(TAG, "Got recents " + recentTasks.size()); - updateTasks(recentTasks); - } - - private static ComponentName getActivityForTask(RecentTaskInfo task) { - // If the task was started from an alias, return the actual activity component that was - // initially started. - if (task.origActivity != null) { - return task.origActivity; - } - // Prefer the first activity of the task. - if (task.baseActivity != null) { - return task.baseActivity; - } - // Then goes the activity that started the task. - if (task.realActivity != null) { - return task.realActivity; - } - // This should not happen, but fall back to the base intent's activity component name. - return task.baseIntent.getComponent(); - } - - private ComponentName getLaunchComponentForPackage(String packageName, int userId) { - // This code is based on ApplicationPackageManager.getLaunchIntentForPackage. - PackageManager packageManager = mContext.getPackageManager(); - - // First see if the package has an INFO activity; the existence of - // such an activity is implied to be the desired front-door for the - // overall package (such as if it has multiple launcher entries). - Intent intentToResolve = new Intent(Intent.ACTION_MAIN); - intentToResolve.addCategory(Intent.CATEGORY_INFO); - intentToResolve.setPackage(packageName); - List<ResolveInfo> ris = packageManager.queryIntentActivitiesAsUser( - intentToResolve, 0, userId); - - // Otherwise, try to find a main launcher activity. - if (ris == null || ris.size() <= 0) { - // reuse the intent instance - intentToResolve.removeCategory(Intent.CATEGORY_INFO); - intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER); - intentToResolve.setPackage(packageName); - ris = packageManager.queryIntentActivitiesAsUser(intentToResolve, 0, userId); - } - if (ris == null || ris.size() <= 0) { - Slog.i(TAG, "Failed to build intent for " + packageName); - return null; - } - return new ComponentName(ris.get(0).activityInfo.packageName, - ris.get(0).activityInfo.name); - } - - private AppInfo taskToAppInfo(RecentTaskInfo task) { - ComponentName componentName = getActivityForTask(task); - UserHandle taskUser = new UserHandle(task.userId); - AppInfo appInfo = new AppInfo(componentName, taskUser); - - if (sAppsModel.resolveApp(appInfo) == null) { - // If task's activity is not launcheable, fall back to a launch component of the - // task's package. - ComponentName component = getLaunchComponentForPackage( - componentName.getPackageName(), task.userId); - - if (component == null) { - return null; - } - - appInfo = new AppInfo(component, taskUser); - } - - return appInfo; - } - - /** - * A listener that updates the app buttons whenever the recents task stack changes. - */ - private class TaskStackListener extends ITaskStackListener.Stub { - @Override - public void onTaskStackChanged() throws RemoteException { - // Post the message back to the UI thread. - post(new Runnable() { - @Override - public void run() { - if (isAttachedToWindow()) { - updateRecentApps(); - } - } - }); - } - - @Override - public void onActivityPinned() { - } - - @Override - public void onPinnedActivityRestartAttempt() { - } - } - - @Override - public void onPinnedAppsChanged() { - if (getPinnedApps().equals(sAppsModel.getApps())) return; - recreatePinnedAppButtons(); - updateRecentApps(); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java deleted file mode 100644 index 76a97984825f..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.phone; - -import android.app.AppGlobals; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.ActivityInfo; -import android.content.pm.IPackageManager; -import android.content.pm.ResolveInfo; -import android.content.pm.UserInfo; -import android.os.RemoteException; -import android.os.UserHandle; -import android.os.UserManager; -import android.util.Slog; - -import com.android.internal.annotations.VisibleForTesting; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Data model and controller for app icons appearing in the navigation bar. The data is stored on - * disk in SharedPreferences. Each icon has a separate pref entry consisting of a flattened - * ComponentName. - */ -class NavigationBarAppsModel { - public interface OnAppsChangedListener { - void onPinnedAppsChanged(); - } - - public class ResolvedApp { - Intent launchIntent; - ResolveInfo ri; - } - - private final static String TAG = "NavigationBarAppsModel"; - - // Default number of apps to load initially. - private final static int NUM_INITIAL_APPS = 4; - - // Preferences file name. - private final static String SHARED_PREFERENCES_NAME = "com.android.systemui.navbarapps"; - - // Preference name for the version of the other preferences. - private final static String VERSION_PREF = "version"; - - // Current version number for preferences. - private final static int CURRENT_VERSION = 3; - - // Preference name for the number of app icons. - private final static String APP_COUNT_PREF = "app_count"; - - // Preference name prefix for each app's info. The actual pref has an integer appended to it. - private final static String APP_PREF_PREFIX = "app_"; - - // User serial number prefix for each app's info. The actual pref has an integer appended to it. - private final static String APP_USER_PREFIX = "app_user_"; - - // Character separating current user serial number from the user-specific part of a pref. - // Example "22|app_user_2" - when logged as user with serial 22, we'll use this pref for the - // user serial of the third app of the logged-in user. - private final static char USER_SEPARATOR = '|'; - - private final Context mContext; - private final UserManager mUserManager; - private final SharedPreferences mPrefs; - - // Apps are represented as an ordered list of app infos. - private List<AppInfo> mApps = new ArrayList<AppInfo>(); - - private List<OnAppsChangedListener> mOnAppsChangedListeners = - new ArrayList<OnAppsChangedListener>(); - - // Id of the current user. - private int mCurrentUserId = -1; - - // Serial number of the current user. - private long mCurrentUserSerialNumber = -1; - - public NavigationBarAppsModel(Context context) { - mContext = context; - mPrefs = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); - mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); - - int version = mPrefs.getInt(VERSION_PREF, -1); - if (version != CURRENT_VERSION) { - // Since the data format changed, clean everything. - SharedPreferences.Editor edit = mPrefs.edit(); - edit.clear(); - edit.putInt(VERSION_PREF, CURRENT_VERSION); - edit.apply(); - } - } - - @VisibleForTesting - protected IPackageManager getPackageManager() { - return AppGlobals.getPackageManager(); - } - - // Returns a resolved app info for a given app info, or null if the app info is unlauncheable. - public ResolvedApp resolveApp(AppInfo appInfo) { - ComponentName component = appInfo.getComponentName(); - int appUserId = appInfo.getUser().getIdentifier(); - - if (mCurrentUserId != appUserId) { - // Check if app user is a profile of current user and the app user is enabled. - UserInfo appUserInfo = mUserManager.getUserInfo(appUserId); - UserInfo currentUserInfo = mUserManager.getUserInfo(mCurrentUserId); - if (appUserInfo == null || currentUserInfo == null - || appUserInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID - || appUserInfo.profileGroupId != currentUserInfo.profileGroupId - || !appUserInfo.isEnabled()) { - Slog.e(TAG, "User " + appUserId + - " is is not a profile of the current user, or is disabled."); - return null; - } - } - - // This code is based on LauncherAppsService.startActivityAsUser code. - Intent launchIntent = new Intent(Intent.ACTION_MAIN); - launchIntent.addCategory(Intent.CATEGORY_LAUNCHER); - launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - launchIntent.setPackage(component.getPackageName()); - - try { - ActivityInfo info = getPackageManager().getActivityInfo(component, 0, appUserId); - if (info == null) { - Slog.e(TAG, "Activity " + component + " is not installed."); - return null; - } - - if (!info.exported) { - Slog.e(TAG, "Activity " + component + " doesn't have 'exported' attribute."); - return null; - } - } catch (RemoteException e) { - Slog.e(TAG, "Failed to get activity info for " + component, e); - return null; - } - - // Check that the component actually has Intent.CATEGORY_LAUNCHER - // as calling startActivityAsUser ignores the category and just - // resolves based on the component if present. - List<ResolveInfo> apps = mContext.getPackageManager().queryIntentActivitiesAsUser(launchIntent, - 0 /* flags */, appUserId); - final int size = apps.size(); - for (int i = 0; i < size; ++i) { - ResolveInfo ri = apps.get(i); - ActivityInfo activityInfo = ri.activityInfo; - if (activityInfo.packageName.equals(component.getPackageName()) && - activityInfo.name.equals(component.getClassName())) { - // Found an activity with category launcher that matches - // this component so ok to launch. - launchIntent.setComponent(component); - ResolvedApp resolvedApp = new ResolvedApp(); - resolvedApp.launchIntent = launchIntent; - resolvedApp.ri = ri; - return resolvedApp; - } - } - - Slog.i(TAG, "Activity doesn't have category Intent.CATEGORY_LAUNCHER " + component); - return null; - } - - public void addOnAppsChangedListener(OnAppsChangedListener listener) { - mOnAppsChangedListeners.add(listener); - } - - public void removeOnAppsChangedListener(OnAppsChangedListener listener) { - mOnAppsChangedListeners.remove(listener); - } - - /** - * Reinitializes the model for a new user. - */ - public void setCurrentUser(int userId) { - mCurrentUserId = userId; - mCurrentUserSerialNumber = mUserManager.getSerialNumberForUser(new UserHandle(userId)); - - mApps.clear(); - - int appCount = mPrefs.getInt(userPrefixed(APP_COUNT_PREF), -1); - if (appCount >= 0) { - loadAppsFromPrefs(appCount); - } else { - // We switched to this user for the first time ever. This is a good opportunity to clean - // prefs for users deleted in the past. - removePrefsForDeletedUsers(); - - addDefaultApps(); - } - } - - /** - * Removes prefs for users that don't exist on the device. - */ - private void removePrefsForDeletedUsers() { - // Build a set of string representations of serial numbers of the device users. - final List<UserInfo> users = mUserManager.getUsers(); - final int userCount = users.size(); - - final Set<String> userSerials = new HashSet<String> (); - - for (int i = 0; i < userCount; ++i) { - userSerials.add(Long.toString(users.get(i).serialNumber)); - } - - // Walk though all prefs and delete ones which user is not in the string set. - final Map<String, ?> allPrefs = mPrefs.getAll(); - final SharedPreferences.Editor edit = mPrefs.edit(); - - for (Map.Entry<String, ?> pref : allPrefs.entrySet()) { - final String key = pref.getKey(); - if (key.equals(VERSION_PREF)) continue; - - final int userSeparatorPos = key.indexOf(USER_SEPARATOR); - - if (userSeparatorPos < 0) { - // Removing anomalous pref with no user. - edit.remove(key); - continue; - } - - final String prefUserSerial = key.substring(0, userSeparatorPos); - - if (!userSerials.contains(prefUserSerial)) { - // Removes pref for a not existing user. - edit.remove(key); - continue; - } - } - - edit.apply(); - } - - /** Returns the list of apps. */ - public List<AppInfo> getApps() { - return mApps; - } - - /** Sets the list of apps and saves it. */ - public void setApps(List<AppInfo> apps) { - mApps = apps; - savePrefs(); - - int size = mOnAppsChangedListeners.size(); - for (int i = 0; i < size; ++i) { - mOnAppsChangedListeners.get(i).onPinnedAppsChanged(); - } - } - - /** Saves the current model to disk. */ - private void savePrefs() { - SharedPreferences.Editor edit = mPrefs.edit(); - int appCount = mApps.size(); - edit.putInt(userPrefixed(APP_COUNT_PREF), appCount); - for (int i = 0; i < appCount; i++) { - final AppInfo appInfo = mApps.get(i); - String componentNameString = appInfo.getComponentName().flattenToString(); - edit.putString(prefNameForApp(i), componentNameString); - long userSerialNumber = mUserManager.getSerialNumberForUser(appInfo.getUser()); - edit.putLong(prefUserForApp(i), userSerialNumber); - } - // Start an asynchronous disk write. - edit.apply(); - } - - /** Loads AppInfo from prefs. Returns null if something is wrong. */ - private AppInfo loadAppFromPrefs(int index) { - String prefValue = mPrefs.getString(prefNameForApp(index), null); - if (prefValue == null) { - Slog.w(TAG, "Couldn't find pref " + prefNameForApp(index)); - return null; - } - ComponentName componentName = ComponentName.unflattenFromString(prefValue); - if (componentName == null) { - Slog.w(TAG, "Invalid component name " + prefValue); - return null; - } - long userSerialNumber = mPrefs.getLong(prefUserForApp(index), -1); - if (userSerialNumber == -1) { - Slog.w(TAG, "Couldn't find pref " + prefUserForApp(index)); - return null; - } - UserHandle appUser = mUserManager.getUserForSerialNumber(userSerialNumber); - if (appUser == null) { - Slog.w(TAG, "No user for serial " + userSerialNumber); - return null; - } - AppInfo appInfo = new AppInfo(componentName, appUser); - if (resolveApp(appInfo) == null) { - return null; - } - return appInfo; - } - - /** Loads the list of apps from SharedPreferences. */ - private void loadAppsFromPrefs(int appCount) { - for (int i = 0; i < appCount; i++) { - AppInfo appInfo = loadAppFromPrefs(i); - if (appInfo != null) { - mApps.add(appInfo); - } - } - - if (appCount != mApps.size()) savePrefs(); - } - - /** Adds the first few apps from the owner profile. Used for demo purposes. */ - private void addDefaultApps() { - // Get a list of all app activities. - final Intent queryIntent = new Intent(Intent.ACTION_MAIN, null); - queryIntent.addCategory(Intent.CATEGORY_LAUNCHER); - - final List<ResolveInfo> apps = mContext.getPackageManager().queryIntentActivitiesAsUser( - queryIntent, 0 /* flags */, mCurrentUserId); - final int appCount = apps.size(); - for (int i = 0; i < NUM_INITIAL_APPS && i < appCount; i++) { - ResolveInfo ri = apps.get(i); - ComponentName componentName = new ComponentName( - ri.activityInfo.packageName, ri.activityInfo.name); - mApps.add(new AppInfo(componentName, new UserHandle(mCurrentUserId))); - } - - savePrefs(); - } - - /** Returns a pref prefixed with the serial number of the current user. */ - private String userPrefixed(String pref) { - return Long.toString(mCurrentUserSerialNumber) + USER_SEPARATOR + pref; - } - - /** Returns the pref name for the app at a given index. */ - private String prefNameForApp(int index) { - return userPrefixed(APP_PREF_PREFIX + Integer.toString(index)); - } - - /** Returns the pref name for the app's user at a given index. */ - private String prefUserForApp(int index) { - return userPrefixed(APP_USER_PREFIX + Integer.toString(index)); - } - -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java index abe357ae004c..92288a3c12c3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java @@ -42,16 +42,20 @@ public class NavigationBarGestureHelper extends GestureDetector.SimpleOnGestureL implements TunerService.Tunable { private static final String KEY_DOCK_WINDOW_GESTURE = "overview_nav_bar_gesture"; + /** + * When dragging from the navigation bar, we drag in recents. + */ + public static final int DRAG_MODE_NONE = -1; /** * When dragging from the navigation bar, we drag in recents. */ - private static final int DRAG_MODE_RECENTS = 0; + public static final int DRAG_MODE_RECENTS = 0; /** * When dragging from the navigation bar, we drag the divider. */ - private static final int DRAG_MODE_DIVIDER = 1; + public static final int DRAG_MODE_DIVIDER = 1; private RecentsComponent mRecentsComponent; private Divider mDivider; @@ -207,12 +211,11 @@ public class NavigationBarGestureHelper extends GestureDetector.SimpleOnGestureL < mContext.getResources().getDisplayMetrics().widthPixels / 2) { createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT; } - boolean docked = mRecentsComponent.dockTopTask(dragMode == DRAG_MODE_RECENTS, - createMode, initialBounds); + boolean docked = mRecentsComponent.dockTopTask(dragMode, createMode, initialBounds); if (docked) { mDragMode = dragMode; if (mDragMode == DRAG_MODE_DIVIDER) { - mDivider.getView().startDragging(false /* animate */); + mDivider.getView().startDragging(false /* animate */, true /* touching*/); } mDockWindowTouchSlopExceeded = true; MetricsLogger.action(mContext, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java index beeee0b80f96..7395a33c8be4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java @@ -43,8 +43,6 @@ public class NavigationBarInflaterView extends FrameLayout implements TunerServi private static final String RECENT = "recent"; private static final String NAVSPACE = "space"; - private static final String APP_SHELF = "app_shelf"; - public static final String GRAVITY_SEPARATOR = ";"; public static final String BUTTON_SEPARATOR = ","; @@ -141,19 +139,13 @@ public class NavigationBarInflaterView extends FrameLayout implements TunerServi inflateButtons(start, (ViewGroup) mRot90.findViewById(R.id.ends_group), (ViewGroup) mRot90.findViewById(R.id.ends_group_lightsout), true); - if (center.length == 1 && APP_SHELF.equals(center[0])) { - inflateShelf((LinearLayout) mRot0.findViewById(R.id.ends_group), - (LinearLayout) mRot0.findViewById(R.id.ends_group_lightsout), false); - inflateShelf((LinearLayout) mRot90.findViewById(R.id.ends_group), - (LinearLayout) mRot90.findViewById(R.id.ends_group_lightsout), true); - } else { - inflateButtons(center, (ViewGroup) mRot0.findViewById(R.id.center_group), - (ViewGroup) mRot0.findViewById(R.id.center_group_lightsout), false); - inflateButtons(center, (ViewGroup) mRot90.findViewById(R.id.center_group), - (ViewGroup) mRot90.findViewById(R.id.center_group_lightsout), true); - addGravitySpacer((LinearLayout) mRot0.findViewById(R.id.ends_group)); - addGravitySpacer((LinearLayout) mRot90.findViewById(R.id.ends_group)); - } + inflateButtons(center, (ViewGroup) mRot0.findViewById(R.id.center_group), + (ViewGroup) mRot0.findViewById(R.id.center_group_lightsout), false); + inflateButtons(center, (ViewGroup) mRot90.findViewById(R.id.center_group), + (ViewGroup) mRot90.findViewById(R.id.center_group_lightsout), true); + + addGravitySpacer((LinearLayout) mRot0.findViewById(R.id.ends_group)); + addGravitySpacer((LinearLayout) mRot90.findViewById(R.id.ends_group)); inflateButtons(end, (ViewGroup) mRot0.findViewById(R.id.ends_group), (ViewGroup) mRot0.findViewById(R.id.ends_group_lightsout), false); @@ -161,14 +153,6 @@ public class NavigationBarInflaterView extends FrameLayout implements TunerServi (ViewGroup) mRot90.findViewById(R.id.ends_group_lightsout), true); } - private void inflateShelf(LinearLayout layout, LinearLayout lightsOut, boolean landscape) { - View v = (landscape ? mLandscapeInflater : mLayoutInflater) - .inflate(R.layout.apps_bar, layout, false); - layout.addView(v); - addToDispatchers(v); - copyToLightsout(v, lightsOut); - } - private void addGravitySpacer(LinearLayout layout) { layout.addView(new Space(mContext), new LinearLayout.LayoutParams(0, 0, 1)); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 839b57956f9a..d86629ff4740 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -263,11 +263,6 @@ public class NavigationBarView extends LinearLayout { return mButtonDisatchers.get(R.id.ime_switcher); } - @Nullable - public View getAppShelf() { - return getCurrentView().findViewById(R.id.app_shelf); - } - private void getCarModeIcons(Context ctx) { mBackCarModeIcon = ctx.getDrawable(R.drawable.ic_sysbar_back_carmode); mBackLandCarModeIcon = mBackCarModeIcon; @@ -395,12 +390,6 @@ public class NavigationBarView extends LinearLayout { getBackButton().setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE); getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE); getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE); - - // The app shelf, if it exists, follows the visibility of the home button. - View appShelf = getAppShelf(); - if (appShelf != null) { - appShelf.setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE); - } } private boolean inLockTask() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index ba20679980d6..8e89efd7b563 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -39,11 +39,11 @@ import android.view.ViewStub; import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.accessibility.AccessibilityEvent; -import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; import android.widget.FrameLayout; import android.widget.TextView; + import com.android.internal.logging.MetricsLogger; import com.android.keyguard.KeyguardStatusView; import com.android.systemui.DejankUtils; @@ -57,6 +57,7 @@ import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.ExpandableView; import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.statusbar.GestureRecorder; +import com.android.systemui.statusbar.Interpolators; import com.android.systemui.statusbar.KeyguardAffordanceView; import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.StatusBarState; @@ -104,7 +105,6 @@ public class NotificationPanelView extends PanelView implements private View mQsNavbarScrim; private NotificationsQuickSettingsContainer mNotificationContainerParent; private NotificationStackScrollLayout mNotificationStackScroller; - private int mNotificationTopPadding; private boolean mAnimateNextTopPaddingChange; private int mTrackingPointer; @@ -151,9 +151,6 @@ public class NotificationPanelView extends PanelView implements private int mUnlockMoveDistance; private float mEmptyDragAmount; - private Interpolator mFastOutSlowInInterpolator; - private Interpolator mFastOutLinearInterpolator; - private Interpolator mDozeAnimationInterpolator; private ObjectAnimator mClockAnimator; private int mClockAnimationTarget = -1; private int mTopPaddingAdjustment; @@ -253,12 +250,6 @@ public class NotificationPanelView extends PanelView implements mNotificationStackScroller.setOverscrollTopChangedListener(this); mNotificationStackScroller.setOnEmptySpaceClickListener(this); mNotificationStackScroller.setScrollView(mScrollView); - mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(), - android.R.interpolator.fast_out_slow_in); - mFastOutLinearInterpolator = AnimationUtils.loadInterpolator(getContext(), - android.R.interpolator.fast_out_linear_in); - mDozeAnimationInterpolator = AnimationUtils.loadInterpolator(getContext(), - android.R.interpolator.linear_out_slow_in); mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area); mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim); mAfforanceHelper = new KeyguardAffordanceHelper(this, getContext()); @@ -281,8 +272,6 @@ public class NotificationPanelView extends PanelView implements @Override protected void loadDimens() { super.loadDimens(); - mNotificationTopPadding = getResources().getDimensionPixelSize( - R.dimen.notifications_top_padding); mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.4f); mStatusBarMinHeight = getResources().getDimensionPixelSize( com.android.internal.R.dimen.status_bar_height); @@ -371,7 +360,7 @@ public class NotificationPanelView extends PanelView implements } mQsSizeChangeAnimator = ValueAnimator.ofInt(oldHeight, newHeight); mQsSizeChangeAnimator.setDuration(300); - mQsSizeChangeAnimator.setInterpolator(mFastOutSlowInInterpolator); + mQsSizeChangeAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); mQsSizeChangeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { @@ -400,8 +389,8 @@ public class NotificationPanelView extends PanelView implements if (mStatusBarState != StatusBarState.KEYGUARD) { int bottom = mHeader.getCollapsedHeight(); stackScrollerPadding = mStatusBarState == StatusBarState.SHADE - ? bottom + mQsPeekHeight + mNotificationTopPadding - : mKeyguardStatusBar.getHeight() + mNotificationTopPadding; + ? bottom + mQsPeekHeight + : mKeyguardStatusBar.getHeight(); mTopPaddingAdjustment = 0; } else { mClockPositionAlgorithm.setup( @@ -433,8 +422,8 @@ public class NotificationPanelView extends PanelView implements public int computeMaxKeyguardNotifications(int maximum) { float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding(getHeight(), mKeyguardStatusView.getHeight()); - int keyguardPadding = getResources().getDimensionPixelSize( - R.dimen.notification_padding_dimmed); + int notificationPadding = Math.max(1, getResources().getDimensionPixelSize( + R.dimen.notification_divider_height)); final int overflowheight = getResources().getDimensionPixelSize( R.dimen.notification_summary_height); float bottomStackSize = mNotificationStackScroller.getKeyguardBottomStackSize(); @@ -446,7 +435,7 @@ public class NotificationPanelView extends PanelView implements if (!(child instanceof ExpandableNotificationRow)) { continue; } - availableSpace -= child.getMinHeight() + keyguardPadding; + availableSpace -= child.getMinHeight() + notificationPadding; if (availableSpace >= 0 && count < maximum) { count++; } else { @@ -471,7 +460,7 @@ public class NotificationPanelView extends PanelView implements } mClockAnimator = ObjectAnimator .ofFloat(mKeyguardStatusView, View.Y, mClockAnimationTarget); - mClockAnimator.setInterpolator(mFastOutSlowInInterpolator); + mClockAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); mClockAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); mClockAnimator.addListener(new AnimatorListenerAdapter() { @Override @@ -1107,7 +1096,7 @@ public class NotificationPanelView extends PanelView implements .translationY(0f) .setStartDelay(delay) .setDuration(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE) - .setInterpolator(mFastOutSlowInInterpolator) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .start(); mQsContainer.setY(-mQsContainer.getHeight()); mQsContainerAnimator = ObjectAnimator.ofFloat(mQsContainer, View.TRANSLATION_Y, @@ -1116,7 +1105,7 @@ public class NotificationPanelView extends PanelView implements - mQsContainer.getTop()); mQsContainerAnimator.setStartDelay(delay); mQsContainerAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE); - mQsContainerAnimator.setInterpolator(mFastOutSlowInInterpolator); + mQsContainerAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); mQsContainerAnimator.addListener(mAnimateHeaderSlidingInListener); mQsContainerAnimator.start(); mQsContainer.addOnLayoutChangeListener(mQsContainerAnimatorUpdater); @@ -1138,7 +1127,7 @@ public class NotificationPanelView extends PanelView implements mHeader.animate().y(-mHeader.getHeight()) .setStartDelay(0) .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD) - .setInterpolator(mFastOutSlowInInterpolator) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -1152,7 +1141,7 @@ public class NotificationPanelView extends PanelView implements .y(-mQsContainer.getHeight()) .setStartDelay(0) .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD) - .setInterpolator(mFastOutSlowInInterpolator) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .start(); } @@ -1174,7 +1163,7 @@ public class NotificationPanelView extends PanelView implements anim.setDuration(mStatusBar.isKeyguardFadingAway() ? mStatusBar.getKeyguardFadingAwayDuration() / 2 : StackStateAnimator.ANIMATION_DURATION_STANDARD); - anim.setInterpolator(mDozeAnimationInterpolator); + anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -1199,7 +1188,7 @@ public class NotificationPanelView extends PanelView implements ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); anim.addUpdateListener(mStatusBarAnimateAlphaListener); anim.setDuration(duration); - anim.setInterpolator(mDozeAnimationInterpolator); + anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); anim.start(); } @@ -1218,7 +1207,7 @@ public class NotificationPanelView extends PanelView implements .alpha(0f) .setStartDelay(mStatusBar.getKeyguardFadingAwayDelay()) .setDuration(mStatusBar.getKeyguardFadingAwayDuration() / 2) - .setInterpolator(PhoneStatusBar.ALPHA_OUT) + .setInterpolator(Interpolators.ALPHA_OUT) .withEndAction(mAnimateKeyguardBottomAreaInvisibleEndRunnable) .start(); } else if (statusBarState == StatusBarState.KEYGUARD @@ -1245,7 +1234,7 @@ public class NotificationPanelView extends PanelView implements .alpha(0f) .setStartDelay(0) .setDuration(160) - .setInterpolator(PhoneStatusBar.ALPHA_OUT) + .setInterpolator(Interpolators.ALPHA_OUT) .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable); if (keyguardFadingAway) { mKeyguardStatusView.animate() @@ -1263,7 +1252,7 @@ public class NotificationPanelView extends PanelView implements .alpha(1f) .setStartDelay(0) .setDuration(320) - .setInterpolator(PhoneStatusBar.ALPHA_IN) + .setInterpolator(Interpolators.ALPHA_IN) .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable); } else if (statusBarState == StatusBarState.KEYGUARD) { mKeyguardStatusView.animate().cancel(); @@ -1380,8 +1369,7 @@ public class NotificationPanelView extends PanelView implements // on Keyguard, maxQs denotes the top padding from the quick settings panel. We need to // take the maximum and linearly interpolate with the panel expansion for a nice motion. int maxNotifications = mClockPositionResult.stackScrollerPadding - - mClockPositionResult.stackScrollerPaddingAdjustment - - mNotificationTopPadding; + - mClockPositionResult.stackScrollerPaddingAdjustment; int maxQs = getTempQsMaxExpansion(); int max = mStatusBarState == StatusBarState.KEYGUARD ? Math.max(maxNotifications, maxQs) @@ -1395,7 +1383,7 @@ public class NotificationPanelView extends PanelView implements // We can only do the smoother transition on Keyguard when we also are not collapsing // from a scrolled quick settings. return interpolate(getQsExpansionFraction(), - mNotificationStackScroller.getIntrinsicPadding() - mNotificationTopPadding, + mNotificationStackScroller.getIntrinsicPadding(), mQsMaxExpansionHeight); } else { return mQsExpansionHeight; @@ -1625,15 +1613,13 @@ public class NotificationPanelView extends PanelView implements maxQsHeight = (int) mQsSizeChangeAnimator.getAnimatedValue(); } float totalHeight = Math.max( - maxQsHeight + mNotificationStackScroller.getNotificationTopPadding(), - mStatusBarState == StatusBarState.KEYGUARD + maxQsHeight, mStatusBarState == StatusBarState.KEYGUARD ? mClockPositionResult.stackScrollerPadding - mTopPaddingAdjustment : 0) + notificationHeight; if (totalHeight > mNotificationStackScroller.getHeight()) { float fullyCollapsedHeight = maxQsHeight + mNotificationStackScroller.getMinStackHeight() - + mNotificationStackScroller.getNotificationTopPadding() - getScrollViewScrollY(); totalHeight = Math.max(fullyCollapsedHeight, mNotificationStackScroller.getHeight()); } @@ -1680,14 +1666,14 @@ public class NotificationPanelView extends PanelView implements boolean active = getMaxPanelHeight() - getExpandedHeight() > mUnlockMoveDistance; KeyguardAffordanceView lockIcon = mKeyguardBottomArea.getLockIcon(); if (active && !mUnlockIconActive && mTracking) { - lockIcon.setImageAlpha(1.0f, true, 150, mFastOutLinearInterpolator, null); + lockIcon.setImageAlpha(1.0f, true, 150, Interpolators.FAST_OUT_LINEAR_IN, null); lockIcon.setImageScale(LOCK_ICON_ACTIVE_SCALE, true, 150, - mFastOutLinearInterpolator); + Interpolators.FAST_OUT_LINEAR_IN); } else if (!active && mUnlockIconActive && mTracking) { lockIcon.setImageAlpha(lockIcon.getRestingAlpha(), true /* animate */, - 150, mFastOutLinearInterpolator, null); + 150, Interpolators.FAST_OUT_LINEAR_IN, null); lockIcon.setImageScale(1.0f, true, 150, - mFastOutLinearInterpolator); + Interpolators.FAST_OUT_LINEAR_IN); } mUnlockIconActive = active; } @@ -1727,7 +1713,7 @@ public class NotificationPanelView extends PanelView implements float translation = stackTranslation / HEADER_RUBBERBAND_FACTOR; if (mHeadsUpManager.hasPinnedHeadsUp() || mIsExpansionFromHeadsUp) { translation = mNotificationStackScroller.getTopPadding() + stackTranslation - - mNotificationTopPadding - mQsMinExpansionHeight; + - mQsMinExpansionHeight; } return Math.min(0, translation); } @@ -1892,8 +1878,8 @@ public class NotificationPanelView extends PanelView implements if (!expand && (mStatusBar.getBarState() == StatusBarState.KEYGUARD || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED)) { KeyguardAffordanceView lockIcon = mKeyguardBottomArea.getLockIcon(); - lockIcon.setImageAlpha(0.0f, true, 100, mFastOutLinearInterpolator, null); - lockIcon.setImageScale(2.0f, true, 100, mFastOutLinearInterpolator); + lockIcon.setImageAlpha(0.0f, true, 100, Interpolators.FAST_OUT_LINEAR_IN, null); + lockIcon.setImageScale(2.0f, true, 100, Interpolators.FAST_OUT_LINEAR_IN); } } @@ -2033,12 +2019,12 @@ public class NotificationPanelView extends PanelView implements */ private void startHighlightIconAnimation(final KeyguardAffordanceView icon) { icon.setImageAlpha(1.0f, true, KeyguardAffordanceHelper.HINT_PHASE1_DURATION, - mFastOutSlowInInterpolator, new Runnable() { + Interpolators.FAST_OUT_SLOW_IN, new Runnable() { @Override public void run() { icon.setImageAlpha(icon.getRestingAlpha(), true /* animate */, KeyguardAffordanceHelper.HINT_PHASE1_DURATION, - mFastOutSlowInInterpolator, null); + Interpolators.FAST_OUT_SLOW_IN, null); } }); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index f0b78944c1fb..f036d0492885 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -29,7 +29,6 @@ import android.view.InputDevice; import android.view.MotionEvent; import android.view.ViewConfiguration; import android.view.ViewTreeObserver; -import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.FrameLayout; @@ -39,6 +38,7 @@ import com.android.systemui.R; import com.android.systemui.classifier.FalsingManager; import com.android.systemui.doze.DozeLog; import com.android.systemui.statusbar.FlingAnimationUtils; +import com.android.systemui.statusbar.Interpolators; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -100,8 +100,6 @@ public abstract class PanelView extends FrameLayout { private float mInitialTouchX; private boolean mTouchDisabled; - private Interpolator mLinearOutSlowInInterpolator; - private Interpolator mFastOutSlowInInterpolator; private Interpolator mBounceInterpolator; protected KeyguardBottomAreaView mKeyguardBottomArea; @@ -161,7 +159,7 @@ public abstract class PanelView extends FrameLayout { } mPeekAnimator = ObjectAnimator.ofFloat(this, "expandedHeight", mPeekHeight) .setDuration(250); - mPeekAnimator.setInterpolator(mLinearOutSlowInInterpolator); + mPeekAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); mPeekAnimator.addListener(new AnimatorListenerAdapter() { private boolean mCancelled; @@ -187,10 +185,6 @@ public abstract class PanelView extends FrameLayout { public PanelView(Context context, AttributeSet attrs) { super(context, attrs); mFlingAnimationUtils = new FlingAnimationUtils(context, 0.6f); - mFastOutSlowInInterpolator = - AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in); - mLinearOutSlowInInterpolator = - AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in); mBounceInterpolator = new BounceInterpolator(); mFalsingManager = FalsingManager.getInstance(context); } @@ -951,7 +945,7 @@ public abstract class PanelView extends FrameLayout { float target = Math.max(0, getMaxPanelHeight() - mHintDistance); ValueAnimator animator = createHeightAnimator(target); animator.setDuration(250); - animator.setInterpolator(mFastOutSlowInInterpolator); + animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); animator.addListener(new AnimatorListenerAdapter() { private boolean mCancelled; @@ -975,7 +969,7 @@ public abstract class PanelView extends FrameLayout { mKeyguardBottomArea.getIndicationView().animate() .translationY(-mHintDistance) .setDuration(250) - .setInterpolator(mFastOutSlowInInterpolator) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .withEndAction(new Runnable() { @Override public void run() { 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 e2a61849c9c4..6fa1f5df460c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -47,6 +47,7 @@ import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.hardware.display.DisplayManager; import android.inputmethodservice.InputMethodService; import android.media.AudioAttributes; import android.media.MediaMetadata; @@ -87,13 +88,11 @@ import android.view.ViewGroup.LayoutParams; import android.view.ViewStub; import android.view.WindowManager; import android.view.WindowManagerGlobal; -import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.AccelerateInterpolator; import android.view.animation.Interpolator; -import android.view.animation.LinearInterpolator; -import android.view.animation.PathInterpolator; 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; @@ -114,6 +113,8 @@ import com.android.systemui.doze.DozeLog; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.qs.QSPanel; import com.android.systemui.recents.ScreenPinningRequest; +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.activity.DockingTopTaskEvent; import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.ActivatableNotificationView; import com.android.systemui.statusbar.BackDropView; @@ -124,6 +125,7 @@ import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.GestureRecorder; +import com.android.systemui.statusbar.Interpolators; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.NotificationData.Entry; @@ -131,7 +133,6 @@ import com.android.systemui.statusbar.NotificationOverflowContainer; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.SignalClusterView; -import com.android.systemui.statusbar.SpeedBumpView; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener; import com.android.systemui.statusbar.policy.AccessibilityController; @@ -438,10 +439,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private boolean mDozingRequested; protected boolean mScrimSrcModeEnabled; - private Interpolator mLinearInterpolator = new LinearInterpolator(); - private Interpolator mBackdropInterpolator = new AccelerateDecelerateInterpolator(); - public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f); - public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f); + public static final Interpolator ALPHA_IN = Interpolators.ALPHA_IN; + public static final Interpolator ALPHA_OUT = Interpolators.ALPHA_OUT; private BackDropView mBackdrop; private ImageView mBackdropFront, mBackdropBack; @@ -744,9 +743,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener); mStackScroller.setOverflowContainer(mKeyguardIconOverflowContainer); - SpeedBumpView speedBump = (SpeedBumpView) LayoutInflater.from(mContext).inflate( - R.layout.status_bar_notification_speed_bump, mStackScroller, false); - mStackScroller.setSpeedBumpView(speedBump); + mEmptyShadeView = (EmptyShadeView) LayoutInflater.from(mContext).inflate( R.layout.status_bar_no_notifications, mStackScroller, false); mStackScroller.setEmptyShadeView(mEmptyShadeView); @@ -1108,9 +1105,22 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, @Override public boolean onLongClick(View v) { if (mRecents != null) { - boolean docked = mRecents.dockTopTask(false /* draggingInRecents */, + Point realSize = new Point(); + mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY) + .getRealSize(realSize); + Rect initialBounds; + + // Hack level over 9000: Make it one pixel smaller so activity manager doesn't + // dismiss it immediately again. Remove once b/26777526 is fixed. + if (mContext.getResources().getConfiguration().orientation + == Configuration.ORIENTATION_LANDSCAPE) { + initialBounds = new Rect(0, 0, realSize.x - 1, realSize.y); + } else { + initialBounds = new Rect(0, 0, realSize.x, realSize.y - 1); + } + boolean docked = mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE, ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, - null /* initialBounds */); + initialBounds); if (docked) { MetricsLogger.action(mContext, MetricsLogger.ACTION_WINDOW_DOCK_LONGPRESS); @@ -1256,7 +1266,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) { - if (mNotificationData.shouldSuppressScreenOn(notification.getKey())) { + if (shouldSupressFullScreenIntent(notification.getKey())) { if (DEBUG) { Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + notification.getKey()); } @@ -1283,6 +1293,14 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, setAreThereNotifications(); } + private boolean shouldSupressFullScreenIntent(String key) { + if (mPowerManager.isInteractive()) { + return mNotificationData.shouldSuppressPeek(key); + } else { + return mNotificationData.shouldSuppressScreenOn(key); + } + } + @Override protected void updateNotificationRanking(RankingMap ranking) { mNotificationData.updateRanking(ranking); @@ -1841,7 +1859,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // if mScrimSrcModeEnabled. Note that 0.001 is rounded down to 0 in // libhwui. .alpha(0.002f) - .setInterpolator(mBackdropInterpolator) + .setInterpolator(Interpolators.ACCELERATE_DECELERATE) .setDuration(300) .setStartDelay(0) .withEndAction(new Runnable() { @@ -1860,7 +1878,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // behind. .setDuration(mKeyguardFadingAwayDuration / 2) .setStartDelay(mKeyguardFadingAwayDelay) - .setInterpolator(mLinearInterpolator) + .setInterpolator(Interpolators.LINEAR) .start(); } } @@ -3740,7 +3758,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mNotificationPanel.setBarState(mState, mKeyguardFadingAway, goingToFullShade); updateDozingState(); updatePublicMode(); - updateStackScrollerState(goingToFullShade); + updateStackScrollerState(goingToFullShade, fromShadeLocked); updateNotifications(); checkBarModes(); updateMediaMetaData(false); @@ -3761,11 +3779,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING, animate); } - public void updateStackScrollerState(boolean goingToFullShade) { + public void updateStackScrollerState(boolean goingToFullShade, boolean fromShadeLocked) { if (mStackScroller == null) return; boolean onKeyguard = mState == StatusBarState.KEYGUARD; mStackScroller.setHideSensitive(isLockscreenPublicMode(), goingToFullShade); - mStackScroller.setDimmed(onKeyguard, false /* animate */); + mStackScroller.setDimmed(onKeyguard, fromShadeLocked /* animate */); mStackScroller.setExpandingEnabled(!onKeyguard); ActivatableNotificationView activatedChild = mStackScroller.getActivatedChild(); mStackScroller.setActivatedChild(null); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java index 5b44f0a4189f..f18c341639f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.Intent; import android.graphics.Rect; import android.graphics.drawable.Animatable; +import android.graphics.drawable.Drawable; import android.graphics.drawable.RippleDrawable; import android.util.AttributeSet; import android.view.View; @@ -70,6 +71,8 @@ public class QuickStatusBarHeader extends BaseStatusBarHeader implements private QuickQSPanel mHeaderQsPanel; private boolean mShowEmergencyCallsOnly; private float mDateTimeTranslation; + private MultiUserSwitch mMultiUserSwitch; + private ImageView mMultiUserAvatar; public QuickStatusBarHeader(Context context, AttributeSet attrs) { super(context, attrs); @@ -100,6 +103,9 @@ public class QuickStatusBarHeader extends BaseStatusBarHeader implements mQsDetailHeaderSwitch = (Switch) mQsDetailHeader.findViewById(android.R.id.toggle); mQsDetailHeaderProgress = (ImageView) findViewById(R.id.qs_detail_header_progress); + mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch); + mMultiUserAvatar = (ImageView) mMultiUserSwitch.findViewById(R.id.multi_user_avatar); + // RenderThread is doing more harm than good when touching the header (to expand quick // settings), so disable it for this view ((RippleDrawable) getBackground()).setForceSoftware(true); @@ -173,6 +179,12 @@ public class QuickStatusBarHeader extends BaseStatusBarHeader implements ? View.VISIBLE : View.INVISIBLE); mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility( TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE); + mMultiUserSwitch.setVisibility(mMultiUserSwitch.hasMultipleUsers() ? View.VISIBLE + : View.GONE); + } + + private boolean hasMultiUsers() { + return false; } private void updateListeners() { @@ -194,6 +206,7 @@ public class QuickStatusBarHeader extends BaseStatusBarHeader implements setupHost(qsPanel.getHost()); if (mQsPanel != null) { mQsPanel.setCallback(mQsPanelCallback); + mMultiUserSwitch.setQsPanel(qsPanel); } } @@ -254,7 +267,12 @@ public class QuickStatusBarHeader extends BaseStatusBarHeader implements @Override public void setUserInfoController(UserInfoController userInfoController) { - // Don't care. + userInfoController.addListener(new UserInfoController.OnUserInfoChangedListener() { + @Override + public void onUserInfoChanged(String name, Drawable picture) { + mMultiUserAvatar.setImageDrawable(picture); + } + }); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 403199a8d0be..f310c2c2488c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -22,6 +22,7 @@ import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Color; +import android.graphics.Rect; import android.view.View; import android.view.ViewTreeObserver; import android.view.animation.DecelerateInterpolator; @@ -497,4 +498,16 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, public void dontAnimateBouncerChangesUntilNextFrame() { mDontAnimateBouncerChanges = true; } + + public void setExcludedBackgroundArea(Rect area) { + mScrimBehind.setExcludedArea(area); + } + + public int getScrimBehindColor() { + return mScrimBehind.getScrimColorWithAlpha(); + } + + public void setScrimBehindChangeRunnable(Runnable changeRunnable) { + mScrimBehind.setChangeRunnable(changeRunnable); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java index 512af1bd1568..7247b57a85f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java @@ -26,7 +26,9 @@ import android.view.View; import android.view.ViewConfiguration; import android.view.animation.Animation; import android.view.animation.AnimationUtils; + import com.android.keyguard.AlphaOptimizedImageButton; +import com.android.systemui.statusbar.Interpolators; public class SettingsButton extends AlphaOptimizedImageButton { @@ -157,8 +159,7 @@ public class SettingsButton extends AlphaOptimizedImageButton { performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); mUpToSpeed = true; mAnimator = ObjectAnimator.ofFloat(this, View.ROTATION, 0, 360); - mAnimator.setInterpolator(AnimationUtils.loadInterpolator(mContext, - android.R.interpolator.linear)); + mAnimator.setInterpolator(Interpolators.LINEAR); mAnimator.setDuration(FULL_SPEED_LENGTH); mAnimator.setRepeatCount(Animation.INFINITE); mAnimator.start(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java index 64fb066babcd..c6537e8caa97 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -32,15 +32,15 @@ import android.util.ArraySet; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; + import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.BatteryMeterView; import com.android.systemui.FontSizeUtils; import com.android.systemui.R; +import com.android.systemui.statusbar.Interpolators; import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.SignalClusterView; import com.android.systemui.statusbar.StatusBarIconView; @@ -63,8 +63,6 @@ public class StatusBarIconController extends StatusBarIconList implements Tunabl private Context mContext; private PhoneStatusBar mPhoneStatusBar; - private Interpolator mLinearOutSlowIn; - private Interpolator mFastOutSlowIn; private DemoStatusIcons mDemoStatusIcons; private LinearLayout mSystemIconArea; @@ -129,10 +127,6 @@ public class StatusBarIconController extends StatusBarIconList implements Tunabl maybeScaleBatteryMeterView(context); mClock = (TextView) statusBar.findViewById(R.id.clock); - mLinearOutSlowIn = AnimationUtils.loadInterpolator(mContext, - android.R.interpolator.linear_out_slow_in); - mFastOutSlowIn = AnimationUtils.loadInterpolator(mContext, - android.R.interpolator.fast_out_slow_in); mDarkModeIconColorSingleTone = context.getColor(R.color.dark_mode_icon_color_single_tone); mLightModeIconColorSingleTone = context.getColor(R.color.light_mode_icon_color_single_tone); mHandler = new Handler(); @@ -348,7 +342,7 @@ public class StatusBarIconController extends StatusBarIconList implements Tunabl .alpha(0f) .setDuration(160) .setStartDelay(0) - .setInterpolator(PhoneStatusBar.ALPHA_OUT) + .setInterpolator(Interpolators.ALPHA_OUT) .withEndAction(new Runnable() { @Override public void run() { @@ -370,7 +364,7 @@ public class StatusBarIconController extends StatusBarIconList implements Tunabl v.animate() .alpha(1f) .setDuration(320) - .setInterpolator(PhoneStatusBar.ALPHA_IN) + .setInterpolator(Interpolators.ALPHA_IN) .setStartDelay(50) // We need to clean up any pending end action from animateHide if we call @@ -382,7 +376,7 @@ public class StatusBarIconController extends StatusBarIconList implements Tunabl if (mPhoneStatusBar.isKeyguardFadingAway()) { v.animate() .setDuration(mPhoneStatusBar.getKeyguardFadingAwayDuration()) - .setInterpolator(mLinearOutSlowIn) + .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN) .setStartDelay(mPhoneStatusBar.getKeyguardFadingAwayDelay()) .start(); } @@ -419,7 +413,7 @@ public class StatusBarIconController extends StatusBarIconList implements Tunabl }); mTintAnimator.setDuration(duration); mTintAnimator.setStartDelay(delay); - mTintAnimator.setInterpolator(mFastOutSlowIn); + mTintAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); mTintAnimator.start(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java index 56c1e107c2f8..1aae4965b4ae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.phone; -import com.android.systemui.R; - import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -31,10 +29,11 @@ import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; -import android.view.animation.AccelerateDecelerateInterpolator; -import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; +import com.android.systemui.R; +import com.android.systemui.statusbar.Interpolators; + public class TrustDrawable extends Drawable { private static final long ENTERING_FROM_UNSET_START_DELAY = 200; @@ -69,10 +68,6 @@ public class TrustDrawable extends Drawable { private final Animator mVisibleAnimator; - private final Interpolator mLinearOutSlowInInterpolator; - private final Interpolator mFastOutSlowInInterpolator; - private final Interpolator mAccelerateDecelerateInterpolator; - public TrustDrawable(Context context) { Resources r = context.getResources(); mInnerRadiusVisibleMin = r.getDimension(R.dimen.trust_circle_inner_radius_visible_min); @@ -83,12 +78,6 @@ public class TrustDrawable extends Drawable { mCurInnerRadius = mInnerRadiusEnter; - mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator( - context, android.R.interpolator.linear_out_slow_in); - mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator( - context, android.R.interpolator.fast_out_slow_in); - mAccelerateDecelerateInterpolator = new AccelerateDecelerateInterpolator(); - mVisibleAnimator = makeVisibleAnimator(); mPaint = new Paint(); @@ -212,19 +201,19 @@ public class TrustDrawable extends Drawable { private Animator makeVisibleAnimator() { return makeAnimators(mInnerRadiusVisibleMax, mInnerRadiusVisibleMin, ALPHA_VISIBLE_MAX, ALPHA_VISIBLE_MIN, VISIBLE_DURATION, - mAccelerateDecelerateInterpolator, + Interpolators.ACCELERATE_DECELERATE, true /* repeating */, false /* stateUpdateListener */); } private Animator makeEnterAnimator(float radius, int alpha) { return makeAnimators(radius, mInnerRadiusVisibleMax, - alpha, ALPHA_VISIBLE_MAX, ENTER_DURATION, mLinearOutSlowInInterpolator, + alpha, ALPHA_VISIBLE_MAX, ENTER_DURATION, Interpolators.LINEAR_OUT_SLOW_IN, false /* repeating */, true /* stateUpdateListener */); } private Animator makeExitAnimator(float radius, int alpha) { return makeAnimators(radius, mInnerRadiusExit, - alpha, 0, EXIT_DURATION, mFastOutSlowInInterpolator, + alpha, 0, EXIT_DURATION, Interpolators.FAST_OUT_SLOW_IN, false /* repeating */, true /* stateUpdateListener */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java index 4ae0321ff68b..59d54ff7678f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java @@ -16,15 +16,15 @@ package com.android.systemui.statusbar.policy; -import com.android.systemui.R; -import com.android.systemui.statusbar.ScrimView; -import com.android.systemui.statusbar.phone.PhoneStatusBar; -import com.android.systemui.statusbar.phone.StatusBarWindowView; - import android.view.View; import android.view.ViewPropertyAnimator; import android.widget.FrameLayout; +import com.android.systemui.R; +import com.android.systemui.statusbar.Interpolators; +import com.android.systemui.statusbar.ScrimView; +import com.android.systemui.statusbar.phone.StatusBarWindowView; + /** * Controls showing and hiding of the brightness mirror. */ @@ -46,13 +46,13 @@ public class BrightnessMirrorController { public void showMirror() { mBrightnessMirror.setVisibility(View.VISIBLE); - mScrimBehind.animateViewAlpha(0.0f, TRANSITION_DURATION_OUT, PhoneStatusBar.ALPHA_OUT); + mScrimBehind.animateViewAlpha(0.0f, TRANSITION_DURATION_OUT, Interpolators.ALPHA_OUT); outAnimation(mNotificationPanel.animate()) .withLayer(); } public void hideMirror() { - mScrimBehind.animateViewAlpha(1.0f, TRANSITION_DURATION_IN, PhoneStatusBar.ALPHA_IN); + mScrimBehind.animateViewAlpha(1.0f, TRANSITION_DURATION_IN, Interpolators.ALPHA_IN); inAnimation(mNotificationPanel.animate()) .withLayer() .withEndAction(new Runnable() { @@ -66,12 +66,12 @@ public class BrightnessMirrorController { private ViewPropertyAnimator outAnimation(ViewPropertyAnimator a) { return a.alpha(0.0f) .setDuration(TRANSITION_DURATION_OUT) - .setInterpolator(PhoneStatusBar.ALPHA_OUT); + .setInterpolator(Interpolators.ALPHA_OUT); } private ViewPropertyAnimator inAnimation(ViewPropertyAnimator a) { return a.alpha(1.0f) .setDuration(TRANSITION_DURATION_IN) - .setInterpolator(PhoneStatusBar.ALPHA_IN); + .setInterpolator(Interpolators.ALPHA_IN); } @@ -103,10 +103,5 @@ public class BrightnessMirrorController { lp.gravity = mBrightnessMirror.getResources().getInteger( R.integer.notification_panel_layout_gravity); mBrightnessMirror.setLayoutParams(lp); - - int padding = mBrightnessMirror.getResources().getDimensionPixelSize( - R.dimen.notification_side_padding); - mBrightnessMirror.setPadding(padding, mBrightnessMirror.getPaddingTop(), - padding, mBrightnessMirror.getPaddingBottom()); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java index dd7bd5656db2..f06552283219 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java @@ -84,7 +84,6 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL private final View mStatusBarWindowView; private final int mStatusBarHeight; - private final int mNotificationsTopPadding; private final Context mContext; private final NotificationGroupManager mGroupManager; private PhoneStatusBar mBar; @@ -138,8 +137,6 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL mGroupManager = groupManager; mStatusBarHeight = resources.getDimensionPixelSize( com.android.internal.R.dimen.status_bar_height); - mNotificationsTopPadding = context.getResources() - .getDimensionPixelSize(R.dimen.notifications_top_padding); } private void updateTouchableRegionListener() { @@ -399,7 +396,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL } info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); - info.touchableRegion.set(minX, 0, maxX, maxY + mNotificationsTopPadding); + info.touchableRegion.set(minX, 0, maxX, maxY); } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) { info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java index 3f63b5fac5f8..d739d6c15448 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java @@ -32,7 +32,7 @@ import android.view.View; import android.view.animation.Interpolator; import com.android.systemui.R; -import com.android.systemui.statusbar.phone.PhoneStatusBar; +import com.android.systemui.statusbar.Interpolators; import java.util.ArrayList; import java.util.HashSet; @@ -59,7 +59,6 @@ public class KeyButtonRipple extends Drawable { private int mMaxWidth; private final Interpolator mInterpolator = new LogInterpolator(); - private final Interpolator mAlphaExitInterpolator = PhoneStatusBar.ALPHA_OUT; private boolean mSupportHardware; private final View mTargetView; @@ -225,7 +224,7 @@ public class KeyButtonRipple extends Drawable { private void exitSoftware() { ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(this, "glowAlpha", mGlowAlpha, 0f); - alphaAnimator.setInterpolator(mAlphaExitInterpolator); + alphaAnimator.setInterpolator(Interpolators.ALPHA_OUT); alphaAnimator.setDuration(ANIMATION_DURATION_FADE); alphaAnimator.addListener(mAnimatorListener); alphaAnimator.start(); @@ -331,7 +330,7 @@ public class KeyButtonRipple extends Drawable { final RenderNodeAnimator opacityAnim = new RenderNodeAnimator(mPaintProp, RenderNodeAnimator.PAINT_ALPHA, 0); opacityAnim.setDuration(ANIMATION_DURATION_FADE); - opacityAnim.setInterpolator(mAlphaExitInterpolator); + opacityAnim.setInterpolator(Interpolators.ALPHA_OUT); opacityAnim.addListener(mAnimatorListener); opacityAnim.setTarget(mTargetView); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java index 5cf61569dc89..0959b0c9db88 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java @@ -21,6 +21,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.Context; import android.database.DataSetObserver; +import android.graphics.Interpolator; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -33,9 +34,9 @@ import android.widget.FrameLayout; import com.android.settingslib.animation.AppearAnimationUtils; import com.android.systemui.R; import com.android.systemui.qs.tiles.UserDetailItemView; +import com.android.systemui.statusbar.Interpolators; import com.android.systemui.statusbar.phone.KeyguardStatusBarView; import com.android.systemui.statusbar.phone.NotificationPanelView; -import com.android.systemui.statusbar.phone.PhoneStatusBar; /** * Manages the user switcher on the Keyguard. @@ -73,8 +74,7 @@ public class KeyguardUserSwitcher { mAdapter.registerDataSetObserver(mDataSetObserver); mUserSwitcherController = userSwitcherController; mAppearAnimationUtils = new AppearAnimationUtils(context, 400, -0.5f, 0.5f, - AnimationUtils.loadInterpolator( - context, android.R.interpolator.fast_out_slow_in)); + Interpolators.FAST_OUT_SLOW_IN); mUserSwitcherContainer.setKeyguardUserSwitcher(this); } else { mUserSwitcherContainer = null; @@ -158,7 +158,7 @@ public class KeyguardUserSwitcher { mAnimating = true; mBgAnimator = ObjectAnimator.ofInt(mBackground, "alpha", 0, 255); mBgAnimator.setDuration(400); - mBgAnimator.setInterpolator(PhoneStatusBar.ALPHA_IN); + mBgAnimator.setInterpolator(Interpolators.ALPHA_IN); mBgAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -174,7 +174,7 @@ public class KeyguardUserSwitcher { mUserSwitcher.animate() .alpha(0f) .setDuration(300) - .setInterpolator(PhoneStatusBar.ALPHA_OUT) + .setInterpolator(Interpolators.ALPHA_OUT) .withEndAction(new Runnable() { @Override public void run() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index dbb0295e7029..107a90484301 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -817,11 +817,11 @@ public class NetworkControllerImpl extends BroadcastReceiver public static class SubscriptionDefaults { public int getDefaultVoiceSubId() { - return SubscriptionManager.getDefaultVoiceSubId(); + return SubscriptionManager.getDefaultVoiceSubscriptionId(); } public int getDefaultDataSubId() { - return SubscriptionManager.getDefaultDataSubId(); + return SubscriptionManager.getDefaultDataSubscriptionId(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java index 56f65645bafc..561b18a68532 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java @@ -25,12 +25,12 @@ public class AnimationFilter { boolean animateAlpha; boolean animateY; boolean animateZ; - boolean animateScale; boolean animateHeight; boolean animateTopInset; boolean animateDimmed; boolean animateDark; boolean animateHideSensitive; + public boolean animateShadowAlpha; boolean hasDelays; boolean hasGoToFullShadeEvent; boolean hasDarkEvent; @@ -57,11 +57,6 @@ public class AnimationFilter { return this; } - public AnimationFilter animateScale() { - animateScale = true; - return this; - } - public AnimationFilter animateHeight() { animateHeight = true; return this; @@ -87,6 +82,11 @@ public class AnimationFilter { return this; } + public AnimationFilter animateShadowAlpha() { + animateShadowAlpha = true; + return this; + } + /** * Combines multiple filters into {@code this} filter, using or as the operand . * @@ -118,12 +118,12 @@ public class AnimationFilter { animateAlpha |= filter.animateAlpha; animateY |= filter.animateY; animateZ |= filter.animateZ; - animateScale |= filter.animateScale; animateHeight |= filter.animateHeight; animateTopInset |= filter.animateTopInset; animateDimmed |= filter.animateDimmed; animateDark |= filter.animateDark; animateHideSensitive |= filter.animateHideSensitive; + animateShadowAlpha |= filter.animateShadowAlpha; hasDelays |= filter.hasDelays; } @@ -131,8 +131,8 @@ public class AnimationFilter { animateAlpha = false; animateY = false; animateZ = false; - animateScale = false; animateHeight = false; + animateShadowAlpha = false; animateTopInset = false; animateDimmed = false; animateDark = false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java index dc40fb091608..409dac102da1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java @@ -76,8 +76,8 @@ public class NotificationChildrenContainer extends ViewGroup { super(context, attrs, defStyleAttr, defStyleRes); mChildPadding = getResources().getDimensionPixelSize( R.dimen.notification_children_padding); - mDividerHeight = getResources().getDimensionPixelSize( - R.dimen.notification_children_divider_height); + mDividerHeight = Math.max(1, getResources().getDimensionPixelSize( + R.dimen.notification_divider_height)); mMaxNotificationHeight = getResources().getDimensionPixelSize( R.dimen.notification_max_height); mNotificationAppearDistance = getResources().getDimensionPixelSize( @@ -324,7 +324,6 @@ public class NotificationChildrenContainer extends ViewGroup { childState.dark = parentState.dark; childState.hideSensitive = parentState.hideSensitive; childState.belowSpeedBump = parentState.belowSpeedBump; - childState.scale = 1.0f; childState.clipTopAmount = 0; childState.topOverLap = 0; boolean visible = i <= lastVisibleIndex; @@ -384,30 +383,8 @@ public class NotificationChildrenContainer extends ViewGroup { * @param state the new state we animate to */ public void prepareExpansionChanged(StackScrollState state) { - if (true) { - // TODO: do something that makes sense - return; - } - int childCount = mChildren.size(); - StackViewState sourceState = new StackViewState(); - ViewState dividerState = new ViewState(); - for (int i = 0; i < childCount; i++) { - ExpandableNotificationRow child = mChildren.get(i); - StackViewState viewState = state.getViewStateForView(child); - sourceState.copyFrom(viewState); - sourceState.alpha = 0; - sourceState.yTranslation += mNotificationAppearDistance; - state.applyState(child, sourceState); - - // layout the divider - View divider = mDividers.get(i); - dividerState.initFrom(divider); - dividerState.yTranslation = viewState.yTranslation - mDividerHeight - + mNotificationAppearDistance; - dividerState.alpha = 0; - state.applyViewState(divider, dividerState); - - } + // TODO: do something that makes sense, like placing the invisible views correctly + return; } public void startAnimationToState(StackScrollState state, StackStateAnimator stateAnimator, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 36b2810313a3..d5b57ac0a018 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -16,12 +16,22 @@ package com.android.systemui.statusbar.stack; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; +import android.animation.TimeAnimator; +import android.animation.ValueAnimator; import android.annotation.Nullable; import android.content.Context; import android.content.res.Configuration; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Paint; import android.graphics.PointF; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.util.Pair; @@ -32,6 +42,7 @@ import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; import android.widget.OverScroller; import com.android.systemui.ExpandHelper; @@ -43,9 +54,8 @@ import com.android.systemui.statusbar.DismissView; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.ExpandableView; -import com.android.systemui.statusbar.NotificationData; +import com.android.systemui.statusbar.Interpolators; import com.android.systemui.statusbar.NotificationOverflowContainer; -import com.android.systemui.statusbar.SpeedBumpView; import com.android.systemui.statusbar.StackScrollerDecorView; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.NotificationGroupManager; @@ -64,6 +74,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter, ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener { + public static final float BACKGROUND_ALPHA_DIMMED = 0.7f; private static final String TAG = "NotificationStackScrollLayout"; private static final boolean DEBUG = false; private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f; @@ -79,6 +90,7 @@ public class NotificationStackScrollLayout extends ViewGroup private SwipeHelper mSwipeHelper; private boolean mSwipingInProgress; private int mCurrentStackHeight = Integer.MAX_VALUE; + private final Paint mBackgroundPaint = new Paint(); /** * mCurrentStackHeight is the actual stack height, mLastSetStackHeight is the stack height set @@ -103,15 +115,13 @@ public class NotificationStackScrollLayout extends ViewGroup private float mInitialTouchX; private float mInitialTouchY; - private int mSidePaddings; private Paint mDebugPaint; private int mContentHeight; private int mCollapsedSize; private int mBottomStackSlowDownHeight; private int mBottomStackPeekSize; private int mPaddingBetweenElements; - private int mPaddingBetweenElementsDimmed; - private int mPaddingBetweenElementsNormal; + private int mIncreasedPaddingBetweenElements; private int mTopPadding; private int mCollapseSecondCardPadding; @@ -163,7 +173,6 @@ public class NotificationStackScrollLayout extends ViewGroup private boolean mGoToFullShadeNeedsAnimation; private boolean mIsExpanded = true; private boolean mChildrenUpdateRequested; - private SpeedBumpView mSpeedBumpView; private boolean mIsExpansionChanging; private boolean mPanelTracking; private boolean mExpandingNotification; @@ -183,7 +192,6 @@ public class NotificationStackScrollLayout extends ViewGroup */ private float mMinTopOverScrollToEscape; private int mIntrinsicPadding; - private int mNotificationTopPadding; private float mStackTranslation; private float mTopPaddingOverflow; private boolean mDontReportNextOverScroll; @@ -234,6 +242,45 @@ public class NotificationStackScrollLayout extends ViewGroup private NotificationOverflowContainer mOverflowContainer; private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>(); private FalsingManager mFalsingManager; + private boolean mAnimationRunning; + private ViewTreeObserver.OnPreDrawListener mBackgroundUpdater + = new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + // if it needs animation + if (!mNeedsAnimation && !mChildrenUpdateRequested) { + updateBackground(); + } + return true; + } + }; + private Rect mBackgroundBounds = new Rect(); + private Rect mStartAnimationRect = new Rect(); + private Rect mEndAnimationRect = new Rect(); + private Rect mCurrentBounds = new Rect(-1, -1, -1, -1); + private boolean mAnimateNextBackgroundBottom; + private boolean mAnimateNextBackgroundTop; + private ObjectAnimator mBottomAnimator = null; + private ObjectAnimator mTopAnimator = null; + private ActivatableNotificationView mFirstVisibleBackgroundChild = null; + private ActivatableNotificationView mLastVisibleBackgroundChild = null; + private int mBgColor; + private float mDimAmount; + private ValueAnimator mDimAnimator; + private Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mDimAnimator = null; + } + }; + private ValueAnimator.AnimatorUpdateListener mDimUpdateListener + = new ValueAnimator.AnimatorUpdateListener() { + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + setDimAmount((Float) animation.getAnimatedValue()); + } + }; public NotificationStackScrollLayout(Context context) { this(context, null); @@ -250,6 +297,7 @@ public class NotificationStackScrollLayout extends ViewGroup public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); + mBgColor = context.getColor(R.color.notification_shade_background_color); int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height); int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_max_height); mExpandHelper = new ExpandHelper(getContext(), this, @@ -261,18 +309,20 @@ public class NotificationStackScrollLayout extends ViewGroup mSwipeHelper.setLongPressListener(mLongPressListener); mStackScrollAlgorithm = new StackScrollAlgorithm(context); initView(context); + setWillNotDraw(false); if (DEBUG) { - setWillNotDraw(false); mDebugPaint = new Paint(); mDebugPaint.setColor(0xffff0000); mDebugPaint.setStrokeWidth(2); mDebugPaint.setStyle(Paint.Style.STROKE); } mFalsingManager = FalsingManager.getInstance(context); + mBackgroundPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); } @Override protected void onDraw(Canvas canvas) { + canvas.drawRect(0, mCurrentBounds.top, getWidth(), mCurrentBounds.bottom, mBackgroundPaint); if (DEBUG) { int y = mTopPadding; canvas.drawLine(0, y, getWidth(), y, mDebugPaint); @@ -288,6 +338,20 @@ public class NotificationStackScrollLayout extends ViewGroup } } + private void updateBackgroundDimming() { + float alpha = BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount); + // We need to manually blend in the background color + int scrimColor = mScrimController.getScrimBehindColor(); + // SRC_OVER blending Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc + float alphaInv = 1 - alpha; + int color = Color.argb((int) (alpha * 255 + alphaInv * Color.alpha(scrimColor)), + (int) (Color.red(mBgColor) + alphaInv * Color.red(scrimColor)), + (int) (Color.green(mBgColor) + alphaInv * Color.green(scrimColor)), + (int) (Color.blue(mBgColor) + alphaInv * Color.blue(scrimColor))); + mBackgroundPaint.setColor(color); + invalidate(); + } + private void initView(Context context) { mScroller = new OverScroller(getContext()); setFocusable(true); @@ -298,36 +362,23 @@ public class NotificationStackScrollLayout extends ViewGroup mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); mOverflingDistance = configuration.getScaledOverflingDistance(); - - mSidePaddings = context.getResources() - .getDimensionPixelSize(R.dimen.notification_side_padding); mCollapsedSize = context.getResources() .getDimensionPixelSize(R.dimen.notification_min_height); mBottomStackPeekSize = context.getResources() .getDimensionPixelSize(R.dimen.bottom_stack_peek_amount); mStackScrollAlgorithm.initView(context); - mPaddingBetweenElementsDimmed = context.getResources() - .getDimensionPixelSize(R.dimen.notification_padding_dimmed); - mPaddingBetweenElementsNormal = context.getResources() - .getDimensionPixelSize(R.dimen.notification_padding); - updatePadding(mAmbientState.isDimmed()); + mPaddingBetweenElements = Math.max(1, context.getResources() + .getDimensionPixelSize(R.dimen.notification_divider_height)); + mIncreasedPaddingBetweenElements = context.getResources() + .getDimensionPixelSize(R.dimen.notification_divider_height_increased); + + mBottomStackSlowDownHeight = mStackScrollAlgorithm.getBottomStackSlowDownLength(); mMinTopOverScrollToEscape = getResources().getDimensionPixelSize( R.dimen.min_top_overscroll_to_qs); - mNotificationTopPadding = getResources().getDimensionPixelSize( - R.dimen.notifications_top_padding); mCollapseSecondCardPadding = getResources().getDimensionPixelSize( R.dimen.notification_collapse_second_card_padding); } - private void updatePadding(boolean dimmed) { - mPaddingBetweenElements = dimmed && mStackScrollAlgorithm.shouldScaleDimmed() - ? mPaddingBetweenElementsDimmed - : mPaddingBetweenElementsNormal; - mBottomStackSlowDownHeight = mStackScrollAlgorithm.getBottomStackSlowDownLength(); - updateContentHeight(); - notifyHeightChangeListener(null); - } - private void notifyHeightChangeListener(ExpandableView view) { if (mOnHeightChangedListener != null) { mOnHeightChangedListener.onHeightChanged(view, false /* needsAnimation */); @@ -337,10 +388,7 @@ public class NotificationStackScrollLayout extends ViewGroup @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); - int mode = MeasureSpec.getMode(widthMeasureSpec); - int size = MeasureSpec.getSize(widthMeasureSpec); - int childMeasureSpec = MeasureSpec.makeMeasureSpec(size - 2 * mSidePaddings, mode); - measureChildren(childMeasureSpec, heightMeasureSpec); + measureChildren(widthMeasureSpec, heightMeasureSpec); } @Override @@ -368,6 +416,7 @@ public class NotificationStackScrollLayout extends ViewGroup mRequestViewResizeAnimationOnLayout = false; } requestChildrenUpdate(); + updateFirstAndLastBackgroundViews(); } private void requestAnimationOnViewResize(ExpandableNotificationRow row) { @@ -378,18 +427,6 @@ public class NotificationStackScrollLayout extends ViewGroup } public void updateSpeedBumpIndex(int newIndex) { - int currentIndex = indexOfChild(mSpeedBumpView); - - // If we are currently layouted before the new speed bump index, we have to decrease it. - boolean validIndex = newIndex > 0; - if (newIndex > getChildCount() - 1) { - validIndex = false; - newIndex = -1; - } - if (validIndex && currentIndex != newIndex) { - changeViewPosition(mSpeedBumpView, newIndex); - } - updateSpeedBump(validIndex); mAmbientState.setSpeedBumpIndex(newIndex); } @@ -686,8 +723,7 @@ public class NotificationStackScrollLayout extends ViewGroup for (int childIdx = 0; childIdx < count; childIdx++) { ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx); if (slidingChild.getVisibility() == GONE - || slidingChild instanceof StackScrollerDecorView - || slidingChild == mSpeedBumpView) { + || slidingChild instanceof StackScrollerDecorView) { continue; } float childTop = slidingChild.getTranslationY(); @@ -714,8 +750,7 @@ public class NotificationStackScrollLayout extends ViewGroup for (int childIdx = 0; childIdx < count; childIdx++) { ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx); if (slidingChild.getVisibility() == GONE - || slidingChild instanceof StackScrollerDecorView - || slidingChild == mSpeedBumpView) { + || slidingChild instanceof StackScrollerDecorView) { continue; } float childTop = slidingChild.getTranslationY(); @@ -1412,22 +1447,251 @@ public class NotificationStackScrollLayout extends ViewGroup private void updateContentHeight() { int height = 0; + boolean previousNeedsIncreasedPaddings = false; for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - if (child.getVisibility() != View.GONE) { + ExpandableView expandableView = (ExpandableView) getChildAt(i); + if (expandableView.getVisibility() != View.GONE) { + boolean needsIncreasedPaddings = expandableView.needsIncreasedPadding(); if (height != 0) { - // add the padding before this element - height += mPaddingBetweenElements; - } - if (child instanceof ExpandableView) { - ExpandableView expandableView = (ExpandableView) child; - height += expandableView.getIntrinsicHeight(); + int padding = needsIncreasedPaddings || previousNeedsIncreasedPaddings + ? mIncreasedPaddingBetweenElements + : mPaddingBetweenElements; + height += padding; } + previousNeedsIncreasedPaddings = needsIncreasedPaddings; + height += expandableView.getIntrinsicHeight(); } } mContentHeight = height + mTopPadding; } + private void updateBackground() { + if (mAmbientState.isDark()) { + return; + } + updateBackgroundBounds(); + if (!mCurrentBounds.equals(mBackgroundBounds)) { + if (mAnimateNextBackgroundTop || mAnimateNextBackgroundBottom || areBoundsAnimating()) { + startBackgroundAnimation(); + } else { + mCurrentBounds.set(mBackgroundBounds); + applyCurrentBackgroundBounds(); + } + } else { + if (mBottomAnimator != null) { + mBottomAnimator.cancel(); + } + if (mTopAnimator != null) { + mTopAnimator.cancel(); + } + } + mAnimateNextBackgroundBottom = false; + mAnimateNextBackgroundTop = false; + } + + private boolean areBoundsAnimating() { + return mBottomAnimator != null || mTopAnimator != null; + } + + private void startBackgroundAnimation() { + startBottomAnimation(); + startTopAnimation(); + } + + private void startTopAnimation() { + int previousEndValue = mEndAnimationRect.top; + int newEndValue = mBackgroundBounds.top; + ObjectAnimator previousAnimator = mTopAnimator; + if (previousAnimator != null && previousEndValue == newEndValue) { + return; + } + if (!mAnimateNextBackgroundTop) { + // just a local update was performed + if (previousAnimator != null) { + // we need to increase all animation keyframes of the previous animator by the + // relative change to the end value + int previousStartValue = mStartAnimationRect.top; + PropertyValuesHolder[] values = previousAnimator.getValues(); + values[0].setIntValues(previousStartValue, newEndValue); + mStartAnimationRect.top = previousStartValue; + mEndAnimationRect.top = newEndValue; + previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); + return; + } else { + // no new animation needed, let's just apply the value + setBackgroundTop(newEndValue); + return; + } + } + if (previousAnimator != null) { + previousAnimator.cancel(); + } + ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundTop", + mCurrentBounds.top, newEndValue); + Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN; + animator.setInterpolator(interpolator); + animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); + // remove the tag when the animation is finished + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mStartAnimationRect.top = -1; + mEndAnimationRect.top = -1; + mTopAnimator = null; + } + }); + animator.start(); + mStartAnimationRect.top = mCurrentBounds.top; + mEndAnimationRect.top = newEndValue; + mTopAnimator = animator; + } + + private void startBottomAnimation() { + int previousStartValue = mStartAnimationRect.bottom; + int previousEndValue = mEndAnimationRect.bottom; + int newEndValue = mBackgroundBounds.bottom; + ObjectAnimator previousAnimator = mBottomAnimator; + if (previousAnimator != null && previousEndValue == newEndValue) { + return; + } + if (!mAnimateNextBackgroundBottom) { + // just a local update was performed + if (previousAnimator != null) { + // we need to increase all animation keyframes of the previous animator by the + // relative change to the end value + PropertyValuesHolder[] values = previousAnimator.getValues(); + values[0].setIntValues(previousStartValue, newEndValue); + mStartAnimationRect.bottom = previousStartValue; + mEndAnimationRect.bottom = newEndValue; + previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); + return; + } else { + // no new animation needed, let's just apply the value + setBackgroundBottom(newEndValue); + return; + } + } + if (previousAnimator != null) { + previousAnimator.cancel(); + } + ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundBottom", + mCurrentBounds.bottom, newEndValue); + Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN; + animator.setInterpolator(interpolator); + animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); + // remove the tag when the animation is finished + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mStartAnimationRect.bottom = -1; + mEndAnimationRect.bottom = -1; + mBottomAnimator = null; + } + }); + animator.start(); + mStartAnimationRect.bottom = mCurrentBounds.bottom; + mEndAnimationRect.bottom = newEndValue; + mBottomAnimator = animator; + } + + private void setBackgroundTop(int top) { + mCurrentBounds.top = top; + applyCurrentBackgroundBounds(); + } + + public void setBackgroundBottom(int bottom) { + mCurrentBounds.bottom = bottom; + applyCurrentBackgroundBounds(); + } + + private void applyCurrentBackgroundBounds() { + mScrimController.setExcludedBackgroundArea(mCurrentBounds); + invalidate(); + } + + /** + * Update the background bounds to the new desired bounds + */ + private void updateBackgroundBounds() { + mBackgroundBounds.left = (int) getX(); + mBackgroundBounds.right = (int) (getX() + getWidth()); + if (!mIsExpanded) { + mBackgroundBounds.top = 0; + mBackgroundBounds.bottom = 0; + } + ActivatableNotificationView firstView = mFirstVisibleBackgroundChild; + int top = 0; + if (firstView != null) { + int finalTranslationY = (int) StackStateAnimator.getFinalTranslationY(firstView); + if (mAnimateNextBackgroundTop + || mTopAnimator == null && mCurrentBounds.top == finalTranslationY + || mTopAnimator != null && mEndAnimationRect.top == finalTranslationY) { + // we're ending up at the same location as we are now, lets just skip the animation + top = finalTranslationY; + } else { + top = (int) firstView.getTranslationY(); + } + } + ActivatableNotificationView lastView = mLastVisibleBackgroundChild; + int bottom = 0; + if (lastView != null) { + int finalTranslationY = (int) StackStateAnimator.getFinalTranslationY(lastView); + int finalHeight = StackStateAnimator.getFinalActualHeight(lastView); + int finalBottom = finalTranslationY + finalHeight; + finalBottom = Math.min(finalBottom, getHeight()); + if (mAnimateNextBackgroundBottom + || mBottomAnimator == null && mCurrentBounds.bottom == finalBottom + || mBottomAnimator != null && mEndAnimationRect.bottom == finalBottom) { + // we're ending up at the same location as we are now, lets just skip the animation + bottom = finalBottom; + } else { + bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()); + bottom = Math.min(bottom, getHeight()); + } + } + mBackgroundBounds.top = top; + mBackgroundBounds.bottom = bottom; + } + + private ActivatableNotificationView getFirstPinnedHeadsUp() { + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (child.getVisibility() != View.GONE + && child instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) child; + if (row.isPinned()) { + return row; + } + } + } + return null; + } + + private ActivatableNotificationView getLastChildWithBackground() { + int childCount = getChildCount(); + for (int i = childCount - 1; i >= 0; i--) { + View child = getChildAt(i); + if (child.getVisibility() != View.GONE + && child instanceof ActivatableNotificationView) { + return (ActivatableNotificationView) child; + } + } + return null; + } + + private ActivatableNotificationView getFirstChildWithBackground() { + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (child.getVisibility() != View.GONE + && child instanceof ActivatableNotificationView) { + return (ActivatableNotificationView) child; + } + } + return null; + } + /** * Fling the scroll view * @@ -1489,7 +1753,7 @@ public class NotificationStackScrollLayout extends ViewGroup */ public void updateTopPadding(float qsHeight, int scrollY, boolean animate, boolean ignoreIntrinsicPadding) { - float start = qsHeight - scrollY + mNotificationTopPadding; + float start = qsHeight - scrollY; float stackHeight = getHeight() - start; int minStackHeight = getMinStackHeight(); if (stackHeight <= minStackHeight) { @@ -1505,10 +1769,6 @@ public class NotificationStackScrollLayout extends ViewGroup setStackHeight(mLastSetStackHeight); } - public int getNotificationTopPadding() { - return mNotificationTopPadding; - } - public int getMinStackHeight() { final ExpandableView firstChild = getFirstChildNotGone(); final int firstChildMinHeight = firstChild != null ? firstChild.getMinHeight() @@ -1655,7 +1915,7 @@ public class NotificationStackScrollLayout extends ViewGroup } ((ExpandableView) child).setOnHeightChangedListener(null); mCurrentStackScrollState.removeViewStateForView(child); - updateScrollStateForRemovedChild(child); + updateScrollStateForRemovedChild((ExpandableView) child); boolean animationGenerated = generateRemoveAnimation(child); if (animationGenerated && !mSwipedOutViews.contains(child)) { // Add this view to an overlay in order to ensure that it will still be temporary @@ -1754,9 +2014,12 @@ public class NotificationStackScrollLayout extends ViewGroup * * @param removedChild the removed child */ - private void updateScrollStateForRemovedChild(View removedChild) { + private void updateScrollStateForRemovedChild(ExpandableView removedChild) { int startingPosition = getPositionInLinearLayout(removedChild); - int childHeight = getIntrinsicHeight(removedChild) + mPaddingBetweenElements; + int padding = removedChild.needsIncreasedPadding() + ? mIncreasedPaddingBetweenElements : + mPaddingBetweenElements; + int childHeight = getIntrinsicHeight(removedChild) + padding; int endPosition = startingPosition + childHeight; if (endPosition <= mOwnScrollY) { // This child is fully scrolled of the top, so we have to deduct its height from the @@ -1779,16 +2042,25 @@ public class NotificationStackScrollLayout extends ViewGroup private int getPositionInLinearLayout(View requestedChild) { int position = 0; + boolean previousNeedsIncreasedPaddings = false; for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); + ExpandableView child = (ExpandableView) getChildAt(i); + boolean notGone = child.getVisibility() != View.GONE; + if (notGone) { + boolean needsIncreasedPaddings = child.needsIncreasedPadding(); + if (position != 0) { + int padding = needsIncreasedPaddings || previousNeedsIncreasedPaddings + ? mIncreasedPaddingBetweenElements : + mPaddingBetweenElements; + position += padding; + } + previousNeedsIncreasedPaddings = needsIncreasedPaddings; + } if (child == requestedChild) { return position; } - if (child.getVisibility() != View.GONE) { + if (notGone) { position += getIntrinsicHeight(child); - if (i < getChildCount()-1) { - position += mPaddingBetweenElements; - } } } return 0; @@ -1800,6 +2072,20 @@ public class NotificationStackScrollLayout extends ViewGroup onViewAddedInternal(child); } + private void updateFirstAndLastBackgroundViews() { + ActivatableNotificationView firstChild = getFirstChildWithBackground(); + ActivatableNotificationView lastChild = getLastChildWithBackground(); + if (mAnimationsEnabled && mIsExpanded) { + mAnimateNextBackgroundTop = firstChild != mFirstVisibleBackgroundChild; + mAnimateNextBackgroundBottom = lastChild != mLastVisibleBackgroundChild; + } else { + mAnimateNextBackgroundTop = false; + mAnimateNextBackgroundBottom = false; + } + mFirstVisibleBackgroundChild = firstChild; + mLastVisibleBackgroundChild = lastChild; + } + private void onViewAddedInternal(View child) { updateHideSensitiveForChild(child); mStackScrollAlgorithm.notifyChildrenChanged(this); @@ -1910,7 +2196,9 @@ public class NotificationStackScrollLayout extends ViewGroup if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) { mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState, mGoToFullShadeDelay); + setAnimationRunning(true); mAnimationEvents.clear(); + updateBackground(); } else { applyCurrentState(); } @@ -2396,6 +2684,7 @@ public class NotificationStackScrollLayout extends ViewGroup } public void onChildAnimationFinished() { + setAnimationRunning(false); requestChildrenUpdate(); runAnimationFinishedRunnables(); clearViewOverlays(); @@ -2418,16 +2707,38 @@ public class NotificationStackScrollLayout extends ViewGroup * See {@link AmbientState#setDimmed}. */ public void setDimmed(boolean dimmed, boolean animate) { - mStackScrollAlgorithm.setDimmed(dimmed); mAmbientState.setDimmed(dimmed); - updatePadding(dimmed); if (animate && mAnimationsEnabled) { mDimmedNeedsAnimation = true; mNeedsAnimation = true; + animateDimmed(dimmed); + } else { + setDimAmount(dimmed ? 1.0f : 0.0f); } requestChildrenUpdate(); } + private void setDimAmount(float dimAmount) { + mDimAmount = dimAmount; + updateBackgroundDimming(); + } + + private void animateDimmed(boolean dimmed) { + if (mDimAnimator != null) { + mDimAnimator.cancel(); + } + float target = dimmed ? 1.0f : 0.0f; + if (target == mDimAmount) { + return; + } + mDimAnimator = TimeAnimator.ofFloat(mDimAmount, target); + mDimAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED); + mDimAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + mDimAnimator.addListener(mDimEndListener); + mDimAnimator.addUpdateListener(mDimUpdateListener); + mDimAnimator.start(); + } + public void setHideSensitive(boolean hideSensitive, boolean animate) { if (hideSensitive != mAmbientState.isHideSensitive()) { int childCount = getChildCount(); @@ -2466,30 +2777,10 @@ public class NotificationStackScrollLayout extends ViewGroup mListener.onChildLocationsChanged(this); } runAnimationFinishedRunnables(); - } - - public void setSpeedBumpView(SpeedBumpView speedBumpView) { - mSpeedBumpView = speedBumpView; - addView(speedBumpView); - } - - private void updateSpeedBump(boolean visible) { - boolean notGoneBefore = mSpeedBumpView.getVisibility() != GONE; - if (visible != notGoneBefore) { - int newVisibility = visible ? VISIBLE : GONE; - mSpeedBumpView.setVisibility(newVisibility); - if (visible) { - // Make invisible to ensure that the appear animation is played. - mSpeedBumpView.setInvisible(); - } else { - // TODO: This doesn't really work, because the view is already set to GONE above. - generateRemoveAnimation(mSpeedBumpView); - } - } + updateBackground(); } public void goToFullShade(long delay) { - updateSpeedBump(true /* visibility */); mDismissView.setInvisible(); mEmptyShadeView.setInvisible(); mGoToFullShadeNeedsAnimation = true; @@ -2533,6 +2824,14 @@ public class NotificationStackScrollLayout extends ViewGroup mNeedsAnimation = true; } requestChildrenUpdate(); + if (dark) { + setWillNotDraw(!DEBUG); + mScrimController.setExcludedBackgroundArea(null); + } else { + updateBackground(); + setWillNotDraw(false); + // TODO: fade in background + } } private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) { @@ -2731,7 +3030,7 @@ public class NotificationStackScrollLayout extends ViewGroup } public int getDismissViewHeight() { - int height = mDismissView.getHeight() + mPaddingBetweenElementsNormal; + int height = mDismissView.getHeight() + mPaddingBetweenElements; // Hack: Accommodate for additional distance when we only have one notification and the // dismiss all button. @@ -2881,6 +3180,12 @@ public class NotificationStackScrollLayout extends ViewGroup public void setScrimController(ScrimController scrimController) { mScrimController = scrimController; + mScrimController.setScrimBehindChangeRunnable(new Runnable() { + @Override + public void run() { + updateBackgroundDimming(); + } + }); } public void forceNoOverlappingRendering(boolean force) { @@ -2892,6 +3197,17 @@ public class NotificationStackScrollLayout extends ViewGroup return !mForceNoOverlappingRendering && super.hasOverlappingRendering(); } + public void setAnimationRunning(boolean animationRunning) { + if (animationRunning != mAnimationRunning) { + if (animationRunning) { + getViewTreeObserver().addOnPreDrawListener(mBackgroundUpdater); + } else { + getViewTreeObserver().removeOnPreDrawListener(mBackgroundUpdater); + } + mAnimationRunning = animationRunning; + } + } + /** * A listener that is notified when some child locations might have changed. */ @@ -2937,7 +3253,7 @@ public class NotificationStackScrollLayout extends ViewGroup // ANIMATION_TYPE_ADD new AnimationFilter() - .animateAlpha() + .animateShadowAlpha() .animateHeight() .animateTopInset() .animateY() @@ -2946,7 +3262,7 @@ public class NotificationStackScrollLayout extends ViewGroup // ANIMATION_TYPE_REMOVE new AnimationFilter() - .animateAlpha() + .animateShadowAlpha() .animateHeight() .animateTopInset() .animateY() @@ -2955,7 +3271,7 @@ public class NotificationStackScrollLayout extends ViewGroup // ANIMATION_TYPE_REMOVE_SWIPED_OUT new AnimationFilter() - .animateAlpha() + .animateShadowAlpha() .animateHeight() .animateTopInset() .animateY() @@ -2964,37 +3280,35 @@ public class NotificationStackScrollLayout extends ViewGroup // ANIMATION_TYPE_TOP_PADDING_CHANGED new AnimationFilter() - .animateAlpha() + .animateShadowAlpha() .animateHeight() .animateTopInset() .animateY() .animateDimmed() - .animateScale() .animateZ(), // ANIMATION_TYPE_START_DRAG new AnimationFilter() - .animateAlpha(), + .animateShadowAlpha(), // ANIMATION_TYPE_SNAP_BACK new AnimationFilter() - .animateAlpha() + .animateShadowAlpha() .animateHeight(), // ANIMATION_TYPE_ACTIVATED_CHILD new AnimationFilter() - .animateScale() - .animateAlpha(), + .animateZ(), // ANIMATION_TYPE_DIMMED new AnimationFilter() .animateY() - .animateScale() .animateDimmed(), // ANIMATION_TYPE_CHANGE_POSITION new AnimationFilter() - .animateAlpha() + .animateAlpha() // maybe the children change positions + .animateShadowAlpha() .animateHeight() .animateTopInset() .animateY() @@ -3007,12 +3321,11 @@ public class NotificationStackScrollLayout extends ViewGroup // ANIMATION_TYPE_GO_TO_FULL_SHADE new AnimationFilter() - .animateAlpha() + .animateShadowAlpha() .animateHeight() .animateTopInset() .animateY() .animateDimmed() - .animateScale() .animateZ() .hasDelays(), @@ -3022,7 +3335,7 @@ public class NotificationStackScrollLayout extends ViewGroup // ANIMATION_TYPE_VIEW_RESIZE new AnimationFilter() - .animateAlpha() + .animateShadowAlpha() .animateHeight() .animateTopInset() .animateY() @@ -3031,6 +3344,7 @@ public class NotificationStackScrollLayout extends ViewGroup // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED new AnimationFilter() .animateAlpha() + .animateShadowAlpha() .animateHeight() .animateTopInset() .animateY() @@ -3038,7 +3352,7 @@ public class NotificationStackScrollLayout extends ViewGroup // ANIMATION_TYPE_HEADS_UP_APPEAR new AnimationFilter() - .animateAlpha() + .animateShadowAlpha() .animateHeight() .animateTopInset() .animateY() @@ -3046,7 +3360,7 @@ public class NotificationStackScrollLayout extends ViewGroup // ANIMATION_TYPE_HEADS_UP_DISAPPEAR new AnimationFilter() - .animateAlpha() + .animateShadowAlpha() .animateHeight() .animateTopInset() .animateY() @@ -3054,7 +3368,7 @@ public class NotificationStackScrollLayout extends ViewGroup // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK new AnimationFilter() - .animateAlpha() + .animateShadowAlpha() .animateHeight() .animateTopInset() .animateY() @@ -3063,7 +3377,7 @@ public class NotificationStackScrollLayout extends ViewGroup // ANIMATION_TYPE_HEADS_UP_OTHER new AnimationFilter() - .animateAlpha() + .animateShadowAlpha() .animateHeight() .animateTopInset() .animateY() @@ -3072,8 +3386,8 @@ public class NotificationStackScrollLayout extends ViewGroup // ANIMATION_TYPE_EVERYTHING new AnimationFilter() .animateAlpha() + .animateShadowAlpha() .animateDark() - .animateScale() .animateDimmed() .animateHideSensitive() .animateHeight() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java index 822012d8bd68..f6959f0dfcbb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.stack; import android.content.Context; -import android.util.DisplayMetrics; import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -27,6 +26,7 @@ import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.ExpandableView; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; /** @@ -41,15 +41,13 @@ public class StackScrollAlgorithm { private static final int MAX_ITEMS_IN_BOTTOM_STACK = 3; private static final int MAX_ITEMS_IN_TOP_STACK = 3; - public static final float DIMMED_SCALE = 0.98f; - private int mPaddingBetweenElements; + private int mIncreasedPaddingBetweenElements; private int mCollapsedSize; private int mTopStackPeekSize; private int mBottomStackPeekSize; private int mZDistanceBetweenElements; private int mZBasicHeight; - private int mRoundedRectCornerRadius; private StackIndentationFunctor mTopStackIndentationFunctor; private StackIndentationFunctor mBottomStackIndentationFunctor; @@ -61,16 +59,11 @@ public class StackScrollAlgorithm { private ExpandableView mFirstChildWhileExpanding; private boolean mExpandedOnStart; private int mTopStackTotalSize; - private int mPaddingBetweenElementsDimmed; - private int mPaddingBetweenElementsNormal; - private int mNotificationsTopPadding; private int mBottomStackSlowDownLength; private int mTopStackSlowDownLength; private int mCollapseSecondCardPadding; - private boolean mScaleDimmed; private ExpandableView mFirstChild; private int mFirstChildMinHeight; - private boolean mDimmed; public StackScrollAlgorithm(Context context) { initView(context); @@ -82,9 +75,6 @@ public class StackScrollAlgorithm { } private void updatePadding() { - mPaddingBetweenElements = mDimmed && mScaleDimmed - ? mPaddingBetweenElementsDimmed - : mPaddingBetweenElementsNormal; mTopStackTotalSize = mTopStackSlowDownLength + mPaddingBetweenElements + mTopStackPeekSize; mTopStackIndentationFunctor = new PiecewiseLinearIndentationFunctor( @@ -104,35 +94,25 @@ public class StackScrollAlgorithm { } private void initConstants(Context context) { - mPaddingBetweenElementsDimmed = context.getResources() - .getDimensionPixelSize(R.dimen.notification_padding_dimmed); - mPaddingBetweenElementsNormal = context.getResources() - .getDimensionPixelSize(R.dimen.notification_padding); - mNotificationsTopPadding = context.getResources() - .getDimensionPixelSize(R.dimen.notifications_top_padding); + mPaddingBetweenElements = Math.max(1, context.getResources() + .getDimensionPixelSize(R.dimen.notification_divider_height)); + mIncreasedPaddingBetweenElements = context.getResources() + .getDimensionPixelSize(R.dimen.notification_divider_height_increased); mCollapsedSize = context.getResources() .getDimensionPixelSize(R.dimen.notification_min_height); mTopStackPeekSize = context.getResources() .getDimensionPixelSize(R.dimen.top_stack_peek_amount); mBottomStackPeekSize = context.getResources() .getDimensionPixelSize(R.dimen.bottom_stack_peek_amount); - mZDistanceBetweenElements = context.getResources() - .getDimensionPixelSize(R.dimen.z_distance_between_notifications); + mZDistanceBetweenElements = Math.max(1, context.getResources() + .getDimensionPixelSize(R.dimen.z_distance_between_notifications)); mZBasicHeight = (MAX_ITEMS_IN_BOTTOM_STACK + 1) * mZDistanceBetweenElements; mBottomStackSlowDownLength = context.getResources() .getDimensionPixelSize(R.dimen.bottom_stack_slow_down_length); mTopStackSlowDownLength = context.getResources() .getDimensionPixelSize(R.dimen.top_stack_slow_down_length); - mRoundedRectCornerRadius = context.getResources().getDimensionPixelSize( - R.dimen.notification_material_rounded_rect_radius); mCollapseSecondCardPadding = context.getResources().getDimensionPixelSize( R.dimen.notification_collapse_second_card_padding); - mScaleDimmed = context.getResources().getDisplayMetrics().densityDpi - >= DisplayMetrics.DENSITY_420; - } - - public boolean shouldScaleDimmed() { - return mScaleDimmed; } public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) { @@ -159,7 +139,7 @@ public class StackScrollAlgorithm { scrollY = Math.max(0, scrollY); algorithmState.scrollY = (int) (scrollY + mFirstChildMinHeight + bottomOverScroll); - updateVisibleChildren(resultState, algorithmState); + initAlgorithmState(resultState, algorithmState); // Phase 1: findNumberOfItemsInTopStackAndUpdateState(resultState, algorithmState, ambientState); @@ -212,8 +192,8 @@ public class StackScrollAlgorithm { for (int i = 0; i < childCount; i++) { ExpandableView child = algorithmState.visibleChildren.get(i); StackViewState state = resultState.getViewStateForView(child); - float newYTranslation = state.yTranslation + state.height * (1f - state.scale) / 2f; - float newHeight = state.height * state.scale; + float newYTranslation = state.yTranslation; + float newHeight = state.height; // apply clipping and shadow float newNotificationEnd = newYTranslation + newHeight; @@ -225,16 +205,6 @@ public class StackScrollAlgorithm { } else { clipHeight = newNotificationEnd - previousNotificationEnd; clipHeight = Math.max(0.0f, clipHeight); - if (clipHeight != 0.0f) { - - // In the unlocked shade we have to clip a little bit higher because of the rounded - // corners of the notifications, but only if we are not fully overlapped by - // the top card. - float clippingCorrection = state.dimmed - ? 0 - : mRoundedRectCornerRadius * state.scale; - clipHeight += clippingCorrection; - } } updateChildClippingAndBackground(state, newHeight, clipHeight, @@ -252,7 +222,7 @@ public class StackScrollAlgorithm { } else { previousNotificationIsSwiped = ambientState.getDraggedViews().contains(child); previousNotificationEnd = newNotificationEnd; - previousNotificationStart = newYTranslation + state.clipTopAmount * state.scale; + previousNotificationStart = newYTranslation + state.clipTopAmount; } } } @@ -276,13 +246,13 @@ public class StackScrollAlgorithm { float clipHeight, float backgroundHeight) { if (realHeight > clipHeight) { // Rather overlap than create a hole. - state.topOverLap = (int) Math.floor((realHeight - clipHeight) / state.scale); + state.topOverLap = (int) Math.floor(realHeight - clipHeight); } else { state.topOverLap = 0; } if (realHeight > backgroundHeight) { // Rather overlap than create a hole. - state.clipTopAmount = (int) Math.floor((realHeight - backgroundHeight) / state.scale); + state.clipTopAmount = (int) Math.floor(realHeight - backgroundHeight); } else { state.clipTopAmount = 0; } @@ -305,9 +275,6 @@ public class StackScrollAlgorithm { childViewState.dark = dark; childViewState.hideSensitive = hideSensitive; boolean isActivatedChild = activatedChild == child; - childViewState.scale = !mScaleDimmed || !dimmed || isActivatedChild - ? 1.0f - : DIMMED_SCALE; if (dimmed && isActivatedChild) { childViewState.zTranslation += 2.0f * mZDistanceBetweenElements; } @@ -331,7 +298,8 @@ public class StackScrollAlgorithm { nextChild); // The child below the dragged one must be fully visible if (ambientState.isShadeExpanded()) { - viewState.alpha = 1; + viewState.shadowAlpha = 1; + viewState.hidden = false; } } @@ -344,19 +312,28 @@ public class StackScrollAlgorithm { } /** - * Update the visible children on the state. + * Initialize the algorithm state like updating the visible children. */ - private void updateVisibleChildren(StackScrollState resultState, + private void initAlgorithmState(StackScrollState resultState, StackScrollAlgorithmState state) { ViewGroup hostView = resultState.getHostView(); int childCount = hostView.getChildCount(); state.visibleChildren.clear(); state.visibleChildren.ensureCapacity(childCount); + state.increasedPaddingSet.clear(); int notGoneIndex = 0; + ExpandableView lastView = null; for (int i = 0; i < childCount; i++) { ExpandableView v = (ExpandableView) hostView.getChildAt(i); if (v.getVisibility() != View.GONE) { notGoneIndex = updateNotGoneIndex(resultState, state, notGoneIndex, v); + boolean needsIncreasedPadding = v.needsIncreasedPadding(); + if (needsIncreasedPadding) { + state.increasedPaddingSet.add(v); + if (lastView != null) { + state.increasedPaddingSet.add(lastView); + } + } if (v instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) v; @@ -374,6 +351,7 @@ public class StackScrollAlgorithm { } } } + lastView = v; } } } @@ -414,15 +392,17 @@ public class StackScrollAlgorithm { int numberOfElementsCompletelyIn = algorithmState.partialInTop == 1.0f ? algorithmState.lastTopStackIndex : (int) algorithmState.itemsInTopStack; + int paddingAfterChild; for (int i = 0; i < childCount; i++) { ExpandableView child = algorithmState.visibleChildren.get(i); StackViewState childViewState = resultState.getViewStateForView(child); childViewState.location = StackViewState.LOCATION_UNKNOWN; + paddingAfterChild = getPaddingAfterChild(algorithmState, child); int childHeight = getMaxAllowedChildHeight(child); int minHeight = child.getMinHeight(); float yPositionInScrollViewAfterElement = yPositionInScrollView + childHeight - + mPaddingBetweenElements; + + paddingAfterChild; float scrollOffset = yPositionInScrollView - algorithmState.scrollY + mFirstChildMinHeight; @@ -437,20 +417,20 @@ public class StackScrollAlgorithm { // The y position after this element float nextYPosition = currentYPosition + childHeight + - mPaddingBetweenElements; + paddingAfterChild; if (i <= algorithmState.lastTopStackIndex) { // Case 1: // We are in the top Stack - updateStateForTopStackChild(algorithmState, + updateStateForTopStackChild(algorithmState, child, numberOfElementsCompletelyIn, i, childHeight, childViewState, scrollOffset); clampPositionToTopStackEnd(childViewState, childHeight); // check if we are overlapping with the bottom stack - if (childViewState.yTranslation + childHeight + mPaddingBetweenElements + if (childViewState.yTranslation + childHeight + paddingAfterChild >= bottomStackStart && !mIsExpansionChanging && i != 0) { // we just collapse this element slightly - int newSize = (int) Math.max(bottomStackStart - mPaddingBetweenElements - + int newSize = (int) Math.max(bottomStackStart - paddingAfterChild - childViewState.yTranslation, minHeight); childViewState.height = newSize; updateStateForChildTransitioningInBottom(algorithmState, bottomStackStart, @@ -466,7 +446,7 @@ public class StackScrollAlgorithm { // According to the regular scroll view we are fully translated out of the // bottom of the screen so we are fully in the bottom stack updateStateForChildFullyInBottomStack(algorithmState, - bottomStackStart, childViewState, minHeight, ambientState); + bottomStackStart, childViewState, minHeight, ambientState, child); } else { // According to the regular scroll view we are currently translating out of / // into the bottom of the screen @@ -483,7 +463,8 @@ public class StackScrollAlgorithm { // The first card is always rendered. if (i == 0) { - childViewState.alpha = 1.0f; + childViewState.hidden = false; + childViewState.shadowAlpha = 1.0f; childViewState.yTranslation = Math.max( mFirstChildMinHeight - algorithmState.scrollY, 0); if (childViewState.yTranslation + childViewState.height @@ -497,7 +478,7 @@ public class StackScrollAlgorithm { if (childViewState.location == StackViewState.LOCATION_UNKNOWN) { Log.wtf(LOG_TAG, "Failed to assign location for child " + i); } - currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements; + currentYPosition = childViewState.yTranslation + childHeight + paddingAfterChild; yPositionInScrollView = yPositionInScrollViewAfterElement; childViewState.yTranslation += ambientState.getTopPadding() @@ -506,6 +487,13 @@ public class StackScrollAlgorithm { updateHeadsUpStates(resultState, algorithmState, ambientState); } + private int getPaddingAfterChild(StackScrollAlgorithmState algorithmState, + ExpandableView child) { + return algorithmState.increasedPaddingSet.contains(child) + ? mIncreasedPaddingBetweenElements + : mPaddingBetweenElements; + } + private void updateHeadsUpStates(StackScrollState resultState, StackScrollAlgorithmState algorithmState, AmbientState ambientState) { int childCount = algorithmState.visibleChildren.size(); @@ -530,8 +518,7 @@ public class StackScrollAlgorithm { bottomPosition); } if (row.isPinned()) { - childState.yTranslation = Math.max(childState.yTranslation, - mNotificationsTopPadding); + childState.yTranslation = Math.max(childState.yTranslation, 0); childState.height = Math.max(row.getIntrinsicHeight(), childState.height); if (!isTopEntry) { // Ensure that a headsUp doesn't vertically extend further than the heads-up at @@ -610,7 +597,7 @@ public class StackScrollAlgorithm { // This is the transitioning element on top of bottom stack, calculate how far we are in. algorithmState.partialInBottom = 1.0f - ( (transitioningPositionStart - currentYPosition) / (childHeight + - mPaddingBetweenElements)); + getPaddingAfterChild(algorithmState, child))); // the offset starting at the transitionPosition of the bottom stack float offset = mBottomStackIndentationFunctor.getValue(algorithmState.partialInBottom); @@ -618,12 +605,12 @@ public class StackScrollAlgorithm { int newHeight = childHeight; if (childHeight > child.getMinHeight()) { newHeight = (int) Math.max(Math.min(transitioningPositionStart + offset - - mPaddingBetweenElements - currentYPosition, childHeight), + getPaddingAfterChild(algorithmState, child) - currentYPosition, childHeight), child.getMinHeight()); childViewState.height = newHeight; } childViewState.yTranslation = transitioningPositionStart + offset - newHeight - - mPaddingBetweenElements; + - getPaddingAfterChild(algorithmState, child); // We want at least to be at the end of the top stack when collapsing clampPositionToTopStackEnd(childViewState, newHeight); @@ -632,22 +619,23 @@ public class StackScrollAlgorithm { private void updateStateForChildFullyInBottomStack(StackScrollAlgorithmState algorithmState, float transitioningPositionStart, StackViewState childViewState, - int minHeight, AmbientState ambientState) { + int minHeight, AmbientState ambientState, ExpandableView child) { float currentYPosition; algorithmState.itemsInBottomStack += 1.0f; if (algorithmState.itemsInBottomStack < MAX_ITEMS_IN_BOTTOM_STACK) { // We are visually entering the bottom stack currentYPosition = transitioningPositionStart + mBottomStackIndentationFunctor.getValue(algorithmState.itemsInBottomStack) - - mPaddingBetweenElements; + - getPaddingAfterChild(algorithmState, child); childViewState.location = StackViewState.LOCATION_BOTTOM_STACK_PEEKING; } else { // we are fully inside the stack if (algorithmState.itemsInBottomStack > MAX_ITEMS_IN_BOTTOM_STACK + 2) { - childViewState.alpha = 0.0f; + childViewState.hidden = true; + childViewState.shadowAlpha = 0.0f; } else if (algorithmState.itemsInBottomStack > MAX_ITEMS_IN_BOTTOM_STACK + 1) { - childViewState.alpha = 1.0f - algorithmState.partialInBottom; + childViewState.shadowAlpha = 1.0f - algorithmState.partialInBottom; } childViewState.location = StackViewState.LOCATION_BOTTOM_STACK_HIDDEN; currentYPosition = ambientState.getInnerHeight(); @@ -658,7 +646,7 @@ public class StackScrollAlgorithm { } private void updateStateForTopStackChild(StackScrollAlgorithmState algorithmState, - int numberOfElementsCompletelyIn, int i, int childHeight, + ExpandableView child, int numberOfElementsCompletelyIn, int i, int childHeight, StackViewState childViewState, float scrollOffset) { @@ -669,10 +657,11 @@ public class StackScrollAlgorithm { if (paddedIndex >= 0) { // We are currently visually entering the top stack - float distanceToStack = (childHeight + mPaddingBetweenElements) + float distanceToStack = (childHeight + getPaddingAfterChild(algorithmState, child)) - algorithmState.scrolledPixelsTop; if (i == algorithmState.lastTopStackIndex - && distanceToStack > (mTopStackTotalSize + mPaddingBetweenElements)) { + && distanceToStack > (mTopStackTotalSize + + getPaddingAfterChild(algorithmState, child))) { // Child is currently translating into stack but not yet inside slow down zone. // Handle it like the regular scrollview. @@ -682,7 +671,8 @@ public class StackScrollAlgorithm { float numItemsBefore; if (i == algorithmState.lastTopStackIndex) { numItemsBefore = 1.0f - - (distanceToStack / (mTopStackTotalSize + mPaddingBetweenElements)); + - (distanceToStack / (mTopStackTotalSize + + getPaddingAfterChild(algorithmState, child))); } else { numItemsBefore = algorithmState.itemsInTopStack - i; } @@ -694,10 +684,11 @@ public class StackScrollAlgorithm { childViewState.location = StackViewState.LOCATION_TOP_STACK_PEEKING; } else { if (paddedIndex == -1) { - childViewState.alpha = 1.0f - algorithmState.partialInTop; + childViewState.shadowAlpha = 1.0f - algorithmState.partialInTop; } else { // We are hidden behind the top card and faded out, so we can hide ourselves. - childViewState.alpha = 0.0f; + childViewState.hidden = true; + childViewState.shadowAlpha = 0.0f; } childViewState.yTranslation = mFirstChildMinHeight - childHeight; childViewState.location = StackViewState.LOCATION_TOP_STACK_HIDDEN; @@ -718,15 +709,15 @@ public class StackScrollAlgorithm { // The y Position if the element would be in a regular scrollView float yPositionInScrollView = 0.0f; int childCount = algorithmState.visibleChildren.size(); - // find the number of elements in the top stack. for (int i = 0; i < childCount; i++) { ExpandableView child = algorithmState.visibleChildren.get(i); StackViewState childViewState = resultState.getViewStateForView(child); int childHeight = getMaxAllowedChildHeight(child); + int paddingAfterChild = getPaddingAfterChild(algorithmState, child); float yPositionInScrollViewAfterElement = yPositionInScrollView + childHeight - + mPaddingBetweenElements; + + paddingAfterChild; if (yPositionInScrollView < algorithmState.scrollY) { if (i == 0 && algorithmState.scrollY <= mFirstChildMinHeight) { @@ -754,7 +745,7 @@ public class StackScrollAlgorithm { algorithmState.scrolledPixelsTop = algorithmState.scrollY - yPositionInScrollView; algorithmState.partialInTop = (algorithmState.scrolledPixelsTop) / (childHeight - + mPaddingBetweenElements); + + paddingAfterChild); // Our element can be expanded, so this can get negative algorithmState.partialInTop = Math.max(0.0f, algorithmState.partialInTop); @@ -763,7 +754,7 @@ public class StackScrollAlgorithm { if (i == 0) { // If it is expanded we have to collapse it to a new size float newSize = yPositionInScrollViewAfterElement - - mPaddingBetweenElements + - paddingAfterChild - algorithmState.scrollY + mFirstChildMinHeight; newSize = Math.max(mFirstChildMinHeight, newSize); algorithmState.itemsInTopStack = 1.0f; @@ -917,11 +908,6 @@ public class StackScrollAlgorithm { } } - public void setDimmed(boolean dimmed) { - mDimmed = dimmed; - updatePadding(); - } - public void onReset(ExpandableView view) { if (view.equals(mFirstChildWhileExpanding)) { updateFirstChildMaxSizeToMaxHeight(); @@ -969,6 +955,11 @@ public class StackScrollAlgorithm { * The children from the host view which are not gone. */ public final ArrayList<ExpandableView> visibleChildren = new ArrayList<ExpandableView>(); + + /** + * The children from the host that need an increased padding after them. + */ + public final HashSet<ExpandableView> increasedPaddingSet = new HashSet<>(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java index e155d7008313..1fedc1e40ac3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java @@ -25,7 +25,6 @@ import com.android.systemui.statusbar.DismissView; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.ExpandableView; -import com.android.systemui.statusbar.SpeedBumpView; import java.util.HashMap; import java.util.List; @@ -83,8 +82,10 @@ public class StackScrollState { // initialize with the default values of the view viewState.height = view.getIntrinsicHeight(); viewState.gone = view.getVisibility() == View.GONE; - viewState.alpha = 1; + viewState.alpha = 1f; + viewState.shadowAlpha = 1f; viewState.notGoneIndex = -1; + viewState.hidden = false; } public StackViewState getViewStateForView(View requestedView) { @@ -107,9 +108,7 @@ public class StackScrollState { if (!applyState(child, state)) { continue; } - if(child instanceof SpeedBumpView) { - performSpeedBumpAnimation(i, (SpeedBumpView) child, state, 0); - } else if (child instanceof DismissView) { + if (child instanceof DismissView) { DismissView dismissView = (DismissView) child; boolean visible = state.topOverLap < mClearAllTopPadding; dismissView.performVisibilityAnimation(visible && !dismissView.willBeGone()); @@ -146,6 +145,14 @@ public class StackScrollState { view.setActualHeight(newHeight, false /* notifyListeners */); } + float shadowAlpha = view.getShadowAlpha(); + float newShadowAlpha = state.shadowAlpha; + + // apply shadowAlpha + if (shadowAlpha != newShadowAlpha) { + view.setShadowAlpha(newShadowAlpha); + } + // apply dimming view.setDimmed(state.dimmed, false /* animate */); @@ -183,12 +190,10 @@ public class StackScrollState { float yTranslation = view.getTranslationY(); float xTranslation = view.getTranslationX(); float zTranslation = view.getTranslationZ(); - float scale = view.getScaleX(); float newAlpha = state.alpha; float newYTranslation = state.yTranslation; float newZTranslation = state.zTranslation; - float newScale = state.scale; - boolean becomesInvisible = newAlpha == 0.0f; + boolean becomesInvisible = newAlpha == 0.0f || state.hidden; if (alpha != newAlpha && xTranslation == 0) { // apply layer type boolean becomesFullyVisible = newAlpha == 1.0f; @@ -225,34 +230,5 @@ public class StackScrollState { if (zTranslation != newZTranslation) { view.setTranslationZ(newZTranslation); } - - // apply scale - if (scale != newScale) { - view.setScaleX(newScale); - view.setScaleY(newScale); - } } - - public void performSpeedBumpAnimation(int i, SpeedBumpView speedBump, StackViewState state, - long delay) { - View nextChild = getNextChildNotGone(i); - if (nextChild != null) { - float lineEnd = state.yTranslation + state.height / 2; - StackViewState nextState = getViewStateForView(nextChild); - boolean startIsAboveNext = nextState.yTranslation > lineEnd; - speedBump.animateDivider(startIsAboveNext, delay, null /* onFinishedRunnable */); - } - } - - private View getNextChildNotGone(int childIndex) { - int childCount = mHostView.getChildCount(); - for (int i = childIndex + 1; i < childCount; i++) { - View child = mHostView.getChildAt(i); - if (child.getVisibility() != View.GONE) { - return child; - } - } - return null; - } - } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java index 9d5e072a0251..e75e8e856455 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java @@ -22,13 +22,12 @@ import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; import android.view.View; -import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import com.android.systemui.R; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.ExpandableView; -import com.android.systemui.statusbar.SpeedBumpView; +import com.android.systemui.statusbar.Interpolators; import com.android.systemui.statusbar.policy.HeadsUpManager; import java.util.ArrayList; @@ -55,24 +54,23 @@ public class StackStateAnimator { private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag; private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag; - private static final int TAG_ANIMATOR_SCALE = R.id.scale_animator_tag; private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag; private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag; private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag; + private static final int TAG_ANIMATOR_SHADOW_ALPHA = R.id.shadow_alpha_animator_tag; private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag; private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag; - private static final int TAG_END_SCALE = R.id.scale_animator_end_value_tag; private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag; private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag; private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag; + private static final int TAG_END_SHADOW_ALPHA = R.id.shadow_alpha_animator_end_value_tag; private static final int TAG_START_TRANSLATION_Y = R.id.translation_y_animator_start_value_tag; private static final int TAG_START_TRANSLATION_Z = R.id.translation_z_animator_start_value_tag; - private static final int TAG_START_SCALE = R.id.scale_animator_start_value_tag; private static final int TAG_START_ALPHA = R.id.alpha_animator_start_value_tag; private static final int TAG_START_HEIGHT = R.id.height_animator_start_value_tag; private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag; + private static final int TAG_START_SHADOW_ALPHA = R.id.shadow_alpha_animator_start_value_tag; - private final Interpolator mFastOutSlowInInterpolator; private final Interpolator mHeadsUpAppearInterpolator; private final int mGoToFullShadeAppearingTranslation; private final StackViewState mTmpState = new StackViewState(); @@ -99,8 +97,6 @@ public class StackStateAnimator { public StackStateAnimator(NotificationStackScrollLayout hostLayout) { mHostLayout = hostLayout; - mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(hostLayout.getContext(), - android.R.interpolator.fast_out_slow_in); mGoToFullShadeAppearingTranslation = hostLayout.getContext().getResources().getDimensionPixelSize( R.dimen.go_to_full_shade_appearing_translation); @@ -200,7 +196,6 @@ public class StackStateAnimator { */ public void startStackAnimations(final ExpandableView child, StackViewState viewState, StackScrollState finalState, int i, long fixedDelay) { - final float alpha = viewState.alpha; boolean wasAdded = mNewAddChildren.contains(child); long duration = mCurrentLength; if (wasAdded && mAnimationFilter.hasGoToFullShadeEvent) { @@ -212,14 +207,14 @@ public class StackStateAnimator { } boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation; boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation; - boolean scaleChanging = child.getScaleX() != viewState.scale; - boolean alphaChanging = alpha != child.getAlpha(); + boolean alphaChanging = viewState.alpha != child.getAlpha(); boolean heightChanging = viewState.height != child.getActualHeight(); + boolean shadowAlphaChanging = viewState.shadowAlpha != child.getShadowAlpha(); boolean darkChanging = viewState.dark != child.isDark(); boolean topInsetChanging = viewState.clipTopAmount != child.getClipTopAmount(); boolean hasDelays = mAnimationFilter.hasDelays; - boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging || - alphaChanging || heightChanging || topInsetChanging || darkChanging; + boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || alphaChanging + || heightChanging || topInsetChanging || darkChanging || shadowAlphaChanging; long delay = 0; if (fixedDelay != -1) { delay = fixedDelay; @@ -234,6 +229,11 @@ public class StackStateAnimator { startHeightAnimation(child, viewState, duration, delay); } + // start shadow alpha animation + if (shadowAlphaChanging) { + startShadowAlphaAnimation(child, viewState, duration, delay); + } + // start top inset animation if (topInsetChanging) { startInsetAnimation(child, viewState, duration, delay); @@ -255,10 +255,7 @@ public class StackStateAnimator { if (wasAdded) { child.performAddAnimation(delay, mCurrentLength); } - if (child instanceof SpeedBumpView) { - finalState.performSpeedBumpAnimation(i, (SpeedBumpView) child, viewState, - delay + duration); - } else if (child instanceof ExpandableNotificationRow) { + if (child instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) child; row.startChildAnimation(finalState, this, delay, duration); } @@ -275,13 +272,12 @@ public class StackStateAnimator { public void startViewAnimations(View child, ViewState viewState, long delay, long duration) { boolean wasVisible = child.getVisibility() == View.VISIBLE; final float alpha = viewState.alpha; - if (!wasVisible && alpha != 0 && !viewState.gone) { + if (!wasVisible && alpha != 0 && !viewState.gone && !viewState.hidden) { child.setVisibility(View.VISIBLE); } boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation; boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation; - boolean scaleChanging = child.getScaleX() != viewState.scale; - float childAlpha = child.getVisibility() == View.INVISIBLE ? 0.0f : child.getAlpha(); + float childAlpha = child.getAlpha(); boolean alphaChanging = viewState.alpha != childAlpha; if (child instanceof ExpandableView) { // We don't want views to change visibility when they are animating to GONE @@ -298,11 +294,6 @@ public class StackStateAnimator { startZTranslationAnimation(child, viewState, duration, delay); } - // start scale animation - if (scaleChanging) { - startScaleAnimation(child, viewState, duration); - } - // start alpha animation if (alphaChanging && child.getTranslationX() == 0) { startAlphaAnimation(child, viewState, duration, delay); @@ -384,6 +375,64 @@ public class StackStateAnimator { return (long) (index * ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE); } + private void startShadowAlphaAnimation(final ExpandableView child, + StackViewState viewState, long duration, long delay) { + Float previousStartValue = getChildTag(child, TAG_START_SHADOW_ALPHA); + Float previousEndValue = getChildTag(child, TAG_END_SHADOW_ALPHA); + float newEndValue = viewState.shadowAlpha; + if (previousEndValue != null && previousEndValue == newEndValue) { + return; + } + ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SHADOW_ALPHA); + if (!mAnimationFilter.animateShadowAlpha) { + // just a local update was performed + if (previousAnimator != null) { + // we need to increase all animation keyframes of the previous animator by the + // relative change to the end value + PropertyValuesHolder[] values = previousAnimator.getValues(); + float relativeDiff = newEndValue - previousEndValue; + float newStartValue = previousStartValue + relativeDiff; + values[0].setFloatValues(newStartValue, newEndValue); + child.setTag(TAG_START_SHADOW_ALPHA, newStartValue); + child.setTag(TAG_END_SHADOW_ALPHA, newEndValue); + previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); + return; + } else { + // no new animation needed, let's just apply the value + child.setShadowAlpha(newEndValue); + return; + } + } + + ValueAnimator animator = ValueAnimator.ofFloat(child.getShadowAlpha(), newEndValue); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + child.setShadowAlpha((float) animation.getAnimatedValue()); + } + }); + animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); + animator.setDuration(newDuration); + if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { + animator.setStartDelay(delay); + } + animator.addListener(getGlobalAnimationFinishedListener()); + // remove the tag when the animation is finished + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + child.setTag(TAG_ANIMATOR_SHADOW_ALPHA, null); + child.setTag(TAG_START_SHADOW_ALPHA, null); + child.setTag(TAG_END_SHADOW_ALPHA, null); + } + }); + startAnimator(animator); + child.setTag(TAG_ANIMATOR_SHADOW_ALPHA, animator); + child.setTag(TAG_START_SHADOW_ALPHA, child.getShadowAlpha()); + child.setTag(TAG_END_SHADOW_ALPHA, newEndValue); + } + private void startHeightAnimation(final ExpandableView child, StackViewState viewState, long duration, long delay) { Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT); @@ -421,7 +470,7 @@ public class StackStateAnimator { false /* notifyListeners */); } }); - animator.setInterpolator(mFastOutSlowInInterpolator); + animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); animator.setDuration(newDuration); if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { @@ -479,7 +528,7 @@ public class StackStateAnimator { child.setClipTopAmount((int) animation.getAnimatedValue()); } }); - animator.setInterpolator(mFastOutSlowInInterpolator); + animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); animator.setDuration(newDuration); if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { @@ -534,7 +583,7 @@ public class StackStateAnimator { ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA, child.getAlpha(), newEndValue); - animator.setInterpolator(mFastOutSlowInInterpolator); + animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); // Handle layer type child.setLayerType(View.LAYER_TYPE_HARDWARE, null); animator.addListener(new AnimatorListenerAdapter() { @@ -605,7 +654,7 @@ public class StackStateAnimator { ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z, child.getTranslationZ(), newEndValue); - animator.setInterpolator(mFastOutSlowInInterpolator); + animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); animator.setDuration(newDuration); if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { @@ -659,7 +708,7 @@ public class StackStateAnimator { ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y, child.getTranslationY(), newEndValue); Interpolator interpolator = mHeadsUpAppearChildren.contains(child) ? - mHeadsUpAppearInterpolator :mFastOutSlowInInterpolator; + mHeadsUpAppearInterpolator :Interpolators.FAST_OUT_SLOW_IN; animator.setInterpolator(interpolator); long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); animator.setDuration(newDuration); @@ -683,60 +732,6 @@ public class StackStateAnimator { child.setTag(TAG_END_TRANSLATION_Y, newEndValue); } - private void startScaleAnimation(final View child, - ViewState viewState, long duration) { - Float previousStartValue = getChildTag(child, TAG_START_SCALE); - Float previousEndValue = getChildTag(child, TAG_END_SCALE); - float newEndValue = viewState.scale; - if (previousEndValue != null && previousEndValue == newEndValue) { - return; - } - ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SCALE); - if (!mAnimationFilter.animateScale) { - // just a local update was performed - if (previousAnimator != null) { - // we need to increase all animation keyframes of the previous animator by the - // relative change to the end value - PropertyValuesHolder[] values = previousAnimator.getValues(); - float relativeDiff = newEndValue - previousEndValue; - float newStartValue = previousStartValue + relativeDiff; - values[0].setFloatValues(newStartValue, newEndValue); - values[1].setFloatValues(newStartValue, newEndValue); - child.setTag(TAG_START_SCALE, newStartValue); - child.setTag(TAG_END_SCALE, newEndValue); - previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); - return; - } else { - // no new animation needed, let's just apply the value - child.setScaleX(newEndValue); - child.setScaleY(newEndValue); - } - } - - PropertyValuesHolder holderX = - PropertyValuesHolder.ofFloat(View.SCALE_X, child.getScaleX(), newEndValue); - PropertyValuesHolder holderY = - PropertyValuesHolder.ofFloat(View.SCALE_Y, child.getScaleY(), newEndValue); - ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(child, holderX, holderY); - animator.setInterpolator(mFastOutSlowInInterpolator); - long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); - animator.setDuration(newDuration); - animator.addListener(getGlobalAnimationFinishedListener()); - // remove the tag when the animation is finished - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - child.setTag(TAG_ANIMATOR_SCALE, null); - child.setTag(TAG_START_SCALE, null); - child.setTag(TAG_END_SCALE, null); - } - }); - startAnimator(animator); - child.setTag(TAG_ANIMATOR_SCALE, animator); - child.setTag(TAG_START_SCALE, child.getScaleX()); - child.setTag(TAG_END_SCALE, newEndValue); - } - private void startAnimator(ValueAnimator animator) { mAnimatorSet.add(animator); animator.start(); @@ -934,7 +929,7 @@ public class StackStateAnimator { isRubberbanded); } }); - overScrollAnimator.setInterpolator(mFastOutSlowInInterpolator); + overScrollAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); overScrollAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -976,6 +971,22 @@ public class StackStateAnimator { } } + /** + * Get the end value of the height animation running on a view or the actualHeight + * if no animation is running. + */ + public static float getFinalTranslationY(ExpandableView view) { + if (view == null) { + return 0; + } + ValueAnimator yAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Y); + if (yAnimator == null) { + return view.getTranslationY(); + } else { + return getChildTag(view, TAG_END_TRANSLATION_Y); + } + } + public void setHeadsUpAppearHeightBottom(int headsUpAppearHeightBottom) { mHeadsUpAppearHeightBottom = headsUpAppearHeightBottom; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java index 55ef44042575..41824eaef718 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java @@ -42,6 +42,7 @@ public class StackViewState extends ViewState { public boolean dark; public boolean hideSensitive; public boolean belowSpeedBump; + public float shadowAlpha; /** * The amount which the view should be clipped from the top. This is calculated to @@ -74,6 +75,7 @@ public class StackViewState extends ViewState { StackViewState svs = (StackViewState) viewState; height = svs.height; dimmed = svs.dimmed; + shadowAlpha = svs.shadowAlpha; dark = svs.dark; hideSensitive = svs.hideSensitive; belowSpeedBump = svs.belowSpeedBump; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java index 3e538df0939a..5beaac378b04 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java @@ -29,21 +29,21 @@ public class ViewState { public float yTranslation; public float zTranslation; public boolean gone; - public float scale; + public boolean hidden; public void copyFrom(ViewState viewState) { alpha = viewState.alpha; yTranslation = viewState.yTranslation; zTranslation = viewState.zTranslation; gone = viewState.gone; - scale = viewState.scale; + hidden = viewState.hidden; } public void initFrom(View view) { - alpha = view.getVisibility() == View.INVISIBLE ? 0.0f : view.getAlpha(); + alpha = view.getAlpha(); yTranslation = view.getTranslationY(); zTranslation = view.getTranslationZ(); gone = view.getVisibility() == View.GONE; - scale = view.getScaleX(); + hidden = false; } } diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java index b237400db484..e947ed5ee7fb 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java @@ -94,6 +94,12 @@ public class PipManager { } } }; + private final Runnable mOnPinnedActivityRestartAttempt = new Runnable() { + @Override + public void run() { + movePipToFullscreen(); + } + }; private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -315,6 +321,8 @@ public class PipManager { @Override public void onPinnedActivityRestartAttempt() { + // Post the message back to the UI thread. + mHandler.post(mOnPinnedActivityRestartAttempt); } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/IconPulser.java b/packages/SystemUI/src/com/android/systemui/volume/IconPulser.java deleted file mode 100644 index 9438af132bc4..000000000000 --- a/packages/SystemUI/src/com/android/systemui/volume/IconPulser.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.volume; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.content.Context; -import android.view.View; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; - -public class IconPulser { - private static final float PULSE_SCALE = 1.1f; - - private final Interpolator mFastOutSlowInInterpolator; - - public IconPulser(Context context) { - mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, - android.R.interpolator.fast_out_slow_in); - } - - public void start(final View target) { - if (target == null || target.getScaleX() != 1) return; // n/a, or already running - target.animate().cancel(); - target.animate().scaleX(PULSE_SCALE).scaleY(PULSE_SCALE) - .setInterpolator(mFastOutSlowInInterpolator) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - target.animate().scaleX(1).scaleY(1).setListener(null); - } - }); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java index ed6fc9e9845c..b005a2b44575 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java @@ -258,7 +258,6 @@ public class VolumeDialog implements TunerService.Tunable { if (w > max) { w = max; } - w -= mContext.getResources().getDimensionPixelSize(R.dimen.notification_side_padding) * 2; lp.width = w; mDialogView.setLayoutParams(lp); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java deleted file mode 100644 index f51e8ff8f02e..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java +++ /dev/null @@ -1,431 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.phone; - -import org.mockito.InOrder; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.ActivityInfo; -import android.content.pm.IPackageManager; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.pm.UserInfo; -import android.os.RemoteException; -import android.os.UserHandle; -import android.os.UserManager; -import android.test.AndroidTestCase; - -import java.util.Arrays; -import java.util.ArrayList; -import java.util.List; -import java.util.HashMap; -import java.util.Map; - -/** Tests for the data model for the navigation bar app icons. */ -public class NavigationBarAppsModelTest extends AndroidTestCase { - private PackageManager mMockPackageManager; - private IPackageManager mMockIPackageManager; - private SharedPreferences mMockPrefs; - private SharedPreferences.Editor mMockEdit; - private UserManager mMockUserManager; - - private NavigationBarAppsModel mModel; - - @Override - protected void setUp() throws Exception { - super.setUp(); - - // Mockito setup boilerplate. - System.setProperty("dexmaker.dexcache", mContext.getCacheDir().getPath()); - Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); - - final Context context = mock(Context.class); - mMockPackageManager = mock(PackageManager.class); - mMockIPackageManager = mock(IPackageManager.class); - mMockPrefs = mock(SharedPreferences.class); - mMockEdit = mock(SharedPreferences.Editor.class); - mMockUserManager = mock(UserManager.class); - - when(context.getSharedPreferences( - "com.android.systemui.navbarapps", Context.MODE_PRIVATE)).thenReturn(mMockPrefs); - when(context.getSystemService(Context.USER_SERVICE)).thenReturn(mMockUserManager); - when(context.getPackageManager()).thenReturn(mMockPackageManager); - - setContext(context); - - when(mMockUserManager.getUsers()).thenReturn(new ArrayList<UserInfo>()); - // Assume the version pref is present and equal to the current version. - when(mMockPrefs.getInt("version", -1)).thenReturn(3); - when(mMockPrefs.edit()).thenReturn(mMockEdit); - - when(mMockUserManager.getSerialNumberForUser(new UserHandle(2))).thenReturn(222L); - when(mMockUserManager.getSerialNumberForUser(new UserHandle(4))).thenReturn(444L); - when(mMockUserManager.getSerialNumberForUser(new UserHandle(5))).thenReturn(555L); - when(mMockUserManager.getUserForSerialNumber(222L)).thenReturn(new UserHandle(2)); - when(mMockUserManager.getUserForSerialNumber(444L)).thenReturn(new UserHandle(4)); - when(mMockUserManager.getUserForSerialNumber(555L)).thenReturn(new UserHandle(5)); - - UserInfo ui2 = new UserInfo(); - ui2.profileGroupId = 999; - UserInfo ui4 = new UserInfo(); - ui4.profileGroupId = 999; - UserInfo ui5 = new UserInfo(); - ui5.profileGroupId = 999; - when(mMockUserManager.getUserInfo(2)).thenReturn(ui2); - when(mMockUserManager.getUserInfo(4)).thenReturn(ui4); - when(mMockUserManager.getUserInfo(5)).thenReturn(ui5); - - mModel = new NavigationBarAppsModel(context) { - @Override - protected IPackageManager getPackageManager() { - return mMockIPackageManager; - } - }; - } - - /** Tests resolveApp(). */ - public void testResolveApp() { - ActivityInfo mockNonExportedActivityInfo = new ActivityInfo(); - mockNonExportedActivityInfo.exported = false; - ActivityInfo mockExportedActivityInfo = new ActivityInfo(); - mockExportedActivityInfo.exported = true; - try { - when(mMockIPackageManager.getActivityInfo( - new ComponentName("package1", "class1"), 0, 4)). - thenReturn(mockNonExportedActivityInfo); - when(mMockIPackageManager.getActivityInfo( - new ComponentName("package2", "class2"), 0, 5)). - thenThrow(new RemoteException()); - when(mMockIPackageManager.getActivityInfo( - new ComponentName("package3", "class3"), 0, 6)). - thenReturn(mockExportedActivityInfo); - when(mMockIPackageManager.getActivityInfo( - new ComponentName("package4", "class4"), 0, 7)). - thenReturn(mockExportedActivityInfo); - } catch (RemoteException e) { - fail("RemoteException can't happen in the test, but it happened."); - } - - // Assume some installed activities. - ActivityInfo ai0 = new ActivityInfo(); - ai0.packageName = "package0"; - ai0.name = "class0"; - ActivityInfo ai1 = new ActivityInfo(); - ai1.packageName = "package4"; - ai1.name = "class4"; - ResolveInfo ri0 = new ResolveInfo(); - ri0.activityInfo = ai0; - ResolveInfo ri1 = new ResolveInfo(); - ri1.activityInfo = ai1; - when(mMockPackageManager - .queryIntentActivitiesAsUser(any(Intent.class), eq(0), any(int.class))) - .thenReturn(Arrays.asList(ri0, ri1)); - - mModel.setCurrentUser(3); - // Unlauncheable (for various reasons) apps. - assertEquals(null, mModel.resolveApp( - new AppInfo(new ComponentName("package0", "class0"), new UserHandle(3)))); - mModel.setCurrentUser(4); - assertEquals(null, mModel.resolveApp( - new AppInfo(new ComponentName("package1", "class1"), new UserHandle(4)))); - mModel.setCurrentUser(5); - assertEquals(null, mModel.resolveApp( - new AppInfo(new ComponentName("package2", "class2"), new UserHandle(5)))); - mModel.setCurrentUser(6); - assertEquals(null, mModel.resolveApp( - new AppInfo(new ComponentName("package3", "class3"), new UserHandle(6)))); - - // A launcheable app. - mModel.setCurrentUser(7); - NavigationBarAppsModel.ResolvedApp resolvedApp = mModel.resolveApp( - new AppInfo(new ComponentName("package4", "class4"), new UserHandle(7))); - assertNotNull(resolvedApp); - Intent intent = resolvedApp.launchIntent; - assertEquals(new ComponentName("package4", "class4"), intent.getComponent()); - assertEquals("package4", intent.getPackage()); - assertEquals(ri1, resolvedApp.ri); - } - - /** Initializes the model from SharedPreferences for a few app activites. */ - private void initializeModelFromPrefs() { - // Assume several apps are stored. - when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(2); - when(mMockPrefs.getString("222|app_0", null)).thenReturn("package1/class1"); - when(mMockPrefs.getLong("222|app_user_0", -1)).thenReturn(444L); - when(mMockPrefs.getString("222|app_1", null)).thenReturn("package2/class2"); - when(mMockPrefs.getLong("222|app_user_1", -1)).thenReturn(555L); - - ActivityInfo mockActivityInfo = new ActivityInfo(); - mockActivityInfo.exported = true; - try { - when(mMockIPackageManager.getActivityInfo( - new ComponentName("package0", "class0"), 0, 5)).thenReturn(mockActivityInfo); - when(mMockIPackageManager.getActivityInfo( - new ComponentName("package1", "class1"), 0, 4)).thenReturn(mockActivityInfo); - when(mMockIPackageManager.getActivityInfo( - new ComponentName("package2", "class2"), 0, 5)).thenReturn(mockActivityInfo); - } catch (RemoteException e) { - fail("RemoteException can't happen in the test, but it happened."); - } - - // Assume some installed activities. - ActivityInfo ai0 = new ActivityInfo(); - ai0.packageName = "package0"; - ai0.name = "class0"; - ActivityInfo ai1 = new ActivityInfo(); - ai1.packageName = "package1"; - ai1.name = "class1"; - ActivityInfo ai2 = new ActivityInfo(); - ai2.packageName = "package2"; - ai2.name = "class2"; - ResolveInfo ri0 = new ResolveInfo(); - ri0.activityInfo = ai0; - ResolveInfo ri1 = new ResolveInfo(); - ri1.activityInfo = ai1; - ResolveInfo ri2 = new ResolveInfo(); - ri2.activityInfo = ai2; - when(mMockPackageManager - .queryIntentActivitiesAsUser(any(Intent.class), eq(0), any(int.class))) - .thenReturn(Arrays.asList(ri0, ri1, ri2)); - - mModel.setCurrentUser(2); - } - - /** Tests initializing the model from SharedPreferences. */ - public void testInitializeFromPrefs() { - initializeModelFromPrefs(); - List<AppInfo> apps = mModel.getApps(); - assertEquals(2, apps.size()); - assertEquals("package1/class1", apps.get(0).getComponentName().flattenToString()); - assertEquals(new UserHandle(4), apps.get(0).getUser()); - assertEquals("package2/class2", apps.get(1).getComponentName().flattenToString()); - assertEquals(new UserHandle(5), apps.get(1).getUser()); - } - - /** Tests initializing the model when the SharedPreferences aren't available. */ - public void testInitializeDefaultApps() { - // Assume the user's app count pref isn't available. - when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(-1); - - // Assume some installed activities. - ActivityInfo ai1 = new ActivityInfo(); - ai1.packageName = "package1"; - ai1.name = "class1"; - ActivityInfo ai2 = new ActivityInfo(); - ai2.packageName = "package2"; - ai2.name = "class2"; - ResolveInfo ri1 = new ResolveInfo(); - ri1.activityInfo = ai1; - ResolveInfo ri2 = new ResolveInfo(); - ri2.activityInfo = ai2; - when(mMockPackageManager - .queryIntentActivitiesAsUser(any(Intent.class), eq(0), eq(2))) - .thenReturn(Arrays.asList(ri1, ri2)); - - // Setting the user should load the installed activities. - mModel.setCurrentUser(2); - List<AppInfo> apps = mModel.getApps(); - assertEquals(2, apps.size()); - assertEquals("package1/class1", apps.get(0).getComponentName().flattenToString()); - assertEquals(new UserHandle(2), apps.get(0).getUser()); - assertEquals("package2/class2", apps.get(1).getComponentName().flattenToString()); - assertEquals(new UserHandle(2), apps.get(1).getUser()); - InOrder order = inOrder(mMockEdit); - order.verify(mMockEdit).apply(); - order.verify(mMockEdit).putInt("222|app_count", 2); - order.verify(mMockEdit).putString("222|app_0", "package1/class1"); - order.verify(mMockEdit).putLong("222|app_user_0", 222L); - order.verify(mMockEdit).putString("222|app_1", "package2/class2"); - order.verify(mMockEdit).putLong("222|app_user_1", 222L); - order.verify(mMockEdit).apply(); - verifyNoMoreInteractions(mMockEdit); - } - - /** Tests initializing the model if one of the prefs is missing. */ - public void testInitializeWithMissingPref() { - // Assume two apps are nominally stored. - when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(2); - when(mMockPrefs.getString("222|app_0", null)).thenReturn("package0/class0"); - when(mMockPrefs.getLong("222|app_user_0", -1)).thenReturn(555L); - - // But assume one pref is missing. - when(mMockPrefs.getString("222|app_1", null)).thenReturn(null); - - ActivityInfo mockActivityInfo = new ActivityInfo(); - mockActivityInfo.exported = true; - try { - when(mMockIPackageManager.getActivityInfo( - new ComponentName("package0", "class0"), 0, 5)).thenReturn(mockActivityInfo); - } catch (RemoteException e) { - fail("RemoteException can't happen in the test, but it happened."); - } - - ActivityInfo ai0 = new ActivityInfo(); - ai0.packageName = "package0"; - ai0.name = "class0"; - ResolveInfo ri0 = new ResolveInfo(); - ri0.activityInfo = ai0; - when(mMockPackageManager - .queryIntentActivitiesAsUser(any(Intent.class), eq(0), any(int.class))) - .thenReturn(Arrays.asList(ri0)); - - // Initializing the model should load from prefs and skip the missing one. - mModel.setCurrentUser(2); - List<AppInfo> apps = mModel.getApps(); - assertEquals(1, apps.size()); - assertEquals("package0/class0", apps.get(0).getComponentName().flattenToString()); - assertEquals(new UserHandle(5), apps.get(0).getUser()); - InOrder order = inOrder(mMockEdit); - order.verify(mMockEdit).putInt("222|app_count", 1); - order.verify(mMockEdit).putString("222|app_0", "package0/class0"); - order.verify(mMockEdit).putLong("222|app_user_0", 555L); - order.verify(mMockEdit).apply(); - verifyNoMoreInteractions(mMockEdit); - } - - /** Tests initializing the model if one of the apps is unlauncheable. */ - public void testInitializeWithUnlauncheableApp() { - // Assume two apps are nominally stored. - when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(2); - when(mMockPrefs.getString("222|app_0", null)).thenReturn("package0/class0"); - when(mMockPrefs.getLong("222|app_user_0", -1)).thenReturn(555L); - when(mMockPrefs.getString("222|app_1", null)).thenReturn("package1/class1"); - when(mMockPrefs.getLong("222|app_user_1", -1)).thenReturn(444L); - - ActivityInfo mockActivityInfo = new ActivityInfo(); - mockActivityInfo.exported = true; - try { - when(mMockIPackageManager.getActivityInfo( - new ComponentName("package0", "class0"), 0, 5)).thenReturn(mockActivityInfo); - } catch (RemoteException e) { - fail("RemoteException can't happen in the test, but it happened."); - } - - ActivityInfo ai0 = new ActivityInfo(); - ai0.packageName = "package0"; - ai0.name = "class0"; - ResolveInfo ri0 = new ResolveInfo(); - ri0.activityInfo = ai0; - when(mMockPackageManager - .queryIntentActivitiesAsUser(any(Intent.class), eq(0), any(int.class))) - .thenReturn(Arrays.asList(ri0)); - - // Initializing the model should load from prefs and skip the unlauncheable one. - mModel.setCurrentUser(2); - List<AppInfo> apps = mModel.getApps(); - assertEquals(1, apps.size()); - assertEquals("package0/class0", apps.get(0).getComponentName().flattenToString()); - assertEquals(new UserHandle(5), apps.get(0).getUser()); - - // Once an unlauncheable app is detected, the model should save all apps excluding the - // unlauncheable one. - verify(mMockEdit).putInt("222|app_count", 1); - verify(mMockEdit).putString("222|app_0", "package0/class0"); - verify(mMockEdit).putLong("222|app_user_0", 555L); - verify(mMockEdit).apply(); - verifyNoMoreInteractions(mMockEdit); - } - - /** Tests saving the model to SharedPreferences. */ - public void testSavePrefs() { - initializeModelFromPrefs(); - - mModel.setApps(mModel.getApps()); - verify(mMockEdit).putInt("222|app_count", 2); - verify(mMockEdit).putString("222|app_0", "package1/class1"); - verify(mMockEdit).putLong("222|app_user_0", 444L); - verify(mMockEdit).putString("222|app_1", "package2/class2"); - verify(mMockEdit).putLong("222|app_user_1", 555L); - verify(mMockEdit).apply(); - verifyNoMoreInteractions(mMockEdit); - } - - /** Tests cleaning all prefs on a version change. */ - public void testVersionChange() { - // Assume the version pref changed. - when(mMockPrefs.getInt("version", -1)).thenReturn(1); - - new NavigationBarAppsModel(getContext()); - verify(mMockEdit).clear(); - verify(mMockEdit).putInt("version", 3); - verify(mMockEdit).apply(); - verifyNoMoreInteractions(mMockEdit); - } - - /** Tests cleaning prefs for deleted users. */ - public void testCleaningDeletedUsers() { - // Users on the device. - final UserInfo user1 = new UserInfo(11, "", 0); - user1.serialNumber = 1111; - final UserInfo user2 = new UserInfo(13, "", 0); - user2.serialNumber = 1313; - - when(mMockUserManager.getUsers()).thenReturn(Arrays.asList(user1, user2)); - - when(mMockPrefs.edit()). - thenReturn(mMockEdit). - thenReturn(mock(SharedPreferences.Editor.class)); - - // Assume the user's app count pref isn't available. This will trigger clearing deleted - // users' prefs. - when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(-1); - - final Map allPrefs = new HashMap<String, Object>(); - allPrefs.put("version", null); - allPrefs.put("some_strange_pref", null); - allPrefs.put("", null); - allPrefs.put("|", null); - allPrefs.put("1313|app_count", null); - allPrefs.put("1212|app_count", null); - when(mMockPrefs.getAll()).thenReturn(allPrefs); - - // Setting the user should remove prefs for deleted users. - mModel.setCurrentUser(2); - verify(mMockEdit).remove("some_strange_pref"); - verify(mMockEdit).remove(""); - verify(mMockEdit).remove("|"); - verify(mMockEdit).remove("1212|app_count"); - verify(mMockEdit).apply(); - verifyNoMoreInteractions(mMockEdit); - } - - /** Tests the apps-changed listener. */ - public void testAppsChangedListeners() { - NavigationBarAppsModel.OnAppsChangedListener listener = - mock(NavigationBarAppsModel.OnAppsChangedListener.class); - - mModel.addOnAppsChangedListener(listener); - mModel.setApps(new ArrayList<AppInfo>()); - verify(listener).onPinnedAppsChanged(); - verifyNoMoreInteractions(listener); - - mModel.removeOnAppsChangedListener(listener); - mModel.setApps(new ArrayList<AppInfo>()); - verifyNoMoreInteractions(listener); - } -} diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index d63dd0c8d03a..46198b0ccc5b 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -1920,7 +1920,9 @@ public class BackupManagerService { synchronized (mBackupParticipants) { if (replacing) { // This is the package-replaced case; we just remove the entry - // under the old uid and fall through to re-add. + // under the old uid and fall through to re-add. If an app + // just added key/value backup participation, this picks it up + // as a known participant. removePackageParticipantsLocked(pkgList, uid); } addPackageParticipantsLocked(pkgList); @@ -1933,6 +1935,14 @@ public class BackupManagerService { if (appGetsFullBackup(app) && appIsEligibleForBackup(app.applicationInfo)) { enqueueFullBackup(packageName, now); scheduleNextFullBackupJob(0); + } else { + // The app might have just transitioned out of full-data into + // doing key/value backups, or might have just disabled backups + // entirely. Make sure it is no longer in the full-data queue. + synchronized (mQueueLock) { + dequeueFullBackupLocked(packageName); + } + writeFullBackupScheduleAsync(); } // Transport maintenance: rebind to known existing transports that have @@ -1964,6 +1974,9 @@ public class BackupManagerService { if (replacing) { // The package is being updated. We'll receive a PACKAGE_ADDED shortly. } else { + // Outright removal. In the full-data case, the app will be dropped + // from the queue when its (now obsolete) name comes up again for + // backup. synchronized (mBackupParticipants) { removePackageParticipantsLocked(pkgList, uid); } @@ -4190,11 +4203,21 @@ public class BackupManagerService { // as well as any explicit mention of the 'special' shared-storage agent // package (we handle that one at the end). if (MORE_DEBUG) { - Slog.d(TAG, "Ignoring not eligible package " + pkg); + Slog.d(TAG, "Ignoring ineligible package " + pkg); } sendBackupOnResult(mBackupObserver, pkg, BackupManager.ERROR_BACKUP_NOT_ALLOWED); continue; + } else if (!appGetsFullBackup(info)) { + // Cull any packages that are found in the queue but now aren't supposed + // to get full-data backup operations. + if (MORE_DEBUG) { + Slog.d(TAG, "Ignoring full-data backup of key/value participant " + + pkg); + } + sendBackupOnResult(mBackupObserver, pkg, + BackupManager.ERROR_BACKUP_NOT_ALLOWED); + continue; } else if (appIsStopped(info.applicationInfo)) { // Cull any packages in the 'stopped' state: they've either just been // installed or have explicitly been force-stopped by the user. In both @@ -4592,21 +4615,28 @@ public class BackupManagerService { } /** + * Remove a package from the full-data queue. + */ + void dequeueFullBackupLocked(String packageName) { + final int N = mFullBackupQueue.size(); + for (int i = N-1; i >= 0; i--) { + final FullBackupEntry e = mFullBackupQueue.get(i); + if (packageName.equals(e.packageName)) { + mFullBackupQueue.remove(i); + } + } + } + + /** * Enqueue full backup for the given app, with a note about when it last ran. */ void enqueueFullBackup(String packageName, long lastBackedUp) { FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp); synchronized (mQueueLock) { - int N = mFullBackupQueue.size(); // First, sanity check that we aren't adding a duplicate. Slow but // straightforward; we'll have at most on the order of a few hundred // items in this list. - for (int i = N-1; i >= 0; i--) { - final FullBackupEntry e = mFullBackupQueue.get(i); - if (packageName.equals(e.packageName)) { - mFullBackupQueue.remove(i); - } - } + dequeueFullBackupLocked(packageName); // This is also slow but easy for modest numbers of apps: work backwards // from the end of the queue until we find an item whose last backup @@ -4700,21 +4730,24 @@ public class BackupManagerService { return false; } - if (mFullBackupQueue.size() == 0) { - // no work to do so just bow out - if (DEBUG) { - Slog.i(TAG, "Backup queue empty; doing nothing"); - } - return false; - } - - // At this point we know that we have work to do, but possibly not right now. + // At this point we think that we have work to do, but possibly not right now. // Any exit without actually running backups will also require that we // reschedule the job. boolean runBackup = true; boolean headBusy; do { + // Recheck each time, because culling due to ineligibility may + // have emptied the queue. + if (mFullBackupQueue.size() == 0) { + // no work to do so just bow out + if (DEBUG) { + Slog.i(TAG, "Backup queue empty; doing nothing"); + } + runBackup = false; + break; + } + headBusy = false; if (!fullBackupAllowable(getTransport(mCurrentTransport))) { @@ -4744,6 +4777,19 @@ public class BackupManagerService { try { PackageInfo appInfo = mPackageManager.getPackageInfo(entry.packageName, 0); + if (!appGetsFullBackup(appInfo)) { + // The head app isn't supposed to get full-data backups [any more]; + // so we cull it and force a loop around to consider the new head + // app. + if (MORE_DEBUG) { + Slog.i(TAG, "Culling package " + entry.packageName + + " in full-backup queue but not eligible"); + } + mFullBackupQueue.remove(0); + headBusy = true; // force the while() condition + continue; + } + headBusy = mActivityManager.isAppForeground(appInfo.applicationInfo.uid); if (headBusy) { @@ -4762,7 +4808,6 @@ public class BackupManagerService { enqueueFullBackup(entry.packageName, nextEligible - MIN_FULL_BACKUP_INTERVAL); } - } catch (NameNotFoundException nnf) { // So, we think we want to back this up, but it turns out the package // in question is no longer installed. We want to drop it from the diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index c1a082b3cb11..499b706a2b87 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -114,13 +114,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int SERVICE_IBLUETOOTH = 1; private static final int SERVICE_IBLUETOOTHGATT = 2; - private static final String[] DEVICE_TYPE_NAMES = new String[] { - "???", - "BR/EDR", - "LE", - "DUAL" - }; - private final Context mContext; private static int mBleAppCount = 0; @@ -131,6 +124,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private final ContentResolver mContentResolver; private final RemoteCallbackList<IBluetoothManagerCallback> mCallbacks; private final RemoteCallbackList<IBluetoothStateChangeCallback> mStateChangeCallbacks; + private IBinder mBluetoothBinder; private IBluetooth mBluetooth; private IBluetoothGatt mBluetoothGatt; private boolean mBinding; @@ -242,6 +236,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext = context; mBluetooth = null; + mBluetoothBinder = null; mBluetoothGatt = null; mBinding = false; mUnbinding = false; @@ -678,6 +673,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } if (DBG) Log.d(TAG, "Sending unbind request."); + mBluetoothBinder = null; mBluetooth = null; //Unbind mContext.unbindService(mConnection); @@ -1166,6 +1162,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); mBinding = false; + mBluetoothBinder = service; mBluetooth = IBluetooth.Stub.asInterface(service); try { @@ -1674,44 +1671,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } @Override - public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); - - writer.println("Bluetooth Status"); - writer.println(" enabled: " + mEnable); - writer.println(" state: " + mState); - writer.println(" address: " + mAddress); - writer.println(" name: " + mName + "\n"); - writer.flush(); - - if (mBluetooth == null) { - writer.println("Bluetooth Service not connected"); - } else { - ParcelFileDescriptor pfd = null; - try { - writer.println("Bonded devices:"); - for (BluetoothDevice device : mBluetooth.getBondedDevices()) { - writer.println(" " + device.getAddress() + - " [" + DEVICE_TYPE_NAMES[device.getType()] + "] " + - device.getName()); - } - writer.flush(); - - pfd = ParcelFileDescriptor.dup(fd); - mBluetooth.dump(pfd); - } catch (RemoteException re) { - writer.println("RemoteException while calling Bluetooth Service"); - } catch (IOException ioe) { - writer.println("IOException attempting to dup() fd"); - } finally { - if (pfd != null) { - try { - pfd.close(); - } catch (IOException ioe) { - writer.println("IOException attempting to close() fd"); - } - } - } + public void dump(FileDescriptor fd, PrintWriter writer, String args[]) { + if (mBluetoothBinder == null) { + writer.println("Bluetooth Service not connected"); + } else { + try { + mBluetoothBinder.dump(fd, args); + } catch (RemoteException re) { + writer.println("RemoteException while calling Bluetooth Service"); } + } } } diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java index 377d52fb1ac5..f6f05fe8f01a 100644 --- a/services/core/java/com/android/server/LockSettingsService.java +++ b/services/core/java/com/android/server/LockSettingsService.java @@ -78,7 +78,7 @@ public class LockSettingsService extends ILockSettings.Stub { private final Context mContext; private final LockSettingsStorage mStorage; - private final LockSettingsStrongAuth mStrongAuth = new LockSettingsStrongAuth(); + private final LockSettingsStrongAuth mStrongAuth; private LockPatternUtils mLockPatternUtils; private boolean mFirstCallToVold; @@ -93,6 +93,7 @@ public class LockSettingsService extends ILockSettings.Stub { public LockSettingsService(Context context) { mContext = context; + mStrongAuth = new LockSettingsStrongAuth(context); // Open the database mLockPatternUtils = new LockPatternUtils(context); diff --git a/services/core/java/com/android/server/LockSettingsStrongAuth.java b/services/core/java/com/android/server/LockSettingsStrongAuth.java index 0e4d5a7f2a7b..551ceb8c328a 100644 --- a/services/core/java/com/android/server/LockSettingsStrongAuth.java +++ b/services/core/java/com/android/server/LockSettingsStrongAuth.java @@ -20,6 +20,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternUtils.StrongAuthTracker; import android.app.trust.IStrongAuthTracker; +import android.content.Context; import android.os.DeadObjectException; import android.os.Handler; import android.os.Message; @@ -46,6 +47,11 @@ public class LockSettingsStrongAuth { private final ArrayList<IStrongAuthTracker> mStrongAuthTrackers = new ArrayList<>(); private final SparseIntArray mStrongAuthForUser = new SparseIntArray(); + private final int mDefaultStrongAuthFlags; + + public LockSettingsStrongAuth(Context context) { + mDefaultStrongAuthFlags = StrongAuthTracker.getDefaultFlags(context); + } private void handleAddStrongAuthTracker(IStrongAuthTracker tracker) { for (int i = 0; i < mStrongAuthTrackers.size(); i++) { @@ -87,7 +93,7 @@ public class LockSettingsStrongAuth { } private void handleRequireStrongAuthOneUser(int strongAuthReason, int userId) { - int oldValue = mStrongAuthForUser.get(userId, LockPatternUtils.StrongAuthTracker.DEFAULT); + int oldValue = mStrongAuthForUser.get(userId, mDefaultStrongAuthFlags); int newValue = strongAuthReason == STRONG_AUTH_NOT_REQUIRED ? STRONG_AUTH_NOT_REQUIRED : (oldValue | strongAuthReason); @@ -101,7 +107,7 @@ public class LockSettingsStrongAuth { int index = mStrongAuthForUser.indexOfKey(userId); if (index >= 0) { mStrongAuthForUser.removeAt(index); - notifyStrongAuthTrackers(StrongAuthTracker.DEFAULT, userId); + notifyStrongAuthTrackers(mDefaultStrongAuthFlags, userId); } } diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index dd19c6a91eae..95f57346dcfc 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -186,7 +186,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub private final Handler mFgHandler; private final Handler mDaemonHandler; - private final PhoneStateListener mPhoneStateListener; private IBatteryStats mBatteryStats; @@ -283,22 +282,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub mDaemonHandler = new Handler(FgThread.get().getLooper()); - mPhoneStateListener = new PhoneStateListener(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, - mDaemonHandler.getLooper()) { - @Override - public void onDataConnectionRealTimeInfoChanged( - DataConnectionRealTimeInfo dcRtInfo) { - if (DBG) Slog.d(TAG, "onDataConnectionRealTimeInfoChanged: " + dcRtInfo); - notifyInterfaceClassActivity(ConnectivityManager.TYPE_MOBILE, - dcRtInfo.getDcPowerState(), dcRtInfo.getTime(), true); - } - }; - TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); - if (tm != null) { - tm.listen(mPhoneStateListener, - PhoneStateListener.LISTEN_DATA_CONNECTION_REAL_TIME_INFO); - } - // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); } diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 19a4851525a6..820551da274b 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -34,7 +34,6 @@ import android.os.Message; import android.os.RemoteException; import android.os.UserHandle; import android.telephony.CellLocation; -import android.telephony.DataConnectionRealTimeInfo; import android.telephony.Rlog; import android.telephony.TelephonyManager; import android.telephony.SubscriptionManager; @@ -179,8 +178,6 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { private int mDefaultPhoneId = SubscriptionManager.INVALID_PHONE_INDEX; - private DataConnectionRealTimeInfo mDcRtInfo = new DataConnectionRealTimeInfo(); - private int mRingingCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE; private int mForegroundCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE; @@ -262,7 +259,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHED, userHandle, 0)); } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED)) { Integer newDefaultSubIdObj = new Integer(intent.getIntExtra( - PhoneConstants.SUBSCRIPTION_KEY, SubscriptionManager.getDefaultSubId())); + PhoneConstants.SUBSCRIPTION_KEY, + SubscriptionManager.getDefaultSubscriptionId())); int newDefaultPhoneId = intent.getIntExtra(PhoneConstants.SLOT_KEY, SubscriptionManager.getPhoneId(mDefaultSubId)); if (DBG) { @@ -624,13 +622,6 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } - if ((events & PhoneStateListener.LISTEN_DATA_CONNECTION_REAL_TIME_INFO) != 0) { - try { - r.callback.onDataConnectionRealTimeInfoChanged(mDcRtInfo); - } catch (RemoteException ex) { - remove(r.binder); - } - } if ((events & PhoneStateListener.LISTEN_PRECISE_CALL_STATE) != 0) { try { r.callback.onPreciseCallStateChanged(mPreciseCallState); @@ -921,31 +912,6 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - public void notifyDataConnectionRealTimeInfo(DataConnectionRealTimeInfo dcRtInfo) { - if (!checkNotifyPermission("notifyDataConnectionRealTimeInfo()")) { - return; - } - - synchronized (mRecords) { - mDcRtInfo = dcRtInfo; - for (Record r : mRecords) { - if (validateEventsAndUserLocked(r, - PhoneStateListener.LISTEN_DATA_CONNECTION_REAL_TIME_INFO)) { - try { - if (DBG_LOC) { - log("notifyDataConnectionRealTimeInfo: mDcRtInfo=" - + mDcRtInfo + " r=" + r); - } - r.callback.onDataConnectionRealTimeInfoChanged(mDcRtInfo); - } catch (RemoteException ex) { - mRemoveList.add(r.binder); - } - } - } - handleRemoveListLocked(); - } - } - @Override public void notifyMessageWaitingChangedForPhoneId(int phoneId, int subId, boolean mwi) { if (!checkNotifyPermission("notifyMessageWaitingChanged()")) { @@ -1370,7 +1336,6 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println(" mCellLocation=" + mCellLocation[i]); pw.println(" mCellInfo=" + mCellInfo.get(i)); } - pw.println(" mDcRtInfo=" + mDcRtInfo); pw.println("registrations: count=" + recordCount); for (Record r : mRecords) { pw.println(" " + r); diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 2683be66b347..d0006aa5c95c 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -4266,21 +4266,6 @@ public class AccountManagerService } } - private boolean isPermitted(String opPackageName, int callingUid, String... permissions) { - for (String perm : permissions) { - if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, " caller uid " + callingUid + " has " + perm); - } - final int opCode = AppOpsManager.permissionToOpCode(perm); - if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp( - opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) { - return true; - } - } - } - return false; - } private int handleIncomingUser(int userId) { try { @@ -4355,12 +4340,50 @@ public class AccountManagerService private List<String> getTypesVisibleToCaller(int callingUid, int userId, String opPackageName) { - boolean isPermitted = - isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS, - Manifest.permission.GET_ACCOUNTS_PRIVILEGED); + List<String> permissionsToCheck = new ArrayList<String>(2); + permissionsToCheck.add(Manifest.permission.GET_ACCOUNTS_PRIVILEGED); + try { + ApplicationInfo appInfo = mPackageManager.getApplicationInfo( + opPackageName, 0 /* flags */); + /* + * At or before SDK 23, clients discover all the accounts in their + * user profile (via AccountManager.getAccounts(...)) by declaring + * the GET_ACCOUNTS permission. + * + * After SDK 23 the GET_ACCOUNTS permission is deprecated. Instead + * apps will be able to retrieve those accounts managed by + * authenticators sharing a package signature without any special + * permissions. The only clients able to discover all the accounts + * on the device will be those with the GET_ACCOUNTS_PRVILEGED + * system permission. + */ + if (23 >= appInfo.targetSdkVersion) { + permissionsToCheck.add(Manifest.permission.GET_ACCOUNTS); + } + } catch (NameNotFoundException e) { + // No application associated with the specified package. + Log.w(TAG, "No application associated with package: " + opPackageName); + } + boolean isPermitted = isPermitted(opPackageName, callingUid, permissionsToCheck); return getTypesForCaller(callingUid, userId, isPermitted); } + private boolean isPermitted(String opPackageName, int callingUid, List<String> permissions) { + for (String perm : permissions) { + if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, " caller uid " + callingUid + " has " + perm); + } + final int opCode = AppOpsManager.permissionToOpCode(perm); + if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp( + opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) { + return true; + } + } + } + return false; + } + private List<String> getTypesManagedByCaller(int callingUid, int userId) { return getTypesForCaller(callingUid, userId, false); } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 7ba633878b29..8c0ec78e7a72 100755 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -2775,7 +2775,7 @@ public final class ActiveServices { } if (anrMessage != null) { - mAm.appNotResponding(proc, null, null, false, anrMessage); + mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index b8327c14a984..2c55ee26cf8f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -277,7 +277,6 @@ import static com.android.internal.util.XmlUtils.readLongAttribute; import static com.android.internal.util.XmlUtils.writeBooleanAttribute; import static com.android.internal.util.XmlUtils.writeIntAttribute; import static com.android.internal.util.XmlUtils.writeLongAttribute; -import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST; @@ -392,7 +391,7 @@ public final class ActivityManagerService extends ActivityManagerNative // The flags that are set for all calls we make to the package manager. static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES; - private static final String SYSTEM_DEBUGGABLE = "ro.debuggable"; + static final String SYSTEM_DEBUGGABLE = "ro.debuggable"; static final boolean IS_USER_BUILD = "user".equals(Build.TYPE); @@ -598,6 +597,12 @@ public final class ActivityManagerService extends ActivityManagerNative final UserController mUserController; + final AppErrors mAppErrors; + + public boolean canShowErrorDialogs() { + return mShowDialogs && !mSleeping && !mShuttingDown; + } + public class PendingAssistExtras extends Binder implements Runnable { public final ActivityRecord activity; public final Bundle extras; @@ -668,38 +673,6 @@ public final class ActivityManagerService extends ActivityManagerNative ProcessRecord mHeavyWeightProcess = null; /** - * The last time that various processes have crashed. - */ - final ProcessMap<Long> mProcessCrashTimes = new ProcessMap<Long>(); - - /** - * Information about a process that is currently marked as bad. - */ - static final class BadProcessInfo { - BadProcessInfo(long time, String shortMsg, String longMsg, String stack) { - this.time = time; - this.shortMsg = shortMsg; - this.longMsg = longMsg; - this.stack = stack; - } - - final long time; - final String shortMsg; - final String longMsg; - final String stack; - } - - /** - * Set of applications that we consider to be bad, and will reject - * incoming broadcasts from (which the user has no control over). - * Processes are added to this set when they have crashed twice within - * a minimum amount of time; they are removed from it when they are - * later restarted (hopefully due to some user action). The value is the - * time it was added to the list. - */ - final ProcessMap<BadProcessInfo> mBadProcesses = new ProcessMap<BadProcessInfo>(); - - /** * All of the processes we currently have running organized by pid. * The keys are the pid running the application. * @@ -1351,8 +1324,6 @@ public final class ActivityManagerService extends ActivityManagerNative final ArrayList<UidRecord.ChangeItem> mPendingUidChanges = new ArrayList<>(); final ArrayList<UidRecord.ChangeItem> mAvailUidChanges = new ArrayList<>(); - ArraySet<String> mAppsNotReportingCrashes; - /** * Runtime CPU use collection thread. This object's lock is used to * perform synchronization with the thread (notifying it to run). @@ -1511,80 +1482,11 @@ public final class ActivityManagerService extends ActivityManagerNative public void handleMessage(Message msg) { switch (msg.what) { case SHOW_ERROR_UI_MSG: { - HashMap<String, Object> data = (HashMap<String, Object>) msg.obj; - boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0; - synchronized (ActivityManagerService.this) { - ProcessRecord proc = (ProcessRecord)data.get("app"); - AppErrorResult res = (AppErrorResult) data.get("result"); - if (proc != null && proc.crashDialog != null) { - Slog.e(TAG, "App already has crash dialog: " + proc); - if (res != null) { - res.set(0); - } - return; - } - boolean isBackground = (UserHandle.getAppId(proc.uid) - >= Process.FIRST_APPLICATION_UID - && proc.pid != MY_PID); - for (int userId : mUserController.getCurrentProfileIdsLocked()) { - isBackground &= (proc.userId != userId); - } - if (isBackground && !showBackground) { - Slog.w(TAG, "Skipping crash dialog of " + proc + ": background"); - if (res != null) { - res.set(0); - } - return; - } - final boolean crashSilenced = mAppsNotReportingCrashes != null && - mAppsNotReportingCrashes.contains(proc.info.packageName); - if (mShowDialogs && !mSleeping && !mShuttingDown && !crashSilenced) { - Dialog d = new AppErrorDialog(mContext, - ActivityManagerService.this, res, proc); - d.show(); - proc.crashDialog = d; - } else { - // The device is asleep, so just pretend that the user - // saw a crash dialog and hit "force quit". - if (res != null) { - res.set(0); - } - } - } - + mAppErrors.handleShowAppErrorUi(msg); ensureBootCompleted(); } break; case SHOW_NOT_RESPONDING_UI_MSG: { - synchronized (ActivityManagerService.this) { - HashMap<String, Object> data = (HashMap<String, Object>) msg.obj; - ProcessRecord proc = (ProcessRecord)data.get("app"); - if (proc != null && proc.anrDialog != null) { - Slog.e(TAG, "App already has anr dialog: " + proc); - return; - } - - Intent intent = new Intent("android.intent.action.ANR"); - if (!mProcessesReady) { - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY - | Intent.FLAG_RECEIVER_FOREGROUND); - } - broadcastIntentLocked(null, null, intent, - null, null, 0, null, null, null, AppOpsManager.OP_NONE, - null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */); - - if (mShowDialogs) { - Dialog d = new AppNotRespondingDialog(ActivityManagerService.this, - mContext, proc, (ActivityRecord)data.get("activity"), - msg.arg1 != 0); - d.show(); - proc.anrDialog = d; - } else { - // Just kill the app if there is no dialog to be shown. - killAppAtUsersRequest(proc, null); - } - } - + mAppErrors.handleShowAnrUi(msg); ensureBootCompleted(); } break; case SHOW_STRICT_MODE_VIOLATION_UI_MSG: { @@ -2509,6 +2411,7 @@ public final class ActivityManagerService extends ActivityManagerNative mServices = new ActiveServices(this); mProviderMap = new ProviderMap(this); + mAppErrors = new AppErrors(mContext, this); // TODO: Move creation of battery stats service outside of activity manager service. File dataDir = Environment.getDataDirectory(); @@ -3027,7 +2930,7 @@ public final class ActivityManagerService extends ActivityManagerNative return index; } - private static void killProcessGroup(int uid, int pid) { + static void killProcessGroup(int uid, int pid) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "killProcessGroup"); Process.killProcessGroup(uid, pid); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); @@ -3349,7 +3252,7 @@ public final class ActivityManagerService extends ActivityManagerNative if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) { // If we are in the background, then check to see if this process // is bad. If so, we will just silently fail. - if (mBadProcesses.get(info.processName, info.uid) != null) { + if (mAppErrors.isBadProcessLocked(info)) { if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid + "/" + info.processName); return null; @@ -3361,12 +3264,12 @@ public final class ActivityManagerService extends ActivityManagerNative // if it had been bad. if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid + "/" + info.processName); - mProcessCrashTimes.remove(info.processName, info.uid); - if (mBadProcesses.get(info.processName, info.uid) != null) { + mAppErrors.resetProcessCrashTimeLocked(info); + if (mAppErrors.isBadProcessLocked(info)) { EventLog.writeEvent(EventLogTags.AM_PROC_GOOD, UserHandle.getUserId(info.uid), info.uid, info.processName); - mBadProcesses.remove(info.processName, info.uid); + mAppErrors.clearBadProcessLocked(info); if (app != null) { app.bad = false; } @@ -4774,46 +4677,7 @@ public final class ActivityManagerService extends ActivityManagerNative } synchronized(this) { - ProcessRecord proc = null; - - // Figure out which process to kill. We don't trust that initialPid - // still has any relation to current pids, so must scan through the - // list. - synchronized (mPidsSelfLocked) { - for (int i=0; i<mPidsSelfLocked.size(); i++) { - ProcessRecord p = mPidsSelfLocked.valueAt(i); - if (p.uid != uid) { - continue; - } - if (p.pid == initialPid) { - proc = p; - break; - } - if (p.pkgList.containsKey(packageName)) { - proc = p; - } - } - } - - if (proc == null) { - Slog.w(TAG, "crashApplication: nothing for uid=" + uid - + " initialPid=" + initialPid - + " packageName=" + packageName); - return; - } - - if (proc.thread != null) { - if (proc.pid == Process.myPid()) { - Log.w(TAG, "crashApplication: trying to crash self!"); - return; - } - long ident = Binder.clearCallingIdentity(); - try { - proc.thread.scheduleCrash(message); - } catch (RemoteException e) { - } - Binder.restoreCallingIdentity(ident); - } + mAppErrors.scheduleAppCrashLocked(uid, initialPid, packageName, message); } } @@ -5294,169 +5158,6 @@ public final class ActivityManagerService extends ActivityManagerNative } } - final void appNotResponding(ProcessRecord app, ActivityRecord activity, - ActivityRecord parent, boolean aboveSystem, final String annotation) { - ArrayList<Integer> firstPids = new ArrayList<Integer>(5); - SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20); - - if (mController != null) { - try { - // 0 == continue, -1 = kill process immediately - int res = mController.appEarlyNotResponding(app.processName, app.pid, annotation); - if (res < 0 && app.pid != MY_PID) { - app.kill("anr", true); - } - } catch (RemoteException e) { - mController = null; - Watchdog.getInstance().setActivityController(null); - } - } - - long anrTime = SystemClock.uptimeMillis(); - if (MONITOR_CPU_USAGE) { - updateCpuStatsNow(); - } - - synchronized (this) { - // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down. - if (mShuttingDown) { - Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation); - return; - } else if (app.notResponding) { - Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation); - return; - } else if (app.crashing) { - Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation); - return; - } - - // In case we come through here for the same app before completing - // this one, mark as anring now so we will bail out. - app.notResponding = true; - - // Log the ANR to the event log. - EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid, - app.processName, app.info.flags, annotation); - - // Dump thread traces as quickly as we can, starting with "interesting" processes. - firstPids.add(app.pid); - - int parentPid = app.pid; - if (parent != null && parent.app != null && parent.app.pid > 0) parentPid = parent.app.pid; - if (parentPid != app.pid) firstPids.add(parentPid); - - if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID); - - for (int i = mLruProcesses.size() - 1; i >= 0; i--) { - ProcessRecord r = mLruProcesses.get(i); - if (r != null && r.thread != null) { - int pid = r.pid; - if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) { - if (r.persistent) { - firstPids.add(pid); - } else { - lastPids.put(pid, Boolean.TRUE); - } - } - } - } - } - - // Log the ANR to the main log. - StringBuilder info = new StringBuilder(); - info.setLength(0); - info.append("ANR in ").append(app.processName); - if (activity != null && activity.shortComponentName != null) { - info.append(" (").append(activity.shortComponentName).append(")"); - } - info.append("\n"); - info.append("PID: ").append(app.pid).append("\n"); - if (annotation != null) { - info.append("Reason: ").append(annotation).append("\n"); - } - if (parent != null && parent != activity) { - info.append("Parent: ").append(parent.shortComponentName).append("\n"); - } - - final ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true); - - File tracesFile = dumpStackTraces(true, firstPids, processCpuTracker, lastPids, - NATIVE_STACKS_OF_INTEREST); - - String cpuInfo = null; - if (MONITOR_CPU_USAGE) { - updateCpuStatsNow(); - synchronized (mProcessCpuTracker) { - cpuInfo = mProcessCpuTracker.printCurrentState(anrTime); - } - info.append(processCpuTracker.printCurrentLoad()); - info.append(cpuInfo); - } - - info.append(processCpuTracker.printCurrentState(anrTime)); - - Slog.e(TAG, info.toString()); - if (tracesFile == null) { - // There is no trace file, so dump (only) the alleged culprit's threads to the log - Process.sendSignal(app.pid, Process.SIGNAL_QUIT); - } - - addErrorToDropBox("anr", app, app.processName, activity, parent, annotation, - cpuInfo, tracesFile, null); - - if (mController != null) { - try { - // 0 == show dialog, 1 = keep waiting, -1 = kill process immediately - int res = mController.appNotResponding(app.processName, app.pid, info.toString()); - if (res != 0) { - if (res < 0 && app.pid != MY_PID) { - app.kill("anr", true); - } else { - synchronized (this) { - mServices.scheduleServiceTimeoutLocked(app); - } - } - return; - } - } catch (RemoteException e) { - mController = null; - Watchdog.getInstance().setActivityController(null); - } - } - - // Unless configured otherwise, swallow ANRs in background processes & kill the process. - boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0; - - synchronized (this) { - mBatteryStatsService.noteProcessAnr(app.processName, app.uid); - - if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) { - app.kill("bg anr", true); - return; - } - - // Set the app's notResponding state, and look up the errorReportReceiver - makeAppNotRespondingLocked(app, - activity != null ? activity.shortComponentName : null, - annotation != null ? "ANR " + annotation : "ANR", - info.toString()); - - // Bring up the infamous App Not Responding dialog - Message msg = Message.obtain(); - HashMap<String, Object> map = new HashMap<String, Object>(); - msg.what = SHOW_NOT_RESPONDING_UI_MSG; - msg.obj = map; - msg.arg1 = aboveSystem ? 1 : 0; - map.put("app", app); - if (activity != null) { - map.put("activity", activity); - } - - mUiHandler.sendMessage(msg); - } - } - final void showLaunchWarningLocked(final ActivityRecord cur, final ActivityRecord next) { if (!mLaunchWarningShown) { mLaunchWarningShown = true; @@ -6079,33 +5780,7 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.i(TAG, "Force stopping u" + userId + ": " + reason); } - final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap(); - for (int ip = pmap.size() - 1; ip >= 0; ip--) { - SparseArray<Long> ba = pmap.valueAt(ip); - for (i = ba.size() - 1; i >= 0; i--) { - boolean remove = false; - final int entUid = ba.keyAt(i); - if (packageName != null) { - if (userId == UserHandle.USER_ALL) { - if (UserHandle.getAppId(entUid) == appId) { - remove = true; - } - } else { - if (entUid == UserHandle.getUid(userId, appId)) { - remove = true; - } - } - } else if (UserHandle.getUserId(entUid) == userId) { - remove = true; - } - if (remove) { - ba.removeAt(i); - } - } - if (ba.size() == 0) { - pmap.removeAt(ip); - } - } + mAppErrors.resetProcessCrashTimeLocked(packageName == null, appId, userId); } boolean didSomething = killPackageProcessesLocked(packageName, appId, userId, @@ -6270,7 +5945,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - private final boolean removeProcessLocked(ProcessRecord app, + boolean removeProcessLocked(ProcessRecord app, boolean callerWillRestart, boolean allowRestart, String reason) { final String name = app.processName; final int uid = app.uid; @@ -9003,6 +8678,11 @@ public final class ActivityManagerService extends ActivityManagerNative continue; } + if (tr.realActivitySuspended) { + if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, activity suspended: " + tr); + continue; + } + // Return the entry if desired by the caller. We always return // the first entry, because callers always expect this to be the // foreground app. We may filter others if the caller has @@ -10933,7 +10613,7 @@ public final class ActivityManagerService extends ActivityManagerNative final long token = Binder.clearCallingIdentity(); try { - appNotResponding(host, null, null, false, "ContentProvider not responding"); + mAppErrors.appNotResponding(host, null, null, false, "ContentProvider not responding"); } finally { Binder.restoreCallingIdentity(token); } @@ -11697,7 +11377,7 @@ public final class ActivityManagerService extends ActivityManagerNative mHandler.post(new Runnable() { @Override public void run() { - appNotResponding(proc, activity, parent, aboveSystem, annotation); + mAppErrors.appNotResponding(proc, activity, parent, aboveSystem, annotation); } }); } @@ -12491,17 +12171,8 @@ public final class ActivityManagerService extends ActivityManagerNative com.android.internal.R.dimen.thumbnail_height); mDefaultPinnedStackBounds = Rect.unflattenFromString(res.getString( com.android.internal.R.string.config_defaultPictureInPictureBounds)); - final String appsNotReportingCrashes = res.getString( - com.android.internal.R.string.config_appsNotReportingCrashes); - if (appsNotReportingCrashes != null) { - final String[] split = appsNotReportingCrashes.split(","); - if (split.length > 0) { - mAppsNotReportingCrashes = new ArraySet<>(); - for (int i = 0; i < split.length; i++) { - mAppsNotReportingCrashes.add(split[i]); - } - } - } + mAppErrors.loadAppsNotReportingCrashesFromConfigLocked(res.getString( + com.android.internal.R.string.config_appsNotReportingCrashes)); } } @@ -12906,174 +12577,12 @@ public final class ActivityManagerService extends ActivityManagerNative } } - private boolean makeAppCrashingLocked(ProcessRecord app, - String shortMsg, String longMsg, String stackTrace) { - app.crashing = true; - app.crashingReport = generateProcessError(app, - ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace); - startAppProblemLocked(app); - app.stopFreezingAllLocked(); - return handleAppCrashLocked(app, "force-crash" /*reason*/, shortMsg, longMsg, stackTrace); - } - - private void makeAppNotRespondingLocked(ProcessRecord app, - String activity, String shortMsg, String longMsg) { - app.notResponding = true; - app.notRespondingReport = generateProcessError(app, - ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING, - activity, shortMsg, longMsg, null); - startAppProblemLocked(app); - app.stopFreezingAllLocked(); - } - - /** - * Generate a process error record, suitable for attachment to a ProcessRecord. - * - * @param app The ProcessRecord in which the error occurred. - * @param condition Crashing, Application Not Responding, etc. Values are defined in - * ActivityManager.AppErrorStateInfo - * @param activity The activity associated with the crash, if known. - * @param shortMsg Short message describing the crash. - * @param longMsg Long message describing the crash. - * @param stackTrace Full crash stack trace, may be null. - * - * @return Returns a fully-formed AppErrorStateInfo record. - */ - private ActivityManager.ProcessErrorStateInfo generateProcessError(ProcessRecord app, - int condition, String activity, String shortMsg, String longMsg, String stackTrace) { - ActivityManager.ProcessErrorStateInfo report = new ActivityManager.ProcessErrorStateInfo(); - - report.condition = condition; - report.processName = app.processName; - report.pid = app.pid; - report.uid = app.info.uid; - report.tag = activity; - report.shortMsg = shortMsg; - report.longMsg = longMsg; - report.stackTrace = stackTrace; - - return report; - } - void killAppAtUsersRequest(ProcessRecord app, Dialog fromDialog) { synchronized (this) { - app.crashing = false; - app.crashingReport = null; - app.notResponding = false; - app.notRespondingReport = null; - if (app.anrDialog == fromDialog) { - app.anrDialog = null; - } - if (app.waitDialog == fromDialog) { - app.waitDialog = null; - } - if (app.pid > 0 && app.pid != MY_PID) { - handleAppCrashLocked(app, "user-terminated" /*reason*/, - null /*shortMsg*/, null /*longMsg*/, null /*stackTrace*/); - app.kill("user request after error", true); - } + mAppErrors.killAppAtUserRequestLocked(app, fromDialog); } } - private boolean handleAppCrashLocked(ProcessRecord app, String reason, - String shortMsg, String longMsg, String stackTrace) { - long now = SystemClock.uptimeMillis(); - - Long crashTime; - if (!app.isolated) { - crashTime = mProcessCrashTimes.get(app.info.processName, app.uid); - } else { - crashTime = null; - } - if (crashTime != null && now < crashTime+ProcessList.MIN_CRASH_INTERVAL) { - // This process loses! - Slog.w(TAG, "Process " + app.info.processName - + " has crashed too many times: killing!"); - EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH, - app.userId, app.info.processName, app.uid); - mStackSupervisor.handleAppCrashLocked(app); - if (!app.persistent) { - // We don't want to start this process again until the user - // explicitly does so... but for persistent process, we really - // need to keep it running. If a persistent process is actually - // repeatedly crashing, then badness for everyone. - EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.userId, app.uid, - app.info.processName); - if (!app.isolated) { - // XXX We don't have a way to mark isolated processes - // as bad, since they don't have a peristent identity. - mBadProcesses.put(app.info.processName, app.uid, - new BadProcessInfo(now, shortMsg, longMsg, stackTrace)); - mProcessCrashTimes.remove(app.info.processName, app.uid); - } - app.bad = true; - app.removed = true; - // Don't let services in this process be restarted and potentially - // annoy the user repeatedly. Unless it is persistent, since those - // processes run critical code. - removeProcessLocked(app, false, false, "crash"); - mStackSupervisor.resumeFocusedStackTopActivityLocked(); - return false; - } - mStackSupervisor.resumeFocusedStackTopActivityLocked(); - } else { - mStackSupervisor.finishTopRunningActivityLocked(app, reason); - } - - // Bump up the crash count of any services currently running in the proc. - for (int i=app.services.size()-1; i>=0; i--) { - // Any services running in the application need to be placed - // back in the pending list. - ServiceRecord sr = app.services.valueAt(i); - sr.crashCount++; - } - - // If the crashing process is what we consider to be the "home process" and it has been - // replaced by a third-party app, clear the package preferred activities from packages - // with a home activity running in the process to prevent a repeatedly crashing app - // from blocking the user to manually clear the list. - final ArrayList<ActivityRecord> activities = app.activities; - if (app == mHomeProcess && activities.size() > 0 - && (mHomeProcess.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { - for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { - final ActivityRecord r = activities.get(activityNdx); - if (r.isHomeActivity()) { - Log.i(TAG, "Clearing package preferred activities from " + r.packageName); - try { - ActivityThread.getPackageManager() - .clearPackagePreferredActivities(r.packageName); - } catch (RemoteException c) { - // pm is in same process, this will never happen. - } - } - } - } - - if (!app.isolated) { - // XXX Can't keep track of crash times for isolated processes, - // because they don't have a perisistent identity. - mProcessCrashTimes.put(app.info.processName, app.uid, now); - } - - if (app.crashHandler != null) mHandler.post(app.crashHandler); - return true; - } - - void startAppProblemLocked(ProcessRecord app) { - // If this app is not running under the current user, then we - // can't give it a report button because that would require - // launching the report UI under a different user. - app.errorReportReceiver = null; - - for (int userId : mUserController.getCurrentProfileIdsLocked()) { - if (app.userId == userId) { - app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver( - mContext, app.info.packageName, app.info.flags); - } - } - skipCurrentReceiverLocked(app); - } - void skipCurrentReceiverLocked(ProcessRecord app) { for (BroadcastQueue queue : mBroadcastQueues) { queue.skipCurrentReceiverLocked(app); @@ -13109,7 +12618,7 @@ public final class ActivityManagerService extends ActivityManagerNative addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo); - crashApplication(r, crashInfo); + mAppErrors.crashApplication(r, crashInfo); } public void handleApplicationStrictModeViolation( @@ -13320,7 +12829,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (r != null && r.pid != Process.myPid() && Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.WTF_IS_FATAL, 0) != 0) { - crashApplication(r, crashInfo); + mAppErrors.crashApplication(r, crashInfo); return true; } else { return false; @@ -13529,164 +13038,6 @@ public final class ActivityManagerService extends ActivityManagerNative } } - /** - * Bring up the "unexpected error" dialog box for a crashing app. - * Deal with edge cases (intercepts from instrumented applications, - * ActivityController, error intent receivers, that sort of thing). - * @param r the application crashing - * @param crashInfo describing the failure - */ - private void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) { - long timeMillis = System.currentTimeMillis(); - String shortMsg = crashInfo.exceptionClassName; - String longMsg = crashInfo.exceptionMessage; - String stackTrace = crashInfo.stackTrace; - if (shortMsg != null && longMsg != null) { - longMsg = shortMsg + ": " + longMsg; - } else if (shortMsg != null) { - longMsg = shortMsg; - } - - AppErrorResult result = new AppErrorResult(); - synchronized (this) { - if (mController != null) { - try { - String name = r != null ? r.processName : null; - int pid = r != null ? r.pid : Binder.getCallingPid(); - int uid = r != null ? r.info.uid : Binder.getCallingUid(); - if (!mController.appCrashed(name, pid, - shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) { - if ("1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0")) - && "Native crash".equals(crashInfo.exceptionClassName)) { - Slog.w(TAG, "Skip killing native crashed app " + name - + "(" + pid + ") during testing"); - } else { - Slog.w(TAG, "Force-killing crashed app " + name - + " at watcher's request"); - if (r != null) { - r.kill("crash", true); - } else { - // Huh. - Process.killProcess(pid); - killProcessGroup(uid, pid); - } - } - return; - } - } catch (RemoteException e) { - mController = null; - Watchdog.getInstance().setActivityController(null); - } - } - - final long origId = Binder.clearCallingIdentity(); - - // If this process is running instrumentation, finish it. - if (r != null && r.instrumentationClass != null) { - Slog.w(TAG, "Error in app " + r.processName - + " running instrumentation " + r.instrumentationClass + ":"); - if (shortMsg != null) Slog.w(TAG, " " + shortMsg); - if (longMsg != null) Slog.w(TAG, " " + longMsg); - Bundle info = new Bundle(); - info.putString("shortMsg", shortMsg); - info.putString("longMsg", longMsg); - finishInstrumentationLocked(r, Activity.RESULT_CANCELED, info); - Binder.restoreCallingIdentity(origId); - return; - } - - // Log crash in battery stats. - if (r != null) { - mBatteryStatsService.noteProcessCrash(r.processName, r.uid); - } - - // If we can't identify the process or it's already exceeded its crash quota, - // quit right away without showing a crash dialog. - if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace)) { - Binder.restoreCallingIdentity(origId); - return; - } - - Message msg = Message.obtain(); - msg.what = SHOW_ERROR_UI_MSG; - HashMap data = new HashMap(); - data.put("result", result); - data.put("app", r); - msg.obj = data; - mUiHandler.sendMessage(msg); - - Binder.restoreCallingIdentity(origId); - } - - int res = result.get(); - - Intent appErrorIntent = null; - synchronized (this) { - if (r != null && !r.isolated) { - // XXX Can't keep track of crash time for isolated processes, - // since they don't have a persistent identity. - mProcessCrashTimes.put(r.info.processName, r.uid, - SystemClock.uptimeMillis()); - } - if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) { - appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo); - } - } - - if (appErrorIntent != null) { - try { - mContext.startActivityAsUser(appErrorIntent, new UserHandle(r.userId)); - } catch (ActivityNotFoundException e) { - Slog.w(TAG, "bug report receiver dissappeared", e); - } - } - } - - Intent createAppErrorIntentLocked(ProcessRecord r, - long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) { - ApplicationErrorReport report = createAppErrorReportLocked(r, timeMillis, crashInfo); - if (report == null) { - return null; - } - Intent result = new Intent(Intent.ACTION_APP_ERROR); - result.setComponent(r.errorReportReceiver); - result.putExtra(Intent.EXTRA_BUG_REPORT, report); - result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - return result; - } - - private ApplicationErrorReport createAppErrorReportLocked(ProcessRecord r, - long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) { - if (r.errorReportReceiver == null) { - return null; - } - - if (!r.crashing && !r.notResponding && !r.forceCrashReport) { - return null; - } - - ApplicationErrorReport report = new ApplicationErrorReport(); - report.packageName = r.info.packageName; - report.installerPackageName = r.errorReportReceiver.getPackageName(); - report.processName = r.processName; - report.time = timeMillis; - report.systemApp = (r.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; - - if (r.crashing || r.forceCrashReport) { - report.type = ApplicationErrorReport.TYPE_CRASH; - report.crashInfo = crashInfo; - } else if (r.notResponding) { - report.type = ApplicationErrorReport.TYPE_ANR; - report.anrInfo = new ApplicationErrorReport.AnrInfo(); - - report.anrInfo.activity = r.notRespondingReport.tag; - report.anrInfo.cause = r.notRespondingReport.shortMsg; - report.anrInfo.info = r.notRespondingReport.longMsg; - } - - return report; - } - public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() { enforceNotIsolatedCaller("getProcessesInErrorState"); // assume our apps are happy - lazy create the list @@ -14424,88 +13775,9 @@ public final class ActivityManagerService extends ActivityManagerNative needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll, dumpPackage); - if (mProcessCrashTimes.getMap().size() > 0) { - boolean printed = false; - long now = SystemClock.uptimeMillis(); - final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap(); - final int NP = pmap.size(); - for (int ip=0; ip<NP; ip++) { - String pname = pmap.keyAt(ip); - SparseArray<Long> uids = pmap.valueAt(ip); - final int N = uids.size(); - for (int i=0; i<N; i++) { - int puid = uids.keyAt(i); - ProcessRecord r = mProcessNames.get(pname, puid); - if (dumpPackage != null && (r == null - || !r.pkgList.containsKey(dumpPackage))) { - continue; - } - if (!printed) { - if (needSep) pw.println(); - needSep = true; - pw.println(" Time since processes crashed:"); - printed = true; - printedAnything = true; - } - pw.print(" Process "); pw.print(pname); - pw.print(" uid "); pw.print(puid); - pw.print(": last crashed "); - TimeUtils.formatDuration(now-uids.valueAt(i), pw); - pw.println(" ago"); - } - } - } - - if (mBadProcesses.getMap().size() > 0) { - boolean printed = false; - final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = mBadProcesses.getMap(); - final int NP = pmap.size(); - for (int ip=0; ip<NP; ip++) { - String pname = pmap.keyAt(ip); - SparseArray<BadProcessInfo> uids = pmap.valueAt(ip); - final int N = uids.size(); - for (int i=0; i<N; i++) { - int puid = uids.keyAt(i); - ProcessRecord r = mProcessNames.get(pname, puid); - if (dumpPackage != null && (r == null - || !r.pkgList.containsKey(dumpPackage))) { - continue; - } - if (!printed) { - if (needSep) pw.println(); - needSep = true; - pw.println(" Bad processes:"); - printedAnything = true; - } - BadProcessInfo info = uids.valueAt(i); - pw.print(" Bad process "); pw.print(pname); - pw.print(" uid "); pw.print(puid); - pw.print(": crashed at time "); pw.println(info.time); - if (info.shortMsg != null) { - pw.print(" Short msg: "); pw.println(info.shortMsg); - } - if (info.longMsg != null) { - pw.print(" Long msg: "); pw.println(info.longMsg); - } - if (info.stack != null) { - pw.println(" Stack:"); - int lastPos = 0; - for (int pos=0; pos<info.stack.length(); pos++) { - if (info.stack.charAt(pos) == '\n') { - pw.print(" "); - pw.write(info.stack, lastPos, pos-lastPos); - pw.println(); - lastPos = pos+1; - } - } - if (lastPos < info.stack.length()) { - pw.print(" "); - pw.write(info.stack, lastPos, info.stack.length()-lastPos); - pw.println(); - } - } - } - } + needSep = mAppErrors.dumpLocked(fd, pw, needSep, dumpPackage); + if (needSep) { + printedAnything = true; } if (dumpPackage == null) { @@ -17587,6 +16859,8 @@ public final class ActivityManagerService extends ActivityManagerNative case Intent.ACTION_PACKAGE_CHANGED: case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE: case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE: + case Intent.ACTION_PACKAGES_SUSPENDED: + case Intent.ACTION_PACKAGES_UNSUSPENDED: // Handle special intents: if this broadcast is from the package // manager about a package being removed, we need to remove all of // its activities from the history stack. @@ -17667,6 +16941,20 @@ public final class ActivityManagerService extends ActivityManagerNative } } break; + case Intent.ACTION_PACKAGES_SUSPENDED: + case Intent.ACTION_PACKAGES_UNSUSPENDED: + final boolean suspended = Intent.ACTION_PACKAGES_SUSPENDED.equals( + intent.getAction()); + final String[] packageNames = intent.getStringArrayExtra( + Intent.EXTRA_CHANGED_PACKAGE_LIST); + final int userHandle = intent.getIntExtra( + Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); + + synchronized(ActivityManagerService.this) { + mRecentTasks.onPackagesSuspendedChanged( + packageNames, suspended, userHandle); + } + break; } break; case Intent.ACTION_PACKAGE_ADDED: @@ -21152,13 +20440,6 @@ public final class ActivityManagerService extends ActivityManagerNative } } - void stopReportingCrashesLocked(ProcessRecord proc) { - if (mAppsNotReportingCrashes == null) { - mAppsNotReportingCrashes = new ArraySet<>(); - } - mAppsNotReportingCrashes.add(proc.info.packageName); - } - private final class LocalService extends ActivityManagerInternal { @Override public void onWakefulnessChanged(int wakefulness) { diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 4bac2d6fd69a..ef8d2305fb2b 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -3041,42 +3041,44 @@ final class ActivityStack { mService.updateOomAdjLocked(); } - final void finishTopRunningActivityLocked(ProcessRecord app, String reason) { + final TaskRecord finishTopRunningActivityLocked(ProcessRecord app, String reason) { ActivityRecord r = topRunningActivityLocked(); - if (r != null && r.app == app) { - // If the top running activity is from this crashing - // process, then terminate it to avoid getting in a loop. - Slog.w(TAG, " Force finishing activity " - + r.intent.getComponent().flattenToShortString()); - int taskNdx = mTaskHistory.indexOf(r.task); - int activityNdx = r.task.mActivities.indexOf(r); - finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false); - // Also terminate any activities below it that aren't yet - // stopped, to avoid a situation where one will get - // re-start our crashing activity once it gets resumed again. - --activityNdx; - if (activityNdx < 0) { - do { - --taskNdx; - if (taskNdx < 0) { - break; - } - activityNdx = mTaskHistory.get(taskNdx).mActivities.size() - 1; - } while (activityNdx < 0); - } - if (activityNdx >= 0) { - r = mTaskHistory.get(taskNdx).mActivities.get(activityNdx); - if (r.state == ActivityState.RESUMED - || r.state == ActivityState.PAUSING - || r.state == ActivityState.PAUSED) { - if (!r.isHomeActivity() || mService.mHomeProcess != r.app) { - Slog.w(TAG, " Force finishing activity " - + r.intent.getComponent().flattenToShortString()); - finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false); - } + TaskRecord finishedTask = null; + if (r == null || r.app != app) { + return null; + } + Slog.w(TAG, " Force finishing activity " + + r.intent.getComponent().flattenToShortString()); + int taskNdx = mTaskHistory.indexOf(r.task); + int activityNdx = r.task.mActivities.indexOf(r); + finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false); + finishedTask = r.task; + // Also terminate any activities below it that aren't yet + // stopped, to avoid a situation where one will get + // re-start our crashing activity once it gets resumed again. + --activityNdx; + if (activityNdx < 0) { + do { + --taskNdx; + if (taskNdx < 0) { + break; + } + activityNdx = mTaskHistory.get(taskNdx).mActivities.size() - 1; + } while (activityNdx < 0); + } + if (activityNdx >= 0) { + r = mTaskHistory.get(taskNdx).mActivities.get(activityNdx); + if (r.state == ActivityState.RESUMED + || r.state == ActivityState.PAUSING + || r.state == ActivityState.PAUSED) { + if (!r.isHomeActivity() || mService.mHomeProcess != r.app) { + Slog.w(TAG, " Force finishing activity " + + r.intent.getComponent().flattenToShortString()); + finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false); } } } + return finishedTask; } final void finishVoiceTask(IVoiceInteractionSession session) { diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 1fc674b7235d..8db2f8ff50f3 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1668,15 +1668,21 @@ public final class ActivityStackSupervisor implements DisplayListener { return false; } - void finishTopRunningActivityLocked(ProcessRecord app, String reason) { + TaskRecord finishTopRunningActivityLocked(ProcessRecord app, String reason) { + TaskRecord finishedTask = null; + ActivityStack focusedStack = getFocusedStack(); for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks; final int numStacks = stacks.size(); for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { final ActivityStack stack = stacks.get(stackNdx); - stack.finishTopRunningActivityLocked(app, reason); + TaskRecord t = stack.finishTopRunningActivityLocked(app, reason); + if (stack == focusedStack || finishedTask == null) { + finishedTask = t; + } } } + return finishedTask; } void finishVoiceTask(IVoiceInteractionSession session) { diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java index c87eae0d61e2..b746a4b6b11c 100644 --- a/services/core/java/com/android/server/am/AppErrorDialog.java +++ b/services/core/java/com/android/server/am/AppErrorDialog.java @@ -16,73 +16,74 @@ package com.android.server.am; +import android.app.ActivityManagerInternal; +import android.app.ActivityOptions; import android.content.Context; -import android.content.DialogInterface; +import android.content.pm.IPackageDataObserver; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.util.Slog; import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.view.WindowManager; -import android.widget.CheckBox; import android.widget.FrameLayout; import android.widget.TextView; -final class AppErrorDialog extends BaseErrorDialog { +import java.util.List; + +import static com.android.server.am.ActivityManagerService.IS_USER_BUILD; + +final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListener { private final ActivityManagerService mService; private final AppErrorResult mResult; private final ProcessRecord mProc; + private final boolean mRepeating; + private CharSequence mName; // Event 'what' codes - static final int FORCE_QUIT = 0; - static final int FORCE_QUIT_AND_REPORT = 1; + static final int FORCE_QUIT = 1; + static final int FORCE_QUIT_AND_REPORT = 2; + static final int RESTART = 3; + static final int RESET = 4; + static final int MUTE = 5; // 5-minute timeout, then we automatically dismiss the crash dialog static final long DISMISS_TIMEOUT = 1000 * 60 * 5; - - public AppErrorDialog(Context context, ActivityManagerService service, - AppErrorResult result, ProcessRecord app) { - super(context); + public AppErrorDialog(Context context, ActivityManagerService service, Data data) { + super(context); Resources res = context.getResources(); mService = service; - mProc = app; - mResult = result; - if ((app.pkgList.size() == 1) && - (mName = context.getPackageManager().getApplicationLabel(app.info)) != null) { - setMessage(res.getString( - com.android.internal.R.string.aerr_application, - mName.toString(), app.info.processName)); + mProc = data.proc; + mResult = data.result; + mRepeating = data.repeating; + if ((mProc.pkgList.size() == 1) && + (mName = context.getPackageManager().getApplicationLabel(mProc.info)) != null) { + setTitle(res.getString( + mRepeating ? com.android.internal.R.string.aerr_application_repeated + : com.android.internal.R.string.aerr_application, + mName.toString(), mProc.info.processName)); } else { - mName = app.processName; - setMessage(res.getString( - com.android.internal.R.string.aerr_process, + mName = mProc.processName; + setTitle(res.getString( + mRepeating ? com.android.internal.R.string.aerr_process_repeated + : com.android.internal.R.string.aerr_process, mName.toString())); } setCancelable(false); - setButton(DialogInterface.BUTTON_POSITIVE, - res.getText(com.android.internal.R.string.force_close), - mHandler.obtainMessage(FORCE_QUIT)); - - if (app.errorReportReceiver != null) { - setButton(DialogInterface.BUTTON_NEGATIVE, - res.getText(com.android.internal.R.string.report), - mHandler.obtainMessage(FORCE_QUIT_AND_REPORT)); - } - - setTitle(res.getText(com.android.internal.R.string.aerr_title)); WindowManager.LayoutParams attrs = getWindow().getAttributes(); - attrs.setTitle("Application Error: " + app.info.processName); + attrs.setTitle("Application Error: " + mProc.info.processName); attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR | WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; getWindow().setAttributes(attrs); - if (app.persistent) { + if (mProc.persistent) { getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); } @@ -95,38 +96,44 @@ final class AppErrorDialog extends BaseErrorDialog { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (!ActivityManagerService.IS_USER_BUILD) { - FrameLayout frame = (FrameLayout) findViewById(android.R.id.custom); - Context context = getContext(); - LayoutInflater.from(context).inflate( - com.android.internal.R.layout.app_error_dialog_dont_show_again, frame, true); - ((TextView) frame.findViewById(com.android.internal.R.id.text)).setText( - context.getResources().getString( - com.android.internal.R.string.aerr_process_silence, - mName.toString())); - findViewById(com.android.internal.R.id.customPanel).setVisibility(View.VISIBLE); - } + final FrameLayout frame = (FrameLayout) findViewById(android.R.id.custom); + final Context context = getContext(); + LayoutInflater.from(context).inflate( + com.android.internal.R.layout.app_error_dialog, frame, true); + + final TextView restart = (TextView) findViewById(com.android.internal.R.id.aerr_restart); + restart.setOnClickListener(this); + restart.setVisibility(!mRepeating ? View.VISIBLE : View.GONE); + final TextView reset = (TextView) findViewById(com.android.internal.R.id.aerr_reset); + reset.setOnClickListener(this); + reset.setVisibility(mRepeating ? View.VISIBLE : View.GONE); + final TextView report = (TextView) findViewById(com.android.internal.R.id.aerr_report); + report.setOnClickListener(this); + final boolean hasReceiver = mProc.errorReportReceiver != null; + report.setVisibility(hasReceiver ? View.VISIBLE : View.GONE); + final TextView close = (TextView) findViewById(com.android.internal.R.id.aerr_close); + close.setOnClickListener(this); + final TextView mute = (TextView) findViewById(com.android.internal.R.id.aerr_mute); + mute.setOnClickListener(this); + mute.setVisibility(!IS_USER_BUILD ? View.VISIBLE : View.GONE); + + findViewById(com.android.internal.R.id.customPanel).setVisibility(View.VISIBLE); } private final Handler mHandler = new Handler() { public void handleMessage(Message msg) { - View view = findViewById(com.android.internal.R.id.checkbox); - final boolean stopReporting = view != null && ((CheckBox) view).isChecked(); + final int result = msg.what; + synchronized (mService) { if (mProc != null && mProc.crashDialog == AppErrorDialog.this) { mProc.crashDialog = null; } - if (stopReporting) { - mService.stopReportingCrashesLocked(mProc); - } } - mResult.set(msg.what); + mResult.set(result); // Make sure we don't have time timeout still hanging around. removeMessages(FORCE_QUIT); - // If this is a timeout we won't be automatically closed, so go - // ahead and explicitly dismiss ourselves just in case. dismiss(); } }; @@ -139,4 +146,34 @@ final class AppErrorDialog extends BaseErrorDialog { } super.dismiss(); } + + @Override + public void onClick(View v) { + switch (v.getId()) { + case com.android.internal.R.id.aerr_restart: + mHandler.obtainMessage(RESTART).sendToTarget(); + break; + case com.android.internal.R.id.aerr_reset: + mHandler.obtainMessage(RESET).sendToTarget(); + break; + case com.android.internal.R.id.aerr_report: + mHandler.obtainMessage(FORCE_QUIT_AND_REPORT).sendToTarget(); + break; + case com.android.internal.R.id.aerr_close: + mHandler.obtainMessage(FORCE_QUIT).sendToTarget(); + break; + case com.android.internal.R.id.aerr_mute: + mHandler.obtainMessage(MUTE).sendToTarget(); + break; + default: + break; + } + } + + static class Data { + AppErrorResult result; + TaskRecord task; + boolean repeating; + ProcessRecord proc; + } } diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java new file mode 100644 index 000000000000..58d9f451b9c9 --- /dev/null +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -0,0 +1,964 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.am; + +import com.android.internal.app.ProcessMap; +import com.android.internal.os.ProcessCpuTracker; +import com.android.server.Watchdog; + +import android.app.Activity; +import android.app.ActivityManager; +import android.app.ActivityOptions; +import android.app.ActivityThread; +import android.app.AppOpsManager; +import android.app.ApplicationErrorReport; +import android.app.Dialog; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageDataObserver; +import android.content.pm.PackageManager; +import android.os.Binder; +import android.os.Bundle; +import android.os.Message; +import android.os.Process; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.EventLog; +import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; +import android.util.TimeUtils; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.concurrent.Semaphore; + +import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.am.ActivityManagerService.MY_PID; +import static com.android.server.am.ActivityManagerService.SYSTEM_DEBUGGABLE; + +/** + * Controls error conditions in applications. + */ +class AppErrors { + + private static final String TAG = TAG_WITH_CLASS_NAME ? "AppErrors" : TAG_AM; + + private final ActivityManagerService mService; + private final Context mContext; + + private ArraySet<String> mAppsNotReportingCrashes; + + /** + * The last time that various processes have crashed since they were last explicitly started. + */ + private final ProcessMap<Long> mProcessCrashTimes = new ProcessMap<>(); + + /** + * The last time that various processes have crashed (not reset even when explicitly started). + */ + private final ProcessMap<Long> mProcessCrashTimesPersistent = new ProcessMap<>(); + + /** + * Set of applications that we consider to be bad, and will reject + * incoming broadcasts from (which the user has no control over). + * Processes are added to this set when they have crashed twice within + * a minimum amount of time; they are removed from it when they are + * later restarted (hopefully due to some user action). The value is the + * time it was added to the list. + */ + private final ProcessMap<BadProcessInfo> mBadProcesses = new ProcessMap<>(); + + + AppErrors(Context context, ActivityManagerService service) { + mService = service; + mContext = context; + } + + boolean dumpLocked(FileDescriptor fd, PrintWriter pw, boolean needSep, + String dumpPackage) { + if (!mProcessCrashTimes.getMap().isEmpty()) { + boolean printed = false; + final long now = SystemClock.uptimeMillis(); + final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap(); + final int processCount = pmap.size(); + for (int ip = 0; ip < processCount; ip++) { + final String pname = pmap.keyAt(ip); + final SparseArray<Long> uids = pmap.valueAt(ip); + final int uidCount = uids.size(); + for (int i = 0; i < uidCount; i++) { + final int puid = uids.keyAt(i); + final ProcessRecord r = mService.mProcessNames.get(pname, puid); + if (dumpPackage != null && (r == null + || !r.pkgList.containsKey(dumpPackage))) { + continue; + } + if (!printed) { + if (needSep) pw.println(); + needSep = true; + pw.println(" Time since processes crashed:"); + printed = true; + } + pw.print(" Process "); pw.print(pname); + pw.print(" uid "); pw.print(puid); + pw.print(": last crashed "); + TimeUtils.formatDuration(now-uids.valueAt(i), pw); + pw.println(" ago"); + } + } + } + + if (!mBadProcesses.getMap().isEmpty()) { + boolean printed = false; + final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = mBadProcesses.getMap(); + final int processCount = pmap.size(); + for (int ip = 0; ip < processCount; ip++) { + final String pname = pmap.keyAt(ip); + final SparseArray<BadProcessInfo> uids = pmap.valueAt(ip); + final int uidCount = uids.size(); + for (int i = 0; i < uidCount; i++) { + final int puid = uids.keyAt(i); + final ProcessRecord r = mService.mProcessNames.get(pname, puid); + if (dumpPackage != null && (r == null + || !r.pkgList.containsKey(dumpPackage))) { + continue; + } + if (!printed) { + if (needSep) pw.println(); + needSep = true; + pw.println(" Bad processes:"); + printed = true; + } + final BadProcessInfo info = uids.valueAt(i); + pw.print(" Bad process "); pw.print(pname); + pw.print(" uid "); pw.print(puid); + pw.print(": crashed at time "); pw.println(info.time); + if (info.shortMsg != null) { + pw.print(" Short msg: "); pw.println(info.shortMsg); + } + if (info.longMsg != null) { + pw.print(" Long msg: "); pw.println(info.longMsg); + } + if (info.stack != null) { + pw.println(" Stack:"); + int lastPos = 0; + for (int pos = 0; pos < info.stack.length(); pos++) { + if (info.stack.charAt(pos) == '\n') { + pw.print(" "); + pw.write(info.stack, lastPos, pos-lastPos); + pw.println(); + lastPos = pos+1; + } + } + if (lastPos < info.stack.length()) { + pw.print(" "); + pw.write(info.stack, lastPos, info.stack.length()-lastPos); + pw.println(); + } + } + } + } + } + return needSep; + } + + boolean isBadProcessLocked(ApplicationInfo info) { + return mBadProcesses.get(info.processName, info.uid) != null; + } + + void clearBadProcessLocked(ApplicationInfo info) { + mBadProcesses.remove(info.processName, info.uid); + } + + void resetProcessCrashTimeLocked(ApplicationInfo info) { + mProcessCrashTimes.remove(info.processName, info.uid); + } + + void resetProcessCrashTimeLocked(boolean resetEntireUser, int appId, int userId) { + final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap(); + for (int ip = pmap.size() - 1; ip >= 0; ip--) { + SparseArray<Long> ba = pmap.valueAt(ip); + for (int i = ba.size() - 1; i >= 0; i--) { + boolean remove = false; + final int entUid = ba.keyAt(i); + if (!resetEntireUser) { + if (userId == UserHandle.USER_ALL) { + if (UserHandle.getAppId(entUid) == appId) { + remove = true; + } + } else { + if (entUid == UserHandle.getUid(userId, appId)) { + remove = true; + } + } + } else if (UserHandle.getUserId(entUid) == userId) { + remove = true; + } + if (remove) { + ba.removeAt(i); + } + } + if (ba.size() == 0) { + pmap.removeAt(ip); + } + } + } + + void loadAppsNotReportingCrashesFromConfigLocked(String appsNotReportingCrashesConfig) { + if (appsNotReportingCrashesConfig != null) { + final String[] split = appsNotReportingCrashesConfig.split(","); + if (split.length > 0) { + mAppsNotReportingCrashes = new ArraySet<>(); + Collections.addAll(mAppsNotReportingCrashes, split); + } + } + } + + void killAppAtUserRequestLocked(ProcessRecord app, Dialog fromDialog) { + app.crashing = false; + app.crashingReport = null; + app.notResponding = false; + app.notRespondingReport = null; + if (app.anrDialog == fromDialog) { + app.anrDialog = null; + } + if (app.waitDialog == fromDialog) { + app.waitDialog = null; + } + if (app.pid > 0 && app.pid != MY_PID) { + handleAppCrashLocked(app, "user-terminated" /*reason*/, + null /*shortMsg*/, null /*longMsg*/, null /*stackTrace*/, null /*data*/); + app.kill("user request after error", true); + } + } + + void scheduleAppCrashLocked(int uid, int initialPid, String packageName, + String message) { + ProcessRecord proc = null; + + // Figure out which process to kill. We don't trust that initialPid + // still has any relation to current pids, so must scan through the + // list. + + synchronized (mService.mPidsSelfLocked) { + for (int i=0; i<mService.mPidsSelfLocked.size(); i++) { + ProcessRecord p = mService.mPidsSelfLocked.valueAt(i); + if (p.uid != uid) { + continue; + } + if (p.pid == initialPid) { + proc = p; + break; + } + if (p.pkgList.containsKey(packageName)) { + proc = p; + } + } + } + + if (proc == null) { + Slog.w(TAG, "crashApplication: nothing for uid=" + uid + + " initialPid=" + initialPid + + " packageName=" + packageName); + return; + } + + if (proc.thread != null) { + if (proc.pid == Process.myPid()) { + Log.w(TAG, "crashApplication: trying to crash self!"); + return; + } + long ident = Binder.clearCallingIdentity(); + try { + proc.thread.scheduleCrash(message); + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + /** + * Bring up the "unexpected error" dialog box for a crashing app. + * Deal with edge cases (intercepts from instrumented applications, + * ActivityController, error intent receivers, that sort of thing). + * @param r the application crashing + * @param crashInfo describing the failure + */ + void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) { + long timeMillis = System.currentTimeMillis(); + String shortMsg = crashInfo.exceptionClassName; + String longMsg = crashInfo.exceptionMessage; + String stackTrace = crashInfo.stackTrace; + if (shortMsg != null && longMsg != null) { + longMsg = shortMsg + ": " + longMsg; + } else if (shortMsg != null) { + longMsg = shortMsg; + } + + AppErrorResult result = new AppErrorResult(); + TaskRecord task; + synchronized (mService) { + if (mService.mController != null) { + try { + String name = r != null ? r.processName : null; + int pid = r != null ? r.pid : Binder.getCallingPid(); + int uid = r != null ? r.info.uid : Binder.getCallingUid(); + if (!mService.mController.appCrashed(name, pid, + shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) { + if ("1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0")) + && "Native crash".equals(crashInfo.exceptionClassName)) { + Slog.w(TAG, "Skip killing native crashed app " + name + + "(" + pid + ") during testing"); + } else { + Slog.w(TAG, "Force-killing crashed app " + name + + " at watcher's request"); + if (r != null) { + r.kill("crash", true); + } else { + // Huh. + Process.killProcess(pid); + ActivityManagerService.killProcessGroup(uid, pid); + } + } + return; + } + } catch (RemoteException e) { + mService.mController = null; + Watchdog.getInstance().setActivityController(null); + } + } + + final long origId = Binder.clearCallingIdentity(); + + // If this process is running instrumentation, finish it. + if (r != null && r.instrumentationClass != null) { + Slog.w(TAG, "Error in app " + r.processName + + " running instrumentation " + r.instrumentationClass + ":"); + if (shortMsg != null) Slog.w(TAG, " " + shortMsg); + if (longMsg != null) Slog.w(TAG, " " + longMsg); + Bundle info = new Bundle(); + info.putString("shortMsg", shortMsg); + info.putString("longMsg", longMsg); + mService.finishInstrumentationLocked(r, Activity.RESULT_CANCELED, info); + Binder.restoreCallingIdentity(origId); + return; + } + + // Log crash in battery stats. + if (r != null) { + mService.mBatteryStatsService.noteProcessCrash(r.processName, r.uid); + } + + AppErrorDialog.Data data = new AppErrorDialog.Data(); + data.result = result; + data.proc = r; + + // If we can't identify the process or it's already exceeded its crash quota, + // quit right away without showing a crash dialog. + if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, data)) { + Binder.restoreCallingIdentity(origId); + return; + } + + Message msg = Message.obtain(); + msg.what = ActivityManagerService.SHOW_ERROR_UI_MSG; + + task = data.task; + msg.obj = data; + mService.mUiHandler.sendMessage(msg); + + Binder.restoreCallingIdentity(origId); + } + + int res = result.get(); + + Intent appErrorIntent = null; + final long ident = Binder.clearCallingIdentity(); + try { + if (res == AppErrorDialog.RESET) { + String[] packageList = r.getPackageList(); + if (packageList != null) { + PackageManager pm = mContext.getPackageManager(); + final Semaphore s = new Semaphore(0); + for (int i = 0; i < packageList.length; i++) { + if (i < packageList.length - 1) { + pm.deleteApplicationCacheFiles(packageList[i], null); + } else { + pm.deleteApplicationCacheFiles(packageList[i], + new IPackageDataObserver.Stub() { + @Override + public void onRemoveCompleted(String packageName, + boolean succeeded) { + s.release(); + } + }); + + // Wait until cache has been cleared before we restart. + try { + s.acquire(); + } catch (InterruptedException e) { + } + } + } + } + // If there was nothing to reset, just restart; + res = AppErrorDialog.RESTART; + } + synchronized (mService) { + if (res == AppErrorDialog.MUTE) { + stopReportingCrashesLocked(r); + } + if (res == AppErrorDialog.RESTART) { + mService.removeProcessLocked(r, false, true, "crash"); + if (task != null) { + try { + mService.startActivityFromRecents(task.taskId, + ActivityOptions.makeBasic().toBundle()); + } catch (IllegalArgumentException e) { + // Hmm, that didn't work, app might have crashed before creating a + // recents entry. Let's see if we have a safe-to-restart intent. + if (task.intent.getCategories().contains( + Intent.CATEGORY_LAUNCHER)) { + mService.startActivityInPackage(task.mCallingUid, + task.mCallingPackage, task.intent, + null, null, null, 0, 0, + ActivityOptions.makeBasic().toBundle(), + task.userId, null, null); + } + } + } + } + if (res == AppErrorDialog.FORCE_QUIT) { + long orig = Binder.clearCallingIdentity(); + try { + // Kill it with fire! + mService.mStackSupervisor.handleAppCrashLocked(r); + if (!r.persistent) { + mService.removeProcessLocked(r, false, false, "crash"); + mService.mStackSupervisor.resumeFocusedStackTopActivityLocked(); + } + } finally { + Binder.restoreCallingIdentity(orig); + } + } + if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) { + appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo); + } + if (r != null && !r.isolated && res != AppErrorDialog.RESTART) { + // XXX Can't keep track of crash time for isolated processes, + // since they don't have a persistent identity. + mProcessCrashTimes.put(r.info.processName, r.uid, + SystemClock.uptimeMillis()); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + + if (appErrorIntent != null) { + try { + mContext.startActivityAsUser(appErrorIntent, new UserHandle(r.userId)); + } catch (ActivityNotFoundException e) { + Slog.w(TAG, "bug report receiver dissappeared", e); + } + } + } + + private boolean makeAppCrashingLocked(ProcessRecord app, + String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) { + app.crashing = true; + app.crashingReport = generateProcessError(app, + ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace); + startAppProblemLocked(app); + app.stopFreezingAllLocked(); + return handleAppCrashLocked(app, "force-crash" /*reason*/, shortMsg, longMsg, stackTrace, + data); + } + + void startAppProblemLocked(ProcessRecord app) { + // If this app is not running under the current user, then we + // can't give it a report button because that would require + // launching the report UI under a different user. + app.errorReportReceiver = null; + + for (int userId : mService.mUserController.getCurrentProfileIdsLocked()) { + if (app.userId == userId) { + app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver( + mContext, app.info.packageName, app.info.flags); + } + } + mService.skipCurrentReceiverLocked(app); + } + + /** + * Generate a process error record, suitable for attachment to a ProcessRecord. + * + * @param app The ProcessRecord in which the error occurred. + * @param condition Crashing, Application Not Responding, etc. Values are defined in + * ActivityManager.AppErrorStateInfo + * @param activity The activity associated with the crash, if known. + * @param shortMsg Short message describing the crash. + * @param longMsg Long message describing the crash. + * @param stackTrace Full crash stack trace, may be null. + * + * @return Returns a fully-formed AppErrorStateInfo record. + */ + private ActivityManager.ProcessErrorStateInfo generateProcessError(ProcessRecord app, + int condition, String activity, String shortMsg, String longMsg, String stackTrace) { + ActivityManager.ProcessErrorStateInfo report = new ActivityManager.ProcessErrorStateInfo(); + + report.condition = condition; + report.processName = app.processName; + report.pid = app.pid; + report.uid = app.info.uid; + report.tag = activity; + report.shortMsg = shortMsg; + report.longMsg = longMsg; + report.stackTrace = stackTrace; + + return report; + } + + Intent createAppErrorIntentLocked(ProcessRecord r, + long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) { + ApplicationErrorReport report = createAppErrorReportLocked(r, timeMillis, crashInfo); + if (report == null) { + return null; + } + Intent result = new Intent(Intent.ACTION_APP_ERROR); + result.setComponent(r.errorReportReceiver); + result.putExtra(Intent.EXTRA_BUG_REPORT, report); + result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return result; + } + + private ApplicationErrorReport createAppErrorReportLocked(ProcessRecord r, + long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) { + if (r.errorReportReceiver == null) { + return null; + } + + if (!r.crashing && !r.notResponding && !r.forceCrashReport) { + return null; + } + + ApplicationErrorReport report = new ApplicationErrorReport(); + report.packageName = r.info.packageName; + report.installerPackageName = r.errorReportReceiver.getPackageName(); + report.processName = r.processName; + report.time = timeMillis; + report.systemApp = (r.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + + if (r.crashing || r.forceCrashReport) { + report.type = ApplicationErrorReport.TYPE_CRASH; + report.crashInfo = crashInfo; + } else if (r.notResponding) { + report.type = ApplicationErrorReport.TYPE_ANR; + report.anrInfo = new ApplicationErrorReport.AnrInfo(); + + report.anrInfo.activity = r.notRespondingReport.tag; + report.anrInfo.cause = r.notRespondingReport.shortMsg; + report.anrInfo.info = r.notRespondingReport.longMsg; + } + + return report; + } + + boolean handleAppCrashLocked(ProcessRecord app, String reason, + String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) { + long now = SystemClock.uptimeMillis(); + + Long crashTime; + Long crashTimePersistent; + if (!app.isolated) { + crashTime = mProcessCrashTimes.get(app.info.processName, app.uid); + crashTimePersistent = mProcessCrashTimesPersistent.get(app.info.processName, app.uid); + } else { + crashTime = crashTimePersistent = null; + } + if (crashTime != null && now < crashTime+ProcessList.MIN_CRASH_INTERVAL) { + // This process loses! + Slog.w(TAG, "Process " + app.info.processName + + " has crashed too many times: killing!"); + EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH, + app.userId, app.info.processName, app.uid); + mService.mStackSupervisor.handleAppCrashLocked(app); + if (!app.persistent) { + // We don't want to start this process again until the user + // explicitly does so... but for persistent process, we really + // need to keep it running. If a persistent process is actually + // repeatedly crashing, then badness for everyone. + EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.userId, app.uid, + app.info.processName); + if (!app.isolated) { + // XXX We don't have a way to mark isolated processes + // as bad, since they don't have a peristent identity. + mBadProcesses.put(app.info.processName, app.uid, + new BadProcessInfo(now, shortMsg, longMsg, stackTrace)); + mProcessCrashTimes.remove(app.info.processName, app.uid); + } + app.bad = true; + app.removed = true; + // Don't let services in this process be restarted and potentially + // annoy the user repeatedly. Unless it is persistent, since those + // processes run critical code. + mService.removeProcessLocked(app, false, false, "crash"); + mService.mStackSupervisor.resumeFocusedStackTopActivityLocked(); + return false; + } + mService.mStackSupervisor.resumeFocusedStackTopActivityLocked(); + } else { + TaskRecord affectedTask = + mService.mStackSupervisor.finishTopRunningActivityLocked(app, reason); + if (data != null) { + data.task = affectedTask; + } + if (data != null && crashTimePersistent != null + && now < crashTimePersistent + ProcessList.MIN_CRASH_INTERVAL) { + data.repeating = true; + } + } + + // Bump up the crash count of any services currently running in the proc. + for (int i=app.services.size()-1; i>=0; i--) { + // Any services running in the application need to be placed + // back in the pending list. + ServiceRecord sr = app.services.valueAt(i); + sr.crashCount++; + } + + // If the crashing process is what we consider to be the "home process" and it has been + // replaced by a third-party app, clear the package preferred activities from packages + // with a home activity running in the process to prevent a repeatedly crashing app + // from blocking the user to manually clear the list. + final ArrayList<ActivityRecord> activities = app.activities; + if (app == mService.mHomeProcess && activities.size() > 0 + && (mService.mHomeProcess.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { + for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { + final ActivityRecord r = activities.get(activityNdx); + if (r.isHomeActivity()) { + Log.i(TAG, "Clearing package preferred activities from " + r.packageName); + try { + ActivityThread.getPackageManager() + .clearPackagePreferredActivities(r.packageName); + } catch (RemoteException c) { + // pm is in same process, this will never happen. + } + } + } + } + + if (!app.isolated) { + // XXX Can't keep track of crash times for isolated processes, + // because they don't have a perisistent identity. + mProcessCrashTimes.put(app.info.processName, app.uid, now); + mProcessCrashTimesPersistent.put(app.info.processName, app.uid, now); + } + + if (app.crashHandler != null) mService.mHandler.post(app.crashHandler); + return true; + } + + void handleShowAppErrorUi(Message msg) { + AppErrorDialog.Data data = (AppErrorDialog.Data) msg.obj; + boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0; + synchronized (mService) { + ProcessRecord proc = data.proc; + AppErrorResult res = data.result; + if (proc != null && proc.crashDialog != null) { + Slog.e(TAG, "App already has crash dialog: " + proc); + if (res != null) { + res.set(0); + } + return; + } + boolean isBackground = (UserHandle.getAppId(proc.uid) + >= Process.FIRST_APPLICATION_UID + && proc.pid != MY_PID); + for (int userId : mService.mUserController.getCurrentProfileIdsLocked()) { + isBackground &= (proc.userId != userId); + } + if (isBackground && !showBackground) { + Slog.w(TAG, "Skipping crash dialog of " + proc + ": background"); + if (res != null) { + res.set(0); + } + return; + } + final boolean crashSilenced = mAppsNotReportingCrashes != null && + mAppsNotReportingCrashes.contains(proc.info.packageName); + if (mService.canShowErrorDialogs() && !crashSilenced) { + Dialog d = new AppErrorDialog(mContext, mService, data); + d.show(); + proc.crashDialog = d; + } else { + // The device is asleep, so just pretend that the user + // saw a crash dialog and hit "force quit". + if (res != null) { + res.set(0); + } + } + } + } + + void stopReportingCrashesLocked(ProcessRecord proc) { + if (mAppsNotReportingCrashes == null) { + mAppsNotReportingCrashes = new ArraySet<>(); + } + mAppsNotReportingCrashes.add(proc.info.packageName); + } + + final void appNotResponding(ProcessRecord app, ActivityRecord activity, + ActivityRecord parent, boolean aboveSystem, final String annotation) { + ArrayList<Integer> firstPids = new ArrayList<Integer>(5); + SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20); + + if (mService.mController != null) { + try { + // 0 == continue, -1 = kill process immediately + int res = mService.mController.appEarlyNotResponding(app.processName, app.pid, annotation); + if (res < 0 && app.pid != MY_PID) { + app.kill("anr", true); + } + } catch (RemoteException e) { + mService.mController = null; + Watchdog.getInstance().setActivityController(null); + } + } + + long anrTime = SystemClock.uptimeMillis(); + if (ActivityManagerService.MONITOR_CPU_USAGE) { + mService.updateCpuStatsNow(); + } + + synchronized (mService) { + // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down. + if (mService.mShuttingDown) { + Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation); + return; + } else if (app.notResponding) { + Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation); + return; + } else if (app.crashing) { + Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation); + return; + } + + // In case we come through here for the same app before completing + // this one, mark as anring now so we will bail out. + app.notResponding = true; + + // Log the ANR to the event log. + EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid, + app.processName, app.info.flags, annotation); + + // Dump thread traces as quickly as we can, starting with "interesting" processes. + firstPids.add(app.pid); + + int parentPid = app.pid; + if (parent != null && parent.app != null && parent.app.pid > 0) { + parentPid = parent.app.pid; + } + if (parentPid != app.pid) firstPids.add(parentPid); + + if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID); + + for (int i = mService.mLruProcesses.size() - 1; i >= 0; i--) { + ProcessRecord r = mService.mLruProcesses.get(i); + if (r != null && r.thread != null) { + int pid = r.pid; + if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) { + if (r.persistent) { + firstPids.add(pid); + } else { + lastPids.put(pid, Boolean.TRUE); + } + } + } + } + } + + // Log the ANR to the main log. + StringBuilder info = new StringBuilder(); + info.setLength(0); + info.append("ANR in ").append(app.processName); + if (activity != null && activity.shortComponentName != null) { + info.append(" (").append(activity.shortComponentName).append(")"); + } + info.append("\n"); + info.append("PID: ").append(app.pid).append("\n"); + if (annotation != null) { + info.append("Reason: ").append(annotation).append("\n"); + } + if (parent != null && parent != activity) { + info.append("Parent: ").append(parent.shortComponentName).append("\n"); + } + + final ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true); + + File tracesFile = mService.dumpStackTraces(true, firstPids, processCpuTracker, lastPids, + NATIVE_STACKS_OF_INTEREST); + + String cpuInfo = null; + if (ActivityManagerService.MONITOR_CPU_USAGE) { + mService.updateCpuStatsNow(); + synchronized (mService.mProcessCpuTracker) { + cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime); + } + info.append(processCpuTracker.printCurrentLoad()); + info.append(cpuInfo); + } + + info.append(processCpuTracker.printCurrentState(anrTime)); + + Slog.e(TAG, info.toString()); + if (tracesFile == null) { + // There is no trace file, so dump (only) the alleged culprit's threads to the log + Process.sendSignal(app.pid, Process.SIGNAL_QUIT); + } + + mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation, + cpuInfo, tracesFile, null); + + if (mService.mController != null) { + try { + // 0 == show dialog, 1 = keep waiting, -1 = kill process immediately + int res = mService.mController.appNotResponding( + app.processName, app.pid, info.toString()); + if (res != 0) { + if (res < 0 && app.pid != MY_PID) { + app.kill("anr", true); + } else { + synchronized (mService) { + mService.mServices.scheduleServiceTimeoutLocked(app); + } + } + return; + } + } catch (RemoteException e) { + mService.mController = null; + Watchdog.getInstance().setActivityController(null); + } + } + + // Unless configured otherwise, swallow ANRs in background processes & kill the process. + boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0; + + synchronized (mService) { + mService.mBatteryStatsService.noteProcessAnr(app.processName, app.uid); + + if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) { + app.kill("bg anr", true); + return; + } + + // Set the app's notResponding state, and look up the errorReportReceiver + makeAppNotRespondingLocked(app, + activity != null ? activity.shortComponentName : null, + annotation != null ? "ANR " + annotation : "ANR", + info.toString()); + + // Bring up the infamous App Not Responding dialog + Message msg = Message.obtain(); + HashMap<String, Object> map = new HashMap<String, Object>(); + msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG; + msg.obj = map; + msg.arg1 = aboveSystem ? 1 : 0; + map.put("app", app); + if (activity != null) { + map.put("activity", activity); + } + + mService.mUiHandler.sendMessage(msg); + } + } + + private void makeAppNotRespondingLocked(ProcessRecord app, + String activity, String shortMsg, String longMsg) { + app.notResponding = true; + app.notRespondingReport = generateProcessError(app, + ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING, + activity, shortMsg, longMsg, null); + startAppProblemLocked(app); + app.stopFreezingAllLocked(); + } + + void handleShowAnrUi(Message msg) { + synchronized (mService) { + HashMap<String, Object> data = (HashMap<String, Object>) msg.obj; + ProcessRecord proc = (ProcessRecord)data.get("app"); + if (proc != null && proc.anrDialog != null) { + Slog.e(TAG, "App already has anr dialog: " + proc); + return; + } + + Intent intent = new Intent("android.intent.action.ANR"); + if (!mService.mProcessesReady) { + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY + | Intent.FLAG_RECEIVER_FOREGROUND); + } + mService.broadcastIntentLocked(null, null, intent, + null, null, 0, null, null, null, AppOpsManager.OP_NONE, + null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */); + + if (mService.canShowErrorDialogs()) { + Dialog d = new AppNotRespondingDialog(mService, + mContext, proc, (ActivityRecord)data.get("activity"), + msg.arg1 != 0); + d.show(); + proc.anrDialog = d; + } else { + // Just kill the app if there is no dialog to be shown. + mService.killAppAtUsersRequest(proc, null); + } + } + } + + /** + * Information about a process that is currently marked as bad. + */ + static final class BadProcessInfo { + BadProcessInfo(long time, String shortMsg, String longMsg, String stack) { + this.time = time; + this.shortMsg = shortMsg; + this.longMsg = longMsg; + this.stack = stack; + } + + final long time; + final String shortMsg; + final String longMsg; + final String stack; + } + +} diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java index f4c166421c35..4587b720d814 100644 --- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java +++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java @@ -117,7 +117,7 @@ final class AppNotRespondingDialog extends BaseErrorDialog { ProcessRecord app = mProc; if (msg.what == WAIT_AND_REPORT) { - appErrorIntent = mService.createAppErrorIntentLocked(app, + appErrorIntent = mService.mAppErrors.createAppErrorIntentLocked(app, System.currentTimeMillis(), null); } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 622aa16cb852..37b0af11a7a6 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -199,7 +199,7 @@ public final class BroadcastQueue { @Override public void run() { - mService.appNotResponding(mApp, null, null, false, mAnnotation); + mService.mAppErrors.appNotResponding(mApp, null, null, false, mAnnotation); } } diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java index 3f0674d99939..9c139d524bc1 100644 --- a/services/core/java/com/android/server/am/RecentTasks.java +++ b/services/core/java/com/android/server/am/RecentTasks.java @@ -24,6 +24,8 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.am.TaskRecord.INVALID_TASK_ID; +import com.google.android.collect.Sets; + import android.app.ActivityManager; import android.app.AppGlobals; import android.content.ComponentName; @@ -45,6 +47,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.Set; /** * Class for managing the recent tasks list. @@ -188,6 +191,21 @@ class RecentTasks extends ArrayList<TaskRecord> { } } + void onPackagesSuspendedChanged(String[] packages, boolean suspended, int userId) { + final Set<String> packageNames = Sets.newHashSet(packages); + for (int i = size() - 1; i >= 0; --i) { + final TaskRecord tr = get(i); + if (tr.realActivity != null + && packageNames.contains(tr.realActivity.getPackageName()) + && tr.userId == userId + && tr.realActivitySuspended != suspended) { + tr.realActivitySuspended = suspended; + notifyTaskPersisterLocked(tr, false); + } + } + + } + /** * Update the recent tasks lists: make sure tasks should still be here (their * applications / activities still exist), update their availability, fix-up ordering @@ -683,5 +701,4 @@ class RecentTasks extends ArrayList<TaskRecord> { // Let the caller know where we left off. return start + tmpSize; } - } diff --git a/services/core/java/com/android/server/am/StrictModeViolationDialog.java b/services/core/java/com/android/server/am/StrictModeViolationDialog.java index fda1ec130980..6da84bd589fc 100644 --- a/services/core/java/com/android/server/am/StrictModeViolationDialog.java +++ b/services/core/java/com/android/server/am/StrictModeViolationDialog.java @@ -73,7 +73,6 @@ final class StrictModeViolationDialog extends BaseErrorDialog { mHandler.obtainMessage(ACTION_OK_AND_REPORT)); } - setTitle(res.getText(com.android.internal.R.string.aerr_title)); getWindow().addPrivateFlags(PRIVATE_FLAG_SYSTEM_ERROR); getWindow().setTitle("Strict Mode Violation: " + app.info.processName); diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 4ce8b2f67eee..fd787df25115 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -95,6 +95,7 @@ final class TaskRecord { private static final String TAG_INTENT = "intent"; private static final String TAG_AFFINITYINTENT = "affinity_intent"; static final String ATTR_REALACTIVITY = "real_activity"; + static final String ATTR_REALACTIVITY_SUSPENDED = "real_activity_suspended"; private static final String ATTR_ORIGACTIVITY = "orig_activity"; private static final String TAG_ACTIVITY = "activity"; private static final String ATTR_AFFINITY = "affinity"; @@ -136,6 +137,8 @@ final class TaskRecord { int effectiveUid; // The current effective uid of the identity of this task. ComponentName origActivity; // The non-alias activity component of the intent. ComponentName realActivity; // The actual activity component that started the task. + boolean realActivitySuspended; // True if the actual activity component that started the + // task is suspended. long firstActiveTime; // First time this task was active. long lastActiveTime; // Last time this task was active, including sleep. boolean inRecents; // Actually in the recents list? @@ -305,7 +308,7 @@ final class TaskRecord { boolean neverRelinquishIdentity, TaskDescription _lastTaskDescription, TaskThumbnailInfo lastThumbnailInfo, int taskAffiliation, int prevTaskId, int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage, - boolean resizeable, boolean privileged) { + boolean resizeable, boolean privileged, boolean realActivitySuspended) { mService = service; mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX + TaskPersister.IMAGE_EXTENSION; @@ -319,6 +322,7 @@ final class TaskRecord { voiceSession = null; voiceInteractor = null; realActivity = _realActivity; + realActivitySuspended = realActivitySuspended; origActivity = _origActivity; rootWasReset = _rootWasReset; isAvailable = true; @@ -1027,6 +1031,7 @@ final class TaskRecord { if (realActivity != null) { out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString()); } + out.attribute(null, ATTR_REALACTIVITY_SUSPENDED, String.valueOf(realActivitySuspended)); if (origActivity != null) { out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString()); } @@ -1105,6 +1110,7 @@ final class TaskRecord { Intent affinityIntent = null; ArrayList<ActivityRecord> activities = new ArrayList<>(); ComponentName realActivity = null; + boolean realActivitySuspended = false; ComponentName origActivity = null; String affinity = null; String rootAffinity = null; @@ -1143,6 +1149,8 @@ final class TaskRecord { if (taskId == INVALID_TASK_ID) taskId = Integer.valueOf(attrValue); } else if (ATTR_REALACTIVITY.equals(attrName)) { realActivity = ComponentName.unflattenFromString(attrValue); + } else if (ATTR_REALACTIVITY_SUSPENDED.equals(attrName)) { + realActivitySuspended = Boolean.valueOf(attrValue); } else if (ATTR_ORIGACTIVITY.equals(attrName)) { origActivity = ComponentName.unflattenFromString(attrValue); } else if (ATTR_AFFINITY.equals(attrName)) { @@ -1253,7 +1261,8 @@ final class TaskRecord { autoRemoveRecents, askedCompatMode, taskType, userId, effectiveUid, lastDescription, activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity, taskDescription, thumbnailInfo, taskAffiliation, prevTaskId, nextTaskId, - taskAffiliationColor, callingUid, callingPackage, resizeable, privileged); + taskAffiliationColor, callingUid, callingPackage, resizeable, privileged, + realActivitySuspended); task.updateOverrideConfiguration(bounds); for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) { diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java index 7ac3c4b514d6..e74d636fe389 100644 --- a/services/core/java/com/android/server/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java @@ -19,6 +19,7 @@ package com.android.server.fingerprint; import android.Manifest; import android.app.ActivityManager; import android.app.ActivityManager.RunningAppProcessInfo; +import android.app.trust.TrustManager; import android.app.ActivityManagerNative; import android.app.AlarmManager; import android.app.AppOpsManager; @@ -103,6 +104,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe private static final int MAX_FAILED_ATTEMPTS = 5; private static final int FINGERPRINT_ACQUIRED_GOOD = 0; private final String mKeyguardPackage; + private int mCurrentUserId = UserHandle.USER_CURRENT; Handler mHandler = new Handler() { @Override @@ -125,6 +127,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe private IFingerprintDaemon mDaemon; private final PowerManager mPowerManager; private final AlarmManager mAlarmManager; + private final UserManager mUserManager; private final BroadcastReceiver mLockoutReceiver = new BroadcastReceiver() { @Override @@ -152,6 +155,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe mAlarmManager = mContext.getSystemService(AlarmManager.class); mContext.registerReceiver(mLockoutReceiver, new IntentFilter(ACTION_LOCKOUT_RESET), RESET_FINGERPRINT_LOCKOUT, null /* handler */); + mUserManager = UserManager.get(mContext); } @Override @@ -170,7 +174,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe mDaemon.init(mDaemonCallback); mHalDeviceId = mDaemon.openHal(); if (mHalDeviceId != 0) { - updateActiveGroup(ActivityManager.getCurrentUser()); + updateActiveGroup(ActivityManager.getCurrentUser(), null); } else { Slog.w(TAG, "Failed to open Fingerprint HAL!"); mDaemon = null; @@ -261,7 +265,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe } void handleUserSwitching(int userId) { - updateActiveGroup(userId); + updateActiveGroup(userId, null); } private void removeClient(ClientMonitor client) { @@ -414,7 +418,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe removeClient(mEnrollClient); } - void startAuthentication(IBinder token, long opId, int groupId, + void startAuthentication(IBinder token, long opId, int realUserId, int groupId, IFingerprintServiceReceiver receiver, int flags, boolean restricted, String opPackageName) { IFingerprintDaemon daemon = getFingerprintDaemon(); @@ -423,6 +427,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe return; } stopPendingOperations(true); + updateActiveGroup(groupId, opPackageName); mAuthClient = new ClientMonitor(token, receiver, groupId, restricted, opPackageName); if (inLockoutMode()) { Slog.v(TAG, "In lockout mode; disallowing authentication"); @@ -564,7 +569,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe checkPermission(USE_FINGERPRINT); final int uid = Binder.getCallingUid(); final int pid = Binder.getCallingPid(); - if (opPackageName.equals(mKeyguardPackage)) { + if (isKeyguard(opPackageName)) { return true; // Keyguard is always allowed } if (!isCurrentUserOrProfile(UserHandle.getCallingUserId())) { @@ -583,6 +588,14 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe return true; } + /** + * @param clientPackage + * @return true if this is keyguard package + */ + private boolean isKeyguard(String clientPackage) { + return mKeyguardPackage.equals(clientPackage); + } + private void addLockoutResetMonitor(FingerprintServiceLockoutResetMonitor monitor) { if (!mLockoutMonitors.contains(monitor)) { mLockoutMonitors.add(monitor); @@ -927,14 +940,15 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe // Group ID is arbitrarily set to parent profile user ID. It just represents // the default fingerprints for the user. final int effectiveGroupId = getEffectiveUserId(groupId); + final int realUserId = Binder.getCallingUid(); final boolean restricted = isRestricted(); mHandler.post(new Runnable() { @Override public void run() { MetricsLogger.histogram(mContext, "fingerprint_token", opId != 0L ? 1 : 0); - startAuthentication(token, opId, effectiveGroupId, receiver, flags, restricted, - opPackageName); + startAuthentication(token, opId, realUserId, effectiveGroupId, receiver, + flags, restricted, opPackageName); } }); } @@ -953,6 +967,17 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe } @Override // Binder call + public void setActiveUser(final int userId) { + checkPermission(MANAGE_FINGERPRINT); + mHandler.post(new Runnable() { + @Override + public void run() { + updateActiveGroup(userId, null); + } + }); + } + + @Override // Binder call public void remove(final IBinder token, final int fingerId, final int groupId, final IFingerprintServiceReceiver receiver) { checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission @@ -1102,33 +1127,56 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe listenForUserSwitches(); } - private void updateActiveGroup(int userId) { + private void updateActiveGroup(int userId, String clientPackage) { IFingerprintDaemon daemon = getFingerprintDaemon(); if (daemon != null) { try { - userId = getEffectiveUserId(userId); - final File systemDir = Environment.getUserSystemDirectory(userId); - final File fpDir = new File(systemDir, FP_DATA_DIR); - if (!fpDir.exists()) { - if (!fpDir.mkdir()) { - Slog.v(TAG, "Cannot make directory: " + fpDir.getAbsolutePath()); - return; - } - // Calling mkdir() from this process will create a directory with our - // permissions (inherited from the containing dir). This command fixes - // the label. - if (!SELinux.restorecon(fpDir)) { - Slog.w(TAG, "Restorecons failed. Directory will have wrong label."); - return; + userId = getUserOrWorkProfileId(clientPackage, userId); + if (userId != mCurrentUserId) { + final File systemDir = Environment.getUserSystemDirectory(userId); + final File fpDir = new File(systemDir, FP_DATA_DIR); + if (!fpDir.exists()) { + if (!fpDir.mkdir()) { + Slog.v(TAG, "Cannot make directory: " + fpDir.getAbsolutePath()); + return; + } + // Calling mkdir() from this process will create a directory with our + // permissions (inherited from the containing dir). This command fixes + // the label. + if (!SELinux.restorecon(fpDir)) { + Slog.w(TAG, "Restorecons failed. Directory will have wrong label."); + return; + } } + daemon.setActiveGroup(userId, fpDir.getAbsolutePath().getBytes()); + mCurrentUserId = userId; } - daemon.setActiveGroup(userId, fpDir.getAbsolutePath().getBytes()); } catch (RemoteException e) { Slog.e(TAG, "Failed to setActiveGroup():", e); } } } + /** + * @param clientPackage the package of the caller + * @return the profile id + */ + private int getUserOrWorkProfileId(String clientPackage, int userId) { + if (!isKeyguard(clientPackage) && isWorkProfile(userId)) { + return userId; + } + return getEffectiveUserId(userId); + } + + /** + * @param userId + * @return true if this is a work profile + */ + private boolean isWorkProfile(int userId) { + UserInfo info = mUserManager.getUserInfo(userId); + return info != null && info.isManagedProfile(); + } + private void listenForUserSwitches() { try { ActivityManagerNative.getDefault().registerUserSwitchObserver( diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 2ee74db8c47d..7f783ecfc058 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1242,58 +1242,55 @@ public class NotificationManagerService extends SystemService { } @Override - public void setTopicPriority(String pkg, int uid, Notification.Topic topic, int priority) { + public void setPriority(String pkg, int uid, Notification.Topic topic, int priority) { checkCallerIsSystem(); - mRankingHelper.setTopicPriority(pkg, uid, topic, priority); + mRankingHelper.setPriority(pkg, uid, topic, priority); savePolicyFile(); } @Override - public int getTopicPriority(String pkg, int uid, Notification.Topic topic) { + public int getPriority(String pkg, int uid, Notification.Topic topic) { checkCallerIsSystem(); - return mRankingHelper.getTopicPriority(pkg, uid, topic); + return mRankingHelper.getPriority(pkg, uid, topic); } @Override - public void setTopicVisibilityOverride(String pkg, int uid, Notification.Topic topic, + public void setVisibilityOverride(String pkg, int uid, Notification.Topic topic, int visibility) { checkCallerIsSystem(); - mRankingHelper.setTopicVisibilityOverride(pkg, uid, topic, visibility); + mRankingHelper.setVisibilityOverride(pkg, uid, topic, visibility); savePolicyFile(); } @Override - public int getTopicVisibilityOverride(String pkg, int uid, Notification.Topic topic) { + public int getVisibilityOverride(String pkg, int uid, Notification.Topic topic) { checkCallerIsSystem(); - return mRankingHelper.getTopicVisibilityOverride(pkg, uid, topic); + return mRankingHelper.getVisibilityOverride(pkg, uid, topic); } @Override - public void setTopicImportance(String pkg, int uid, Notification.Topic topic, + public void setImportance(String pkg, int uid, Notification.Topic topic, int importance) { enforceSystemOrSystemUI("Caller not system or systemui"); - if (NotificationListenerService.Ranking.IMPORTANCE_NONE == importance) { - cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, - UserHandle.getUserId(uid), - REASON_TOPIC_BANNED, topic, null); + if (topic == null) { + // App wide, potentially store block in app ops. + setNotificationsEnabledForPackageImpl(pkg, uid, + importance != NotificationListenerService.Ranking.IMPORTANCE_NONE); + } else { + if (NotificationListenerService.Ranking.IMPORTANCE_NONE == importance) { + cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, + UserHandle.getUserId(uid), + REASON_TOPIC_BANNED, topic, null); + } } - mRankingHelper.setTopicImportance(pkg, uid, topic, importance); + mRankingHelper.setImportance(pkg, uid, topic, importance); savePolicyFile(); } @Override - public int getTopicImportance(String pkg, int uid, Notification.Topic topic) { + public int getImportance(String pkg, int uid, Notification.Topic topic) { checkCallerIsSystem(); - return mRankingHelper.getTopicImportance(pkg, uid, topic); - } - - @Override - public void setAppImportance(String pkg, int uid, int importance) { - enforceSystemOrSystemUI("Caller not system or systemui"); - setNotificationsEnabledForPackageImpl(pkg, uid, - importance != NotificationListenerService.Ranking.IMPORTANCE_NONE); - mRankingHelper.setAppImportance(pkg, uid, importance); - savePolicyFile(); + return mRankingHelper.getImportance(pkg, uid, topic); } @Override diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index 490e890f93b2..484b0e95b359 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -33,6 +33,7 @@ import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.MetricsLogger; import com.android.server.EventLogTags; import java.io.PrintWriter; @@ -106,6 +107,7 @@ public final class NotificationRecord { mCreationTimeMs = sbn.getPostTime(); mUpdateTimeMs = mCreationTimeMs; mContext = context; + stats = new NotificationUsageStats.SingleNotificationStats(); mImportance = defaultImportance(); } @@ -133,27 +135,22 @@ public final class NotificationRecord { importance = IMPORTANCE_MAX; break; } + stats.requestedImportance = importance; boolean isNoisy = (n.defaults & Notification.DEFAULT_SOUND) != 0 || (n.defaults & Notification.DEFAULT_VIBRATE) != 0 || n.sound != null || n.vibrate != null; + stats.isNoisy = isNoisy; if (!isNoisy && importance > IMPORTANCE_DEFAULT) { importance = IMPORTANCE_DEFAULT; } - // maybe only do this for target API < N? - if (isNoisy) { - if (importance >= IMPORTANCE_HIGH) { - importance = IMPORTANCE_MAX; - } else { - importance = IMPORTANCE_HIGH; - } - } if (n.fullScreenIntent != null) { importance = IMPORTANCE_MAX; } + stats.naturalImportance = importance; return importance; } diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java index 1cdc6db0890b..e75324faeb5f 100644 --- a/services/core/java/com/android/server/notification/NotificationUsageStats.java +++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java @@ -16,6 +16,8 @@ package com.android.server.notification; +import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH; + import android.app.Notification; import android.content.ContentValues; import android.content.Context; @@ -101,7 +103,6 @@ public class NotificationUsageStats { * Called when a notification has been posted. */ public synchronized void registerPostedByApp(NotificationRecord notification) { - notification.stats = new SingleNotificationStats(); notification.stats.posttimeElapsedMs = SystemClock.elapsedRealtime(); AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification); @@ -119,13 +120,16 @@ public class NotificationUsageStats { * Called when a notification has been updated. */ public void registerUpdatedByApp(NotificationRecord notification, NotificationRecord old) { - notification.stats = old.stats; + notification.stats.updateFrom(old.stats); AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification); for (AggregatedStats stats : aggregatedStatsArray) { stats.numUpdatedByApp++; stats.countApiUse(notification); } releaseAggregatedStatsLocked(aggregatedStatsArray); + if (ENABLE_SQLITE_LOG) { + mSQLiteLog.logPosted(notification); + } } /** @@ -297,10 +301,6 @@ public class NotificationUsageStats { 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; @@ -314,11 +314,17 @@ public class NotificationUsageStats { public int numWithSubText; public int numWithInfoText; public int numInterrupt; + public ImportanceHistogram noisyImportance; + public ImportanceHistogram quietImportance; + public ImportanceHistogram finalImportance; public AggregatedStats(Context context, String key) { this.key = key; mContext = context; mCreated = SystemClock.elapsedRealtime(); + noisyImportance = new ImportanceHistogram(context, "note_imp_noisy_"); + quietImportance = new ImportanceHistogram(context, "note_imp_quiet_"); + finalImportance = new ImportanceHistogram(context, "note_importance_"); } public void countApiUse(NotificationRecord record) { @@ -354,20 +360,12 @@ public class NotificationUsageStats { 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; + if (record.stats.isNoisy) { + noisyImportance.increment(record.stats.requestedImportance); + } else { + quietImportance.increment(record.stats.requestedImportance); } + finalImportance.increment(record.getImportance()); final Set<String> names = n.extras.keySet(); if (names.contains(Notification.EXTRA_BIG_TEXT)) { @@ -419,10 +417,6 @@ public class NotificationUsageStats { 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)); @@ -436,6 +430,9 @@ public class NotificationUsageStats { maybeCount("note_text", (numWithText - mPrevious.numWithText)); maybeCount("note_sub_text", (numWithSubText - mPrevious.numWithSubText)); maybeCount("note_info_text", (numWithInfoText - mPrevious.numWithInfoText)); + noisyImportance.maybeCount(mPrevious.noisyImportance); + quietImportance.maybeCount(mPrevious.quietImportance); + finalImportance.maybeCount(mPrevious.finalImportance); mPrevious.numPostedByApp = numPostedByApp; mPrevious.numUpdatedByApp = numUpdatedByApp; @@ -448,10 +445,6 @@ public class NotificationUsageStats { 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; @@ -465,6 +458,9 @@ public class NotificationUsageStats { mPrevious.numWithText = numWithText; mPrevious.numWithSubText = numWithSubText; mPrevious.numWithInfoText = numWithInfoText; + noisyImportance.update(mPrevious.noisyImportance); + quietImportance.update(mPrevious.quietImportance); + finalImportance.update(mPrevious.finalImportance); } void maybeCount(String name, int value) { @@ -483,17 +479,64 @@ public class NotificationUsageStats { } private String toStringWithIndent(String indent) { - return indent + "AggregatedStats{\n" + - indent + " key='" + key + "',\n" + - indent + " numPostedByApp=" + numPostedByApp + ",\n" + - indent + " numUpdatedByApp=" + numUpdatedByApp + ",\n" + - indent + " numRemovedByApp=" + numRemovedByApp + ",\n" + - indent + " numPeopleCacheHit=" + numPeopleCacheHit + ",\n" + - indent + " numWithStaredPeople=" + numWithStaredPeople + ",\n" + - indent + " numWithValidPeople=" + numWithValidPeople + ",\n" + - indent + " numPeopleCacheMiss=" + numPeopleCacheMiss + ",\n" + - indent + " numBlocked=" + numBlocked + ",\n" + - indent + "}"; + StringBuilder output = new StringBuilder(); + output.append(indent).append("AggregatedStats{\n"); + String indentPlusTwo = indent + " "; + output.append(indentPlusTwo); + output.append("key='").append(key).append("',\n"); + output.append(indentPlusTwo); + output.append("numPostedByApp=").append(numPostedByApp).append(",\n"); + output.append(indentPlusTwo); + output.append("numUpdatedByApp=").append(numUpdatedByApp).append(",\n"); + output.append(indentPlusTwo); + output.append("numRemovedByApp=").append(numRemovedByApp).append(",\n"); + output.append(indentPlusTwo); + output.append("numPeopleCacheHit=").append(numPeopleCacheHit).append(",\n"); + output.append(indentPlusTwo); + output.append("numWithStaredPeople=").append(numWithStaredPeople).append(",\n"); + output.append(indentPlusTwo); + output.append("numWithValidPeople=").append(numWithValidPeople).append(",\n"); + output.append(indentPlusTwo); + output.append("numPeopleCacheMiss=").append(numPeopleCacheMiss).append(",\n"); + output.append(indentPlusTwo); + output.append("numBlocked=").append(numBlocked).append(",\n"); + output.append(indentPlusTwo); + output.append("numWithActions=").append(numWithActions).append(",\n"); + output.append(indentPlusTwo); + output.append("numPrivate=").append(numPrivate).append(",\n"); + output.append(indentPlusTwo); + output.append("numSecret=").append(numSecret).append(",\n"); + output.append(indentPlusTwo); + output.append("numInterrupt=").append(numInterrupt).append(",\n"); + output.append(indentPlusTwo); + output.append("numWithBigText=").append(numWithBigText).append(",\n"); + output.append(indentPlusTwo); + output.append("numWithBigPicture=").append(numWithBigPicture).append("\n"); + output.append(indentPlusTwo); + output.append("numForegroundService=").append(numForegroundService).append("\n"); + output.append(indentPlusTwo); + output.append("numOngoing=").append(numOngoing).append("\n"); + output.append(indentPlusTwo); + output.append("numAutoCancel=").append(numAutoCancel).append("\n"); + output.append(indentPlusTwo); + output.append("numWithLargeIcon=").append(numWithLargeIcon).append("\n"); + output.append(indentPlusTwo); + output.append("numWithInbox=").append(numWithInbox).append("\n"); + output.append(indentPlusTwo); + output.append("numWithMediaSession=").append(numWithMediaSession).append("\n"); + output.append(indentPlusTwo); + output.append("numWithTitle=").append(numWithTitle).append("\n"); + output.append(indentPlusTwo); + output.append("numWithText=").append(numWithText).append("\n"); + output.append(indentPlusTwo); + output.append("numWithSubText=").append(numWithSubText).append("\n"); + output.append(indentPlusTwo); + output.append("numWithInfoText=").append(numWithInfoText).append("\n"); + output.append(indentPlusTwo).append(noisyImportance.toString()).append("\n"); + output.append(indentPlusTwo).append(quietImportance.toString()).append("\n"); + output.append(indentPlusTwo).append(finalImportance.toString()).append("\n"); + output.append(indent).append("}"); + return output.toString(); } public JSONObject dumpJson() throws JSONException { @@ -511,10 +554,6 @@ public class NotificationUsageStats { maybePut(dump, "numWithActions", numWithActions); maybePut(dump, "numPrivate", numPrivate); maybePut(dump, "numSecret", numSecret); - maybePut(dump, "numPriorityMax", numPriorityMax); - maybePut(dump, "numPriorityHigh", numPriorityHigh); - maybePut(dump, "numPriorityLow", numPriorityLow); - maybePut(dump, "numPriorityMin", numPriorityMin); maybePut(dump, "numInterrupt", numInterrupt); maybePut(dump, "numWithBigText", numWithBigText); maybePut(dump, "numWithBigPicture", numWithBigPicture); @@ -528,6 +567,10 @@ public class NotificationUsageStats { maybePut(dump, "numWithText", numWithText); maybePut(dump, "numWithSubText", numWithSubText); maybePut(dump, "numWithInfoText", numWithInfoText); + noisyImportance.maybePut(dump, mPrevious.noisyImportance); + quietImportance.maybePut(dump, mPrevious.quietImportance); + finalImportance.maybePut(dump, mPrevious.finalImportance); + return dump; } @@ -538,6 +581,65 @@ public class NotificationUsageStats { } } + private static class ImportanceHistogram { + // TODO define these somewhere else + private static final int NUM_IMPORTANCES = 5; + private static final String[] IMPORTANCE_NAMES = {"none", "low", "default", "high", "max"}; + private final Context mContext; + private final String[] mCounterNames; + private final String mPrefix; + private int[] mCount; + + ImportanceHistogram(Context context, String prefix) { + mContext = context; + mCount = new int[NUM_IMPORTANCES]; + mCounterNames = new String[NUM_IMPORTANCES]; + mPrefix = prefix; + for (int i = 0; i < NUM_IMPORTANCES; i++) { + mCounterNames[i] = mPrefix + IMPORTANCE_NAMES[i]; + } + } + + void increment(int imp) { + imp = imp < 0 ? 0 : imp > NUM_IMPORTANCES ? NUM_IMPORTANCES : imp; + mCount[imp] ++; + } + + void maybeCount(ImportanceHistogram prev) { + for (int i = 0; i < NUM_IMPORTANCES; i++) { + final int value = mCount[i] - prev.mCount[i]; + if (value > 0) { + MetricsLogger.count(mContext, mCounterNames[i], value); + } + } + } + + void update(ImportanceHistogram that) { + for (int i = 0; i < NUM_IMPORTANCES; i++) { + mCount[i] = that.mCount[i]; + } + } + + public void maybePut(JSONObject dump, ImportanceHistogram prev) + throws JSONException { + dump.put(mPrefix, new JSONArray(mCount)); + } + + @Override + public String toString() { + StringBuilder output = new StringBuilder(); + output.append(mPrefix).append(": ["); + for (int i = 0; i < NUM_IMPORTANCES; i++) { + output.append(mCount[i]); + if (i < (NUM_IMPORTANCES-1)) { + output.append(", "); + } + } + output.append("]"); + return output.toString(); + } + } + /** * Tracks usage of an individual notification that is currently active. */ @@ -575,6 +677,12 @@ public class NotificationUsageStats { public long airtimeExpandedMs = 0; /** Number of times the notification has been expanded by the user. */ public long userExpansionCount = 0; + /** Importance directly requested by the app. */ + public int requestedImportance; + /** Did the app include sound or vibration on the notificaiton. */ + public boolean isNoisy; + /** Importance after initial filtering for noise and other features */ + public int naturalImportance; public long getCurrentPosttimeMs() { if (posttimeElapsedMs < 0) { @@ -686,17 +794,40 @@ public class NotificationUsageStats { @Override public String toString() { - return "SingleNotificationStats{" + - "posttimeElapsedMs=" + posttimeElapsedMs + - ", posttimeToFirstClickMs=" + posttimeToFirstClickMs + - ", posttimeToDismissMs=" + posttimeToDismissMs + - ", airtimeCount=" + airtimeCount + - ", airtimeMs=" + airtimeMs + - ", currentAirtimeStartElapsedMs=" + currentAirtimeStartElapsedMs + - ", airtimeExpandedMs=" + airtimeExpandedMs + - ", posttimeToFirstVisibleExpansionMs=" + posttimeToFirstVisibleExpansionMs + - ", currentAirtimeExpandedSEMs=" + currentAirtimeExpandedStartElapsedMs + - '}'; + StringBuilder output = new StringBuilder(); + output.append("SingleNotificationStats{"); + + output.append("posttimeElapsedMs=").append(posttimeElapsedMs).append(", "); + output.append("posttimeToFirstClickMs=").append(posttimeToFirstClickMs).append(", "); + output.append("posttimeToDismissMs=").append(posttimeToDismissMs).append(", "); + output.append("airtimeCount=").append(airtimeCount).append(", "); + output.append("airtimeMs=").append(airtimeMs).append(", "); + output.append("currentAirtimeStartElapsedMs=").append(currentAirtimeStartElapsedMs) + .append(", "); + output.append("airtimeExpandedMs=").append(airtimeExpandedMs).append(", "); + output.append("posttimeToFirstVisibleExpansionMs=") + .append(posttimeToFirstVisibleExpansionMs).append(", "); + output.append("currentAirtimeExpandedStartElapsedMs=") + .append(currentAirtimeExpandedStartElapsedMs).append(", "); + output.append("requestedImportance=").append(requestedImportance).append(", "); + output.append("naturalImportance=").append(naturalImportance).append(", "); + output.append("isNoisy=").append(isNoisy); + output.append('}'); + return output.toString(); + } + + /** Copy useful information out of the stats from the pre-update notifications. */ + public void updateFrom(SingleNotificationStats old) { + posttimeElapsedMs = old.posttimeElapsedMs; + posttimeToFirstClickMs = old.posttimeToFirstClickMs; + airtimeCount = old.airtimeCount; + posttimeToFirstAirtimeMs = old.posttimeToFirstAirtimeMs; + currentAirtimeStartElapsedMs = old.currentAirtimeStartElapsedMs; + airtimeMs = old.airtimeMs; + posttimeToFirstVisibleExpansionMs = old.posttimeToFirstVisibleExpansionMs; + currentAirtimeExpandedStartElapsedMs = old.currentAirtimeExpandedStartElapsedMs; + airtimeExpandedMs = old.airtimeExpandedMs; + userExpansionCount = old.userExpansionCount; } } @@ -741,7 +872,7 @@ public class NotificationUsageStats { private static final int MSG_DISMISS = 4; private static final String DB_NAME = "notification_log.db"; - private static final int DB_VERSION = 4; + private static final int DB_VERSION = 5; /** Age in ms after which events are pruned from the DB. */ private static final long HORIZON_MS = 7 * 24 * 60 * 60 * 1000L; // 1 week @@ -762,7 +893,11 @@ public class NotificationUsageStats { private static final String COL_WHEN_MS = "when_ms"; private static final String COL_DEFAULTS = "defaults"; private static final String COL_FLAGS = "flags"; - private static final String COL_PRIORITY = "priority"; + private static final String COL_IMPORTANCE_REQ = "importance_request"; + private static final String COL_IMPORTANCE_FINAL = "importance_final"; + private static final String COL_NOISY = "noisy"; + private static final String COL_MUTED = "muted"; + private static final String COL_DEMOTED = "demoted"; private static final String COL_CATEGORY = "category"; private static final String COL_ACTION_COUNT = "action_count"; private static final String COL_POSTTIME_MS = "posttime_ms"; @@ -776,14 +911,28 @@ public class NotificationUsageStats { private static final int EVENT_TYPE_CLICK = 2; private static final int EVENT_TYPE_REMOVE = 3; private static final int EVENT_TYPE_DISMISS = 4; - private static long sLastPruneMs; - private static long sNumWrites; + private static long sNumWrites; private final SQLiteOpenHelper mHelper; - private final Handler mWriteHandler; + private final Handler mWriteHandler; private static final long DAY_MS = 24 * 60 * 60 * 1000; + private static final String STATS_QUERY = "SELECT " + + COL_EVENT_USER_ID + ", " + + COL_PKG + ", " + + // Bucket by day by looking at 'floor((midnight - eventTimeMs) / dayMs)' + "CAST(((%d - " + COL_EVENT_TIME + ") / " + DAY_MS + ") AS int) " + + "AS day, " + + "COUNT(*) AS cnt, " + + "SUM(" + COL_MUTED + ") as muted, " + + "SUM(" + COL_NOISY + ") as noisy, " + + "SUM(" + COL_DEMOTED + ") as demoted " + + "FROM " + TAB_LOG + " " + + "WHERE " + + COL_EVENT_TYPE + "=" + EVENT_TYPE_POST + + " AND " + COL_EVENT_TIME + " > %d " + + " GROUP BY " + COL_EVENT_USER_ID + ", day, " + COL_PKG; public SQLiteLog(Context context) { HandlerThread backgroundThread = new HandlerThread("notification-sqlite-log", @@ -828,7 +977,11 @@ public class NotificationUsageStats { COL_WHEN_MS + " INT," + COL_DEFAULTS + " INT," + COL_FLAGS + " INT," + - COL_PRIORITY + " INT," + + COL_IMPORTANCE_REQ + " INT," + + COL_IMPORTANCE_FINAL + " INT," + + COL_NOISY + " INT," + + COL_MUTED + " INT," + + COL_DEMOTED + " INT," + COL_CATEGORY + " TEXT," + COL_ACTION_COUNT + " INT," + COL_POSTTIME_MS + " INT," + @@ -841,8 +994,7 @@ public class NotificationUsageStats { @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - if (oldVersion <= 3) { - // Version 3 creation left 'log' in a weird state. Just reset for now. + if (oldVersion != newVersion) { db.execSQL("DROP TABLE IF EXISTS " + TAB_LOG); onCreate(db); } @@ -866,22 +1018,11 @@ public class NotificationUsageStats { mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_DISMISS, notification)); } - private JSONArray JsonPostFrequencies(DumpFilter filter) throws JSONException { + private JSONArray jsonPostFrequencies(DumpFilter filter) throws JSONException { JSONArray frequencies = new JSONArray(); SQLiteDatabase db = mHelper.getReadableDatabase(); long midnight = getMidnightMs(); - String q = "SELECT " + - COL_EVENT_USER_ID + ", " + - COL_PKG + ", " + - // Bucket by day by looking at 'floor((midnight - eventTimeMs) / dayMs)' - "CAST(((" + midnight + " - " + COL_EVENT_TIME + ") / " + DAY_MS + ") AS int) " + - "AS day, " + - "COUNT(*) AS cnt " + - "FROM " + TAB_LOG + " " + - "WHERE " + - COL_EVENT_TYPE + "=" + EVENT_TYPE_POST + - " AND " + COL_EVENT_TIME + " > " + filter.since + - " GROUP BY " + COL_EVENT_USER_ID + ", day, " + COL_PKG; + String q = String.format(STATS_QUERY, midnight, filter.since); Cursor cursor = db.rawQuery(q, null); try { for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { @@ -890,11 +1031,17 @@ public class NotificationUsageStats { if (filter != null && !filter.matches(pkg)) continue; int day = cursor.getInt(2); int count = cursor.getInt(3); + int muted = cursor.getInt(4); + int noisy = cursor.getInt(5); + int demoted = cursor.getInt(6); JSONObject row = new JSONObject(); row.put("user_id", userId); row.put("package", pkg); row.put("day", day); row.put("count", count); + row.put("noisy", noisy); + row.put("muted", muted); + row.put("demoted", demoted); frequencies.put(row); } } finally { @@ -906,17 +1053,7 @@ public class NotificationUsageStats { public void printPostFrequencies(PrintWriter pw, String indent, DumpFilter filter) { SQLiteDatabase db = mHelper.getReadableDatabase(); long midnight = getMidnightMs(); - String q = "SELECT " + - COL_EVENT_USER_ID + ", " + - COL_PKG + ", " + - // Bucket by day by looking at 'floor((midnight - eventTimeMs) / dayMs)' - "CAST(((" + midnight + " - " + COL_EVENT_TIME + ") / " + DAY_MS + ") AS int) " + - "AS day, " + - "COUNT(*) AS cnt " + - "FROM " + TAB_LOG + " " + - "WHERE " + - COL_EVENT_TYPE + "=" + EVENT_TYPE_POST + " " + - "GROUP BY " + COL_EVENT_USER_ID + ", day, " + COL_PKG; + String q = String.format(STATS_QUERY, midnight, filter.since); Cursor cursor = db.rawQuery(q, null); try { for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { @@ -925,8 +1062,12 @@ public class NotificationUsageStats { if (filter != null && !filter.matches(pkg)) continue; int day = cursor.getInt(2); int count = cursor.getInt(3); + int muted = cursor.getInt(4); + int noisy = cursor.getInt(5); + int demoted = cursor.getInt(6); pw.println(indent + "post_frequency{user_id=" + userId + ",pkg=" + pkg + - ",day=" + day + ",count=" + count + "}"); + ",day=" + day + ",count=" + count + ",muted=" + muted + "/" + noisy + + ",demoted=" + demoted + "}"); } } finally { cursor.close(); @@ -985,7 +1126,18 @@ public class NotificationUsageStats { } outCv.put(COL_WHEN_MS, r.sbn.getPostTime()); outCv.put(COL_FLAGS, r.getNotification().flags); - outCv.put(COL_PRIORITY, r.getNotification().priority); + final int before = r.stats.requestedImportance; + final int after = r.getImportance(); + final boolean noisy = r.stats.isNoisy; + outCv.put(COL_IMPORTANCE_REQ, before); + outCv.put(COL_IMPORTANCE_FINAL, after); + outCv.put(COL_DEMOTED, after < before ? 1 : 0); + outCv.put(COL_NOISY, noisy); + if (noisy && after < IMPORTANCE_HIGH) { + outCv.put(COL_MUTED, 1); + } else { + outCv.put(COL_MUTED, 0); + } if (r.getNotification().category != null) { outCv.put(COL_CATEGORY, r.getNotification().category); } @@ -1008,7 +1160,9 @@ public class NotificationUsageStats { public JSONObject dumpJson(DumpFilter filter) { JSONObject dump = new JSONObject(); try { - dump.put("post_frequency", JsonPostFrequencies(filter)); + dump.put("post_frequency", jsonPostFrequencies(filter)); + dump.put("since", filter.since); + dump.put("now", System.currentTimeMillis()); } catch (JSONException e) { // pass } diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java index 9b10ef2f0fa3..7f85e1f8123d 100644 --- a/services/core/java/com/android/server/notification/RankingConfig.java +++ b/services/core/java/com/android/server/notification/RankingConfig.java @@ -23,20 +23,18 @@ public interface RankingConfig { List<Notification.Topic> getTopics(String packageName, int uid); - int getTopicPriority(String packageName, int uid, Notification.Topic topic); + int getPriority(String packageName, int uid, Notification.Topic topic); - void setTopicPriority(String packageName, int uid, Notification.Topic topic, int priority); + void setPriority(String packageName, int uid, Notification.Topic topic, int priority); - int getTopicVisibilityOverride(String packageName, int uid, Notification.Topic topic); + int getVisibilityOverride(String packageName, int uid, Notification.Topic topic); - void setTopicVisibilityOverride(String packageName, int uid, Notification.Topic topic, + void setVisibilityOverride(String packageName, int uid, Notification.Topic topic, int visibility); - void setTopicImportance(String packageName, int uid, Notification.Topic topic, int importance); + void setImportance(String packageName, int uid, Notification.Topic topic, int importance); - int getTopicImportance(String packageName, int uid, Notification.Topic topic); - - void setAppImportance(String packageName, int uid, int importance); + int getImportance(String packageName, int uid, Notification.Topic topic); boolean doesAppUseTopics(String packageName, int uid); } diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java index ce4ecd39cd58..827482ff2a4b 100644 --- a/services/core/java/com/android/server/notification/RankingHelper.java +++ b/services/core/java/com/android/server/notification/RankingHelper.java @@ -164,6 +164,8 @@ public class RankingHelper implements RankingConfig { r = getOrCreateRecord(name, uid); } r.importance = safeInt(parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE); + r.priority = priority; + r.visibility = vis; // Migrate package level settings to the default topic. // Might be overwritten by parseTopics. @@ -245,7 +247,15 @@ public class RankingHelper implements RankingConfig { } out.startTag(null, TAG_PACKAGE); out.attribute(null, ATT_NAME, r.pkg); - out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance)); + if (r.importance != DEFAULT_IMPORTANCE) { + out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance)); + } + if (r.priority != DEFAULT_PRIORITY) { + out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority)); + } + if (r.visibility != DEFAULT_VISIBILITY) { + out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility)); + } if (!forBackup) { out.attribute(null, ATT_UID, Integer.toString(r.uid)); @@ -373,66 +383,109 @@ public class RankingHelper implements RankingConfig { public List<Notification.Topic> getTopics(String packageName, int uid) { final Record r = getOrCreateRecord(packageName, uid); List<Notification.Topic> topics = new ArrayList<>(); - for (Topic t : r.topics.values()) { + for (Topic t : r.topics.values()) { topics.add(t.topic); } return topics; } + /** + * Gets priority. If a topic is given, returns the priority of that topic. Otherwise, the + * priority of the app. + */ @Override - public int getTopicPriority(String packageName, int uid, Notification.Topic topic) { + public int getPriority(String packageName, int uid, Notification.Topic topic) { final Record r = getOrCreateRecord(packageName, uid); + if (topic == null) { + return r.priority; + } return getOrCreateTopic(r, topic).priority; } + /** + * Sets priority. If a topic is given, sets the priority of that topic. If not, + * sets the default priority for all new topics that appear in the future, and resets + * the priority of all current topics. + */ @Override - public void setTopicPriority(String packageName, int uid, Notification.Topic topic, + public void setPriority(String packageName, int uid, Notification.Topic topic, int priority) { final Record r = getOrCreateRecord(packageName, uid); - getOrCreateTopic(r, topic).priority = priority; + if (topic == null) { + r.priority = priority; + for (Topic t : r.topics.values()) { + t.priority = priority; + } + } else { + getOrCreateTopic(r, topic).priority = priority; + } updateConfig(); } + /** + * Gets visual override. If a topic is given, returns the override of that topic. Otherwise, the + * override of the app. + */ @Override - public int getTopicVisibilityOverride(String packageName, int uid, Notification.Topic topic) { + public int getVisibilityOverride(String packageName, int uid, Notification.Topic topic) { final Record r = getOrCreateRecord(packageName, uid); + if (topic == null) { + return r.visibility; + } return getOrCreateTopic(r, topic).visibility; } + /** + * Sets visibility override. If a topic is given, sets the override of that topic. If not, + * sets the default override for all new topics that appear in the future, and resets + * the override of all current topics. + */ @Override - public void setTopicVisibilityOverride(String pkgName, int uid, Notification.Topic topic, + public void setVisibilityOverride(String pkgName, int uid, Notification.Topic topic, int visibility) { final Record r = getOrCreateRecord(pkgName, uid); - getOrCreateTopic(r, topic).visibility = visibility; + if (topic == null) { + r.visibility = visibility; + for (Topic t : r.topics.values()) { + t.visibility = visibility; + } + } else { + getOrCreateTopic(r, topic).visibility = visibility; + } updateConfig(); } + /** + * Gets importance. If a topic is given, returns the importance of that topic. Otherwise, the + * importance of the app. + */ @Override - public int getTopicImportance(String packageName, int uid, Notification.Topic topic) { + public int getImportance(String packageName, int uid, Notification.Topic topic) { final Record r = getOrCreateRecord(packageName, uid); + if (topic == null) { + return r.importance; + } return getOrCreateTopic(r, topic).importance; } - @Override - public void setTopicImportance(String pkgName, int uid, Notification.Topic topic, - int importance) { - final Record r = getOrCreateRecord(pkgName, uid); - getOrCreateTopic(r, topic).importance = importance; - updateConfig(); - } - /** - * Sets the default importance for all new topics that appear in the future, and resets + * Sets importance. If a topic is given, sets the importance of that topic. If not, sets the + * default importance for all new topics that appear in the future, and resets * the importance of all current topics (unless the app is being blocked). */ @Override - public void setAppImportance(String pkgName, int uid, int importance) { + public void setImportance(String pkgName, int uid, Notification.Topic topic, + int importance) { final Record r = getOrCreateRecord(pkgName, uid); - r.importance = importance; - if (Ranking.IMPORTANCE_NONE != importance) { - for (Topic t : r.topics.values()) { - t.importance = importance; + if (topic == null) { + r.importance = importance; + if (Ranking.IMPORTANCE_NONE != importance) { + for (Topic t : r.topics.values()) { + t.importance = importance; + } } + } else { + getOrCreateTopic(r, topic).importance = importance; } updateConfig(); } @@ -459,6 +512,8 @@ public class RankingHelper implements RankingConfig { } else { t = new Topic(topic); t.importance = r.importance; + t.priority = r.priority; + t.visibility = r.visibility; r.topics.put(topic.getId(), t); return t; } @@ -503,8 +558,18 @@ public class RankingHelper implements RankingConfig { pw.print(" ("); pw.print(r.uid == Record.UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid)); pw.print(')'); - pw.print(" importance="); - pw.print(Ranking.importanceToString(r.importance)); + if (r.importance != DEFAULT_IMPORTANCE) { + pw.print(" importance="); + pw.print(Ranking.importanceToString(r.importance)); + } + if (r.priority != DEFAULT_PRIORITY) { + pw.print(" priority="); + pw.print(Ranking.importanceToString(r.priority)); + } + if (r.visibility != DEFAULT_VISIBILITY) { + pw.print(" visibility="); + pw.print(Ranking.importanceToString(r.visibility)); + } pw.println(); for (Topic t : r.topics.values()) { pw.print(prefix); @@ -561,6 +626,8 @@ public class RankingHelper implements RankingConfig { String pkg; int uid = UNKNOWN_UID; int importance = DEFAULT_IMPORTANCE; + int priority = DEFAULT_PRIORITY; + int visibility = DEFAULT_VISIBILITY; Map<String, Topic> topics = new ArrayMap<>(); } diff --git a/services/core/java/com/android/server/notification/TopicImportanceExtractor.java b/services/core/java/com/android/server/notification/TopicImportanceExtractor.java index 01770d0907b1..c6b3e0fdb0d6 100644 --- a/services/core/java/com/android/server/notification/TopicImportanceExtractor.java +++ b/services/core/java/com/android/server/notification/TopicImportanceExtractor.java @@ -42,7 +42,7 @@ public class TopicImportanceExtractor implements NotificationSignalExtractor { return null; } - final int topicImportance = mConfig.getTopicImportance(record.sbn.getPackageName(), + final int topicImportance = mConfig.getImportance(record.sbn.getPackageName(), record.sbn.getUid(), record.sbn.getNotification().getTopic()); record.setTopicImportance(topicImportance); diff --git a/services/core/java/com/android/server/notification/TopicPriorityExtractor.java b/services/core/java/com/android/server/notification/TopicPriorityExtractor.java index 5bf989ae0cdd..1df5c2b5fb2a 100644 --- a/services/core/java/com/android/server/notification/TopicPriorityExtractor.java +++ b/services/core/java/com/android/server/notification/TopicPriorityExtractor.java @@ -42,7 +42,7 @@ public class TopicPriorityExtractor implements NotificationSignalExtractor { return null; } - final int packagePriority = mConfig.getTopicPriority(record.sbn.getPackageName(), + final int packagePriority = mConfig.getPriority(record.sbn.getPackageName(), record.sbn.getUid(), record.sbn.getNotification().getTopic()); record.setPackagePriority(packagePriority); diff --git a/services/core/java/com/android/server/notification/TopicVisibilityExtractor.java b/services/core/java/com/android/server/notification/TopicVisibilityExtractor.java index e05338277b1c..eaa3ed3623e1 100644 --- a/services/core/java/com/android/server/notification/TopicVisibilityExtractor.java +++ b/services/core/java/com/android/server/notification/TopicVisibilityExtractor.java @@ -42,7 +42,7 @@ public class TopicVisibilityExtractor implements NotificationSignalExtractor { return null; } - final int packageVisibility = mConfig.getTopicVisibilityOverride( + final int packageVisibility = mConfig.getVisibilityOverride( record.sbn.getPackageName(), record.sbn.getUid(), record.sbn.getNotification().getTopic()); record.setPackageVisibilityOverride(packageVisibility); diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 3452f41eb587..b54e86660a47 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -125,6 +125,8 @@ public class TrustManagerService extends SystemService { @GuardedBy("mDeviceLockedForUser") private final SparseBooleanArray mTrustUsuallyManagedForUser = new SparseBooleanArray(); + private final StrongAuthTracker mStrongAuthTracker; + private boolean mTrustAgentsCanRun = false; private int mCurrentUser = UserHandle.USER_SYSTEM; @@ -134,6 +136,13 @@ public class TrustManagerService extends SystemService { mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); mLockPatternUtils = new LockPatternUtils(context); + + mStrongAuthTracker = new StrongAuthTracker(context) { + @Override + public void onStrongAuthRequiredChanged(int userId) { + refreshAgentList(userId); + } + }; } @Override @@ -920,13 +929,6 @@ public class TrustManagerService extends SystemService { } }; - private final StrongAuthTracker mStrongAuthTracker = new StrongAuthTracker() { - @Override - public void onStrongAuthRequiredChanged(int userId) { - refreshAgentList(userId); - } - }; - private class Receiver extends BroadcastReceiver { @Override diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 3ca99d400820..62e7fb49e082 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -314,7 +314,8 @@ public final class TvInputManagerService extends SystemService { } } else { try { - inputList.add(TvInputInfo.createTvInputInfo(mContext, ri)); + TvInputInfo info = new TvInputInfo.Builder(mContext, ri).build(); + inputList.add(info); } catch (XmlPullParserException | IOException e) { Slog.e(TAG, "failed to load TV input " + si.name, e); continue; diff --git a/services/core/java/com/android/server/wm/DropPermissionsHandler.java b/services/core/java/com/android/server/wm/DropPermissionsHandler.java index 68cfaab02cdb..5889fb8eb53e 100644 --- a/services/core/java/com/android/server/wm/DropPermissionsHandler.java +++ b/services/core/java/com/android/server/wm/DropPermissionsHandler.java @@ -27,7 +27,7 @@ import com.android.internal.view.IDropPermissions; import java.util.ArrayList; -class DropPermissionsHandler extends IDropPermissions.Stub { +class DropPermissionsHandler extends IDropPermissions.Stub implements IBinder.DeathRecipient { private final int mSourceUid; private final String mTargetPackage; @@ -38,6 +38,7 @@ class DropPermissionsHandler extends IDropPermissions.Stub { private final ArrayList<Uri> mUris = new ArrayList<Uri>(); private IBinder mActivityToken = null; + private IBinder mPermissionOwnerToken = null; DropPermissionsHandler(ClipData clipData, int sourceUid, String targetPackage, int mode, int sourceUserId, int targetUserId) { @@ -52,7 +53,7 @@ class DropPermissionsHandler extends IDropPermissions.Stub { @Override public void take(IBinder activityToken) throws RemoteException { - if (mActivityToken != null) { + if (mActivityToken != null || mPermissionOwnerToken != null) { return; } mActivityToken = activityToken; @@ -61,6 +62,10 @@ class DropPermissionsHandler extends IDropPermissions.Stub { IBinder permissionOwner = ActivityManagerNative.getDefault(). getUriPermissionOwnerForActivity(mActivityToken); + doTake(permissionOwner); + } + + private void doTake(IBinder permissionOwner) throws RemoteException { long origId = Binder.clearCallingIdentity(); try { for (int i = 0; i < mUris.size(); i++) { @@ -74,20 +79,37 @@ class DropPermissionsHandler extends IDropPermissions.Stub { } @Override + public void takeTransient(IBinder permissionOwnerToken) throws RemoteException { + if (mActivityToken != null || mPermissionOwnerToken != null) { + return; + } + mPermissionOwnerToken = permissionOwnerToken; + mPermissionOwnerToken.linkToDeath(this, 0); + + doTake(mPermissionOwnerToken); + } + + @Override public void release() throws RemoteException { - if (mActivityToken == null) { + if (mActivityToken == null && mPermissionOwnerToken == null) { return; } IBinder permissionOwner = null; - try { - permissionOwner = ActivityManagerNative.getDefault(). - getUriPermissionOwnerForActivity(mActivityToken); - } catch (Exception e) { - // Activity is destroyed, permissions already revoked. - return; - } finally { - mActivityToken = null; + if (mActivityToken != null) { + try { + permissionOwner = ActivityManagerNative.getDefault(). + getUriPermissionOwnerForActivity(mActivityToken); + } catch (Exception e) { + // Activity is destroyed, permissions already revoked. + return; + } finally { + mActivityToken = null; + } + } else { + permissionOwner = mPermissionOwnerToken; + mPermissionOwnerToken.unlinkToDeath(this, 0); + mPermissionOwnerToken = null; } for (int i = 0; i < mUris.size(); ++i) { @@ -95,4 +117,13 @@ class DropPermissionsHandler extends IDropPermissions.Stub { permissionOwner, mUris.get(i), mMode, mSourceUserId); } } + + @Override + public void binderDied() { + try { + release(); + } catch (RemoteException e) { + // Cannot happen, local call. + } + } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 5adf6278dd9d..93a10156414a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -5349,7 +5349,29 @@ public class WindowManagerService extends IWindowManager.Stub rebuildAppWindowListLocked(displayContent); } mWindowPlacerLocked.performSurfacePlacement(); + + // Notify whether the docked stack exists for the current user + getDefaultDisplayContentLocked().mDividerControllerLocked + .notifyDockedStackExistsChanged(hasDockedTasksForUser(newUserId)); + } + } + + /** + * Returns whether there is a docked task for the current user. + */ + boolean hasDockedTasksForUser(int userId) { + final TaskStack stack = mStackIdToStack.get(DOCKED_STACK_ID); + if (stack == null) { + return false; + } + + final ArrayList<Task> tasks = stack.getTasks(); + boolean hasUserTask = false; + for (int i = tasks.size() - 1; i >= 0 && !hasUserTask; i--) { + final Task task = tasks.get(i); + hasUserTask = (task.mUserId == userId); } + return hasUserTask; } /* Called by WindowState */ diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index e3e90b668bb9..04820a10072c 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -202,6 +202,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private static final String ATTR_PERMISSION_PROVIDER = "permission-provider"; private static final String ATTR_SETUP_COMPLETE = "setup-complete"; + private static final String ATTR_PROVISIONING_STATE = "provisioning-state"; private static final String ATTR_PERMISSION_POLICY = "permission-policy"; private static final String ATTR_DELEGATED_CERT_INSTALLER = "delegated-cert-installer"; @@ -364,6 +365,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { int mPasswordOwner = -1; long mLastMaximumTimeToLock = -1; boolean mUserSetupComplete = false; + int mUserProvisioningState; int mPermissionPolicy; final ArrayMap<ComponentName, ActiveAdmin> mAdminMap = new ArrayMap<>(); @@ -2017,6 +2019,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { out.attribute(null, ATTR_SETUP_COMPLETE, Boolean.toString(true)); } + if (policy.mUserProvisioningState != DevicePolicyManager.STATE_USER_UNMANAGED) { + out.attribute(null, ATTR_PROVISIONING_STATE, + Integer.toString(policy.mUserProvisioningState)); + } if (policy.mPermissionPolicy != DevicePolicyManager.PERMISSION_POLICY_PROMPT) { out.attribute(null, ATTR_PERMISSION_POLICY, Integer.toString(policy.mPermissionPolicy)); @@ -2167,6 +2173,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (userSetupComplete != null && Boolean.toString(true).equals(userSetupComplete)) { policy.mUserSetupComplete = true; } + String provisioningState = parser.getAttributeValue(null, ATTR_PROVISIONING_STATE); + if (!TextUtils.isEmpty(provisioningState)) { + policy.mUserProvisioningState = Integer.parseInt(provisioningState); + } String permissionPolicy = parser.getAttributeValue(null, ATTR_PERMISSION_POLICY); if (!TextUtils.isEmpty(permissionPolicy)) { policy.mPermissionPolicy = Integer.parseInt(permissionPolicy); @@ -5379,6 +5389,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { policy.mDelegatedCertInstallerPackage = null; policy.mApplicationRestrictionsManagingPackage = null; policy.mStatusBarDisabled = false; + policy.mUserProvisioningState = DevicePolicyManager.STATE_USER_UNMANAGED; saveSettingsLocked(userId); final long ident = mInjector.binderClearCallingIdentity(); @@ -5406,6 +5417,98 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override + public int getUserProvisioningState() { + if (!mHasFeature) { + return DevicePolicyManager.STATE_USER_UNMANAGED; + } + int userHandle = mInjector.userHandleGetCallingUserId(); + return getUserProvisioningState(userHandle); + } + + private int getUserProvisioningState(int userHandle) { + return getUserData(userHandle).mUserProvisioningState; + } + + @Override + public void setUserProvisioningState(int newState, int userHandle) { + if (!mHasFeature) { + return; + } + + if (userHandle != mOwners.getDeviceOwnerUserId() && !mOwners.hasProfileOwner(userHandle) + && getManagedUserId(userHandle) == -1) { + // No managed device, user or profile, so setting provisioning state makes no sense. + throw new IllegalStateException("Not allowed to change provisioning state unless a " + + "device or profile owner is set."); + } + + synchronized (this) { + boolean transitionCheckNeeded = true; + + // Calling identity/permission checks. + final int callingUid = mInjector.binderGetCallingUid(); + if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) { + // ADB shell can only move directly from un-managed to finalized as part of directly + // setting profile-owner or device-owner. + if (getUserProvisioningState(userHandle) != + DevicePolicyManager.STATE_USER_UNMANAGED + || newState != DevicePolicyManager.STATE_USER_SETUP_FINALIZED) { + throw new IllegalStateException("Not allowed to change provisioning state " + + "unless current provisioning state is unmanaged, and new state is " + + "finalized."); + } + transitionCheckNeeded = false; + } else { + // For all other cases, caller must have MANAGE_PROFILE_AND_DEVICE_OWNERS. + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null); + } + + final DevicePolicyData policyData = getUserData(userHandle); + if (transitionCheckNeeded) { + // Optional state transition check for non-ADB case. + checkUserProvisioningStateTransition(policyData.mUserProvisioningState, newState); + } + policyData.mUserProvisioningState = newState; + saveSettingsLocked(userHandle); + } + } + + private void checkUserProvisioningStateTransition(int currentState, int newState) { + // Valid transitions for normal use-cases. + switch (currentState) { + case DevicePolicyManager.STATE_USER_UNMANAGED: + // Can move to any state from unmanaged (except itself as an edge case).. + if (newState != DevicePolicyManager.STATE_USER_UNMANAGED) { + return; + } + break; + case DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE: + case DevicePolicyManager.STATE_USER_SETUP_COMPLETE: + // Can only move to finalized from these states. + if (newState == DevicePolicyManager.STATE_USER_SETUP_FINALIZED) { + return; + } + break; + case DevicePolicyManager.STATE_USER_PROFILE_COMPLETE: + // Current user has a managed-profile, but current user is not managed, so + // rather than moving to finalized state, go back to unmanaged once + // profile provisioning is complete. + if (newState == DevicePolicyManager.STATE_USER_UNMANAGED) { + return; + } + break; + case DevicePolicyManager.STATE_USER_SETUP_FINALIZED: + // Cannot transition out of finalized. + break; + } + + // Didn't meet any of the accepted state transition checks above, throw appropriate error. + throw new IllegalStateException("Cannot move to user provisioning state [" + newState + "] " + + "from state [" + currentState + "]"); + } + + @Override public void setProfileEnabled(ComponentName who) { if (!mHasFeature) { return; @@ -5724,7 +5827,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { for (int u = 0; u < userCount; u++) { DevicePolicyData policy = getUserData(mUserData.keyAt(u)); pw.println(); - pw.println(" Enabled Device Admins (User " + policy.mUserHandle + "):"); + pw.println(" Enabled Device Admins (User " + policy.mUserHandle + + ", provisioningState: " + policy.mUserProvisioningState + "):"); final int N = policy.mAdminList.size(); for (int i=0; i<N; i++) { ActiveAdmin ap = policy.mAdminList.get(i); @@ -6937,9 +7041,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public void startManagedQuickContact(String actualLookupKey, long actualContactId, - long actualDirectoryId, Intent originalIntent) { - final Intent intent = QuickContact.rebuildManagedQuickContactsIntent( - actualLookupKey, actualContactId, actualDirectoryId, originalIntent); + boolean isContactIdIgnored, long actualDirectoryId, Intent originalIntent) { + final Intent intent = QuickContact.rebuildManagedQuickContactsIntent(actualLookupKey, + actualContactId, isContactIdIgnored, actualDirectoryId, originalIntent); final int callingUserId = UserHandle.getCallingUserId(); final long ident = mInjector.binderClearCallingIdentity(); diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java index 5abb6e7d9367..e6f41775111c 100644 --- a/services/print/java/com/android/server/print/PrintManagerService.java +++ b/services/print/java/com/android/server/print/PrintManagerService.java @@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.GET_SERVICES; import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; import android.Manifest; +import android.annotation.NonNull; import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.content.ComponentName; @@ -47,11 +48,11 @@ import android.print.PrintJobInfo; import android.print.PrinterId; import android.printservice.PrintServiceInfo; import android.provider.Settings; -import android.text.TextUtils; import android.util.SparseArray; import com.android.internal.content.PackageMonitor; import com.android.internal.os.BackgroundThread; +import com.android.internal.util.Preconditions; import com.android.server.SystemService; import java.io.FileDescriptor; @@ -108,6 +109,10 @@ public final class PrintManagerService extends SystemService { @Override public Bundle print(String printJobName, IPrintDocumentAdapter adapter, PrintAttributes attributes, String packageName, int appId, int userId) { + printJobName = Preconditions.checkStringNotEmpty(printJobName); + adapter = Preconditions.checkNotNull(adapter); + packageName = Preconditions.checkStringNotEmpty(packageName); + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final int resolvedAppId; final UserState userState; @@ -153,6 +158,10 @@ public final class PrintManagerService extends SystemService { @Override public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId, int userId) { + if (printJobId == null) { + return null; + } + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final int resolvedAppId; final UserState userState; @@ -174,6 +183,8 @@ public final class PrintManagerService extends SystemService { @Override public Icon getCustomPrinterIcon(PrinterId printerId, int userId) { + printerId = Preconditions.checkNotNull(printerId); + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { @@ -193,6 +204,10 @@ public final class PrintManagerService extends SystemService { @Override public void cancelPrintJob(PrintJobId printJobId, int appId, int userId) { + if (printJobId == null) { + return; + } + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final int resolvedAppId; final UserState userState; @@ -214,6 +229,10 @@ public final class PrintManagerService extends SystemService { @Override public void restartPrintJob(PrintJobId printJobId, int appId, int userId) { + if (printJobId == null) { + return; + } + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final int resolvedAppId; final UserState userState; @@ -279,6 +298,8 @@ public final class PrintManagerService extends SystemService { @Override public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer, int userId) { + observer = Preconditions.checkNotNull(observer); + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { @@ -299,6 +320,8 @@ public final class PrintManagerService extends SystemService { @Override public void destroyPrinterDiscoverySession(IPrinterDiscoveryObserver observer, int userId) { + observer = Preconditions.checkNotNull(observer); + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { @@ -319,6 +342,12 @@ public final class PrintManagerService extends SystemService { @Override public void startPrinterDiscovery(IPrinterDiscoveryObserver observer, List<PrinterId> priorityList, int userId) { + observer = Preconditions.checkNotNull(observer); + if (priorityList != null) { + priorityList = Preconditions.checkCollectionElementsNotNull(priorityList, + "PrinterId"); + } + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { @@ -338,6 +367,8 @@ public final class PrintManagerService extends SystemService { @Override public void stopPrinterDiscovery(IPrinterDiscoveryObserver observer, int userId) { + observer = Preconditions.checkNotNull(observer); + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { @@ -357,6 +388,8 @@ public final class PrintManagerService extends SystemService { @Override public void validatePrinters(List<PrinterId> printerIds, int userId) { + printerIds = Preconditions.checkCollectionElementsNotNull(printerIds, "PrinterId"); + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { @@ -376,6 +409,8 @@ public final class PrintManagerService extends SystemService { @Override public void startPrinterStateTracking(PrinterId printerId, int userId) { + printerId = Preconditions.checkNotNull(printerId); + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { @@ -395,6 +430,8 @@ public final class PrintManagerService extends SystemService { @Override public void stopPrinterStateTracking(PrinterId printerId, int userId) { + printerId = Preconditions.checkNotNull(printerId); + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { @@ -415,6 +452,8 @@ public final class PrintManagerService extends SystemService { @Override public void addPrintJobStateChangeListener(IPrintJobStateChangeListener listener, int appId, int userId) throws RemoteException { + listener = Preconditions.checkNotNull(listener); + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final int resolvedAppId; final UserState userState; @@ -437,6 +476,8 @@ public final class PrintManagerService extends SystemService { @Override public void removePrintJobStateChangeListener(IPrintJobStateChangeListener listener, int userId) { + listener = Preconditions.checkNotNull(listener); + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { @@ -456,6 +497,9 @@ public final class PrintManagerService extends SystemService { @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + fd = Preconditions.checkNotNull(fd); + pw = Preconditions.checkNotNull(pw); + if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump PrintManager from from pid=" @@ -707,10 +751,8 @@ public final class PrintManagerService extends SystemService { return userId; } - private String resolveCallingPackageNameEnforcingSecurity(String packageName) { - if (TextUtils.isEmpty(packageName)) { - return null; - } + private @NonNull String resolveCallingPackageNameEnforcingSecurity( + @NonNull String packageName) { String[] packages = mContext.getPackageManager().getPackagesForUid( Binder.getCallingUid()); final int packageCount = packages.length; @@ -719,7 +761,7 @@ public final class PrintManagerService extends SystemService { return packageName; } } - return null; + throw new IllegalArgumentException("packageName has to belong to the caller"); } private int getCurrentUserId () { diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java index 5ee4066f69ec..9b99c677a822 100644 --- a/services/print/java/com/android/server/print/RemotePrintService.java +++ b/services/print/java/com/android/server/print/RemotePrintService.java @@ -409,7 +409,7 @@ final class RemotePrintService implements DeathRecipient { } } - public void startPrinterStateTracking(PrinterId printerId) { + public void startPrinterStateTracking(@NonNull PrinterId printerId) { mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_STATE_TRACKING, printerId).sendToTarget(); } @@ -420,7 +420,7 @@ final class RemotePrintService implements DeathRecipient { * @param printerId the id of the printer the icon should be loaded for * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon() */ - public void requestCustomPrinterIcon(PrinterId printerId) { + public void requestCustomPrinterIcon(@NonNull PrinterId printerId) { try { if (isBound()) { mPrintService.requestCustomPrinterIcon(printerId); @@ -430,7 +430,7 @@ final class RemotePrintService implements DeathRecipient { } } - private void handleStartPrinterStateTracking(final PrinterId printerId) { + private void handleStartPrinterStateTracking(final @NonNull PrinterId printerId) { throwIfDestroyed(); // Take a note we are tracking the printer. if (mTrackedPrinterList == null) { diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java index 78edc4df08fa..fcf2fc8e7860 100644 --- a/services/print/java/com/android/server/print/UserState.java +++ b/services/print/java/com/android/server/print/UserState.java @@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.GET_META_DATA; import static android.content.pm.PackageManager.GET_SERVICES; import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.PendingIntent; import android.content.ComponentName; @@ -176,8 +177,8 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } @SuppressWarnings("deprecation") - public Bundle print(String printJobName, IPrintDocumentAdapter adapter, - PrintAttributes attributes, String packageName, int appId) { + public Bundle print(@NonNull String printJobName, @NonNull IPrintDocumentAdapter adapter, + @Nullable PrintAttributes attributes, @NonNull String packageName, int appId) { // Create print job place holder. final PrintJobInfo printJob = new PrintJobInfo(); printJob.setId(new PrintJobId()); @@ -267,7 +268,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { return new ArrayList<PrintJobInfo>(result.values()); } - public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId) { + public PrintJobInfo getPrintJobInfo(@NonNull PrintJobId printJobId, int appId) { PrintJobInfo printJob = mPrintJobForAppCache.getPrintJob(printJobId, appId); if (printJob == null) { printJob = mSpooler.getPrintJobInfo(printJobId, appId); @@ -290,7 +291,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { * not yet available * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon() */ - public Icon getCustomPrinterIcon(PrinterId printerId) { + public @Nullable Icon getCustomPrinterIcon(@NonNull PrinterId printerId) { Icon icon = mSpooler.getCustomPrinterIcon(printerId); if (icon == null) { @@ -303,7 +304,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { return icon; } - public void cancelPrintJob(PrintJobId printJobId, int appId) { + public void cancelPrintJob(@NonNull PrintJobId printJobId, int appId) { PrintJobInfo printJobInfo = mSpooler.getPrintJobInfo(printJobId, appId); if (printJobInfo == null) { return; @@ -313,15 +314,19 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { mSpooler.setPrintJobCancelling(printJobId, true); if (printJobInfo.getState() != PrintJobInfo.STATE_FAILED) { - ComponentName printServiceName = printJobInfo.getPrinterId().getServiceName(); - RemotePrintService printService = null; - synchronized (mLock) { - printService = mActiveServices.get(printServiceName); - } - if (printService == null) { - return; + PrinterId printerId = printJobInfo.getPrinterId(); + + if (printerId != null) { + ComponentName printServiceName = printerId.getServiceName(); + RemotePrintService printService = null; + synchronized (mLock) { + printService = mActiveServices.get(printServiceName); + } + if (printService == null) { + return; + } + printService.onRequestCancelPrintJob(printJobInfo); } - printService.onRequestCancelPrintJob(printJobInfo); } else { // If the print job is failed we do not need cooperation // from the print service. @@ -329,7 +334,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public void restartPrintJob(PrintJobId printJobId, int appId) { + public void restartPrintJob(@NonNull PrintJobId printJobId, int appId) { PrintJobInfo printJobInfo = getPrintJobInfo(printJobId, appId); if (printJobInfo == null || printJobInfo.getState() != PrintJobInfo.STATE_FAILED) { return; @@ -363,7 +368,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer) { + public void createPrinterDiscoverySession(@NonNull IPrinterDiscoveryObserver observer) { synchronized (mLock) { throwIfDestroyedLocked(); @@ -386,7 +391,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public void destroyPrinterDiscoverySession(IPrinterDiscoveryObserver observer) { + public void destroyPrinterDiscoverySession(@NonNull IPrinterDiscoveryObserver observer) { synchronized (mLock) { // Already destroyed - nothing to do. if (mPrinterDiscoverySession == null) { @@ -397,8 +402,8 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public void startPrinterDiscovery(IPrinterDiscoveryObserver observer, - List<PrinterId> printerIds) { + public void startPrinterDiscovery(@NonNull IPrinterDiscoveryObserver observer, + @Nullable List<PrinterId> printerIds) { synchronized (mLock) { throwIfDestroyedLocked(); // No services - nothing to do. @@ -415,7 +420,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public void stopPrinterDiscovery(IPrinterDiscoveryObserver observer) { + public void stopPrinterDiscovery(@NonNull IPrinterDiscoveryObserver observer) { synchronized (mLock) { throwIfDestroyedLocked(); // No services - nothing to do. @@ -431,7 +436,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public void validatePrinters(List<PrinterId> printerIds) { + public void validatePrinters(@NonNull List<PrinterId> printerIds) { synchronized (mLock) { throwIfDestroyedLocked(); // No services - nothing to do. @@ -447,7 +452,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public void startPrinterStateTracking(PrinterId printerId) { + public void startPrinterStateTracking(@NonNull PrinterId printerId) { synchronized (mLock) { throwIfDestroyedLocked(); // No services - nothing to do. @@ -479,7 +484,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public void addPrintJobStateChangeListener(IPrintJobStateChangeListener listener, + public void addPrintJobStateChangeListener(@NonNull IPrintJobStateChangeListener listener, int appId) throws RemoteException { synchronized (mLock) { throwIfDestroyedLocked(); @@ -497,7 +502,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public void removePrintJobStateChangeListener(IPrintJobStateChangeListener listener) { + public void removePrintJobStateChangeListener(@NonNull IPrintJobStateChangeListener listener) { synchronized (mLock) { throwIfDestroyedLocked(); if (mPrintJobStateChangeListenerRecords == null) { @@ -613,7 +618,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { mDestroyed = true; } - public void dump(FileDescriptor fd, PrintWriter pw, String prefix) { + public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String prefix) { pw.append(prefix).append("user state ").append(String.valueOf(mUserId)).append(":"); pw.println(); @@ -991,10 +996,10 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } private abstract class PrintJobStateChangeListenerRecord implements DeathRecipient { - final IPrintJobStateChangeListener listener; + @NonNull final IPrintJobStateChangeListener listener; final int appId; - public PrintJobStateChangeListenerRecord(IPrintJobStateChangeListener listener, + public PrintJobStateChangeListenerRecord(@NonNull IPrintJobStateChangeListener listener, int appId) throws RemoteException { this.listener = listener; this.appId = appId; @@ -1043,7 +1048,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { .sendToTarget(); } - public void addObserverLocked(IPrinterDiscoveryObserver observer) { + public void addObserverLocked(@NonNull IPrinterDiscoveryObserver observer) { // Add the observer. mDiscoveryObservers.register(observer); @@ -1058,7 +1063,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public void removeObserverLocked(IPrinterDiscoveryObserver observer) { + public void removeObserverLocked(@NonNull IPrinterDiscoveryObserver observer) { // Remove the observer. mDiscoveryObservers.unregister(observer); // No one else observing - then kill it. @@ -1067,8 +1072,8 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public final void startPrinterDiscoveryLocked(IPrinterDiscoveryObserver observer, - List<PrinterId> priorityList) { + public final void startPrinterDiscoveryLocked(@NonNull IPrinterDiscoveryObserver observer, + @Nullable List<PrinterId> priorityList) { if (mIsDestroyed) { Log.w(LOG_TAG, "Not starting dicovery - session destroyed"); return; @@ -1101,7 +1106,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { .sendToTarget(); } - public final void stopPrinterDiscoveryLocked(IPrinterDiscoveryObserver observer) { + public final void stopPrinterDiscoveryLocked(@NonNull IPrinterDiscoveryObserver observer) { if (mIsDestroyed) { Log.w(LOG_TAG, "Not stopping dicovery - session destroyed"); return; @@ -1121,7 +1126,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { .sendToTarget(); } - public void validatePrintersLocked(List<PrinterId> printerIds) { + public void validatePrintersLocked(@NonNull List<PrinterId> printerIds) { if (mIsDestroyed) { Log.w(LOG_TAG, "Not validating pritners - session destroyed"); return; @@ -1135,13 +1140,15 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { ComponentName serviceName = null; while (iterator.hasNext()) { PrinterId printerId = iterator.next(); - if (updateList.isEmpty()) { - updateList.add(printerId); - serviceName = printerId.getServiceName(); - iterator.remove(); - } else if (printerId.getServiceName().equals(serviceName)) { - updateList.add(printerId); - iterator.remove(); + if (printerId != null) { + if (updateList.isEmpty()) { + updateList.add(printerId); + serviceName = printerId.getServiceName(); + iterator.remove(); + } else if (printerId.getServiceName().equals(serviceName)) { + updateList.add(printerId); + iterator.remove(); + } } } // Schedule a notification of the service. @@ -1157,7 +1164,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - public final void startPrinterStateTrackingLocked(PrinterId printerId) { + public final void startPrinterStateTrackingLocked(@NonNull PrinterId printerId) { if (mIsDestroyed) { Log.w(LOG_TAG, "Not starting printer state tracking - session destroyed"); return; @@ -1500,8 +1507,8 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { service.validatePrinters(printerIds); } - private void handleStartPrinterStateTracking(RemotePrintService service, - PrinterId printerId) { + private void handleStartPrinterStateTracking(@NonNull RemotePrintService service, + @NonNull PrinterId printerId) { service.startPrinterStateTracking(printerId); } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 536fb70d9068..87569b7a6caa 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -15,9 +15,6 @@ */ package com.android.server.devicepolicy; -import com.android.server.LocalServices; -import com.android.server.SystemService; - import android.Manifest.permission; import android.app.Activity; import android.app.admin.DeviceAdminReceiver; @@ -27,21 +24,26 @@ import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.pm.PackageManager; import android.net.wifi.WifiInfo; -import android.os.Build; import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.test.MoreAsserts; +import android.test.suitebuilder.annotation.SmallTest; import android.util.ArraySet; import android.util.Pair; +import com.android.server.LocalServices; +import com.android.server.SystemService; + import org.mockito.ArgumentCaptor; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -64,13 +66,18 @@ import static org.mockito.Mockito.when; * m FrameworksServicesTests && adb install \ - -r out/target/product/hammerhead/data/app/FrameworksServicesTests/FrameworksServicesTests.apk && + -r ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk && adb shell am instrument -e class com.android.server.devicepolicy.DevicePolicyManagerTest \ -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner (mmma frameworks/base/services/tests/servicestests/ for non-ninja build) */ +@SmallTest public class DevicePolicyManagerTest extends DpmTestBase { + private static final List<String> OWNER_SETUP_PERMISSIONS = Arrays.asList( + permission.MANAGE_DEVICE_ADMINS, permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, + permission.MANAGE_USERS, permission.INTERACT_ACROSS_USERS_FULL); + private DpmMockContext mContext; public DevicePolicyManager dpm; public DevicePolicyManagerServiceTestable dpms; @@ -1543,4 +1550,156 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; assertTrue(dpm.isAffiliatedUser()); } + + public void testGetUserProvisioningState_defaultResult() { + assertEquals(DevicePolicyManager.STATE_USER_UNMANAGED, dpm.getUserProvisioningState()); + } + + public void testSetUserProvisioningState_permission() throws Exception { + setupProfileOwner(); + mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); + + exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE, + DevicePolicyManager.STATE_USER_SETUP_FINALIZED); + } + + public void testSetUserProvisioningState_unprivileged() throws Exception { + setupProfileOwner(); + try { + dpm.setUserProvisioningState(DevicePolicyManager.STATE_USER_SETUP_FINALIZED, + DpmMockContext.CALLER_USER_HANDLE); + fail("Expected SecurityException"); + } catch (SecurityException expected) { + } + } + + public void testSetUserProvisioningState_noManagement() { + mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); + try { + dpm.setUserProvisioningState(DevicePolicyManager.STATE_USER_SETUP_FINALIZED, + DpmMockContext.CALLER_USER_HANDLE); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + MoreAsserts.assertContainsRegex("change provisioning state unless a .* owner is set", + e.getMessage()); + } + assertEquals(DevicePolicyManager.STATE_USER_UNMANAGED, dpm.getUserProvisioningState()); + } + + public void testSetUserProvisioningState_deviceOwnerFromSetupWizard() throws Exception { + mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; + setupDeviceOwner(); + mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); + + exerciseUserProvisioningTransitions(UserHandle.USER_SYSTEM, + DevicePolicyManager.STATE_USER_SETUP_COMPLETE, + DevicePolicyManager.STATE_USER_SETUP_FINALIZED); + } + + public void testSetUserProvisioningState_deviceOwnerFromSetupWizardAlternative() + throws Exception { + mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; + setupDeviceOwner(); + mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); + + exerciseUserProvisioningTransitions(UserHandle.USER_SYSTEM, + DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE, + DevicePolicyManager.STATE_USER_SETUP_FINALIZED); + } + + public void testSetUserProvisioningState_deviceOwnerWithoutSetupWizard() throws Exception { + mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; + setupDeviceOwner(); + mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); + + exerciseUserProvisioningTransitions(UserHandle.USER_SYSTEM, + DevicePolicyManager.STATE_USER_SETUP_FINALIZED); + } + + public void testSetUserProvisioningState_managedProfileFromSetupWizard_primaryUser() + throws Exception { + setupProfileOwner(); + mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); + + exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE, + DevicePolicyManager.STATE_USER_PROFILE_COMPLETE, + DevicePolicyManager.STATE_USER_UNMANAGED); + } + + public void testSetUserProvisioningState_managedProfileFromSetupWizard_managedProfile() + throws Exception { + setupProfileOwner(); + mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); + + exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE, + DevicePolicyManager.STATE_USER_SETUP_COMPLETE, + DevicePolicyManager.STATE_USER_SETUP_FINALIZED); + } + + public void testSetUserProvisioningState_managedProfileWithoutSetupWizard() throws Exception { + setupProfileOwner(); + mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); + + exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE, + DevicePolicyManager.STATE_USER_SETUP_FINALIZED); + } + + public void testSetUserProvisioningState_illegalTransitionOutOfFinalized1() throws Exception { + setupProfileOwner(); + mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); + + try { + exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE, + DevicePolicyManager.STATE_USER_SETUP_FINALIZED, + DevicePolicyManager.STATE_USER_UNMANAGED); + fail("Expected IllegalStateException"); + } catch (IllegalStateException e) { + MoreAsserts.assertContainsRegex("Cannot move to user provisioning state", + e.getMessage()); + } + } + + public void testSetUserProvisioningState_illegalTransitionToAnotherInProgressState() + throws Exception { + setupProfileOwner(); + mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); + + try { + exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE, + DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE, + DevicePolicyManager.STATE_USER_SETUP_COMPLETE); + fail("Expected IllegalStateException"); + } catch (IllegalStateException e) { + MoreAsserts.assertContainsRegex("Cannot move to user provisioning state", + e.getMessage()); + } + } + + private void exerciseUserProvisioningTransitions(int userId, int... states) { + assertEquals(DevicePolicyManager.STATE_USER_UNMANAGED, dpm.getUserProvisioningState()); + for (int state : states) { + dpm.setUserProvisioningState(state, userId); + assertEquals(state, dpm.getUserProvisioningState()); + } + } + + private void setupProfileOwner() throws Exception { + mContext.callerPermissions.addAll(OWNER_SETUP_PERMISSIONS); + + setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID); + dpm.setActiveAdmin(admin1, false); + assertTrue(dpm.setProfileOwner(admin1, null, DpmMockContext.CALLER_USER_HANDLE)); + + mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS); + } + + private void setupDeviceOwner() throws Exception { + mContext.callerPermissions.addAll(OWNER_SETUP_PERMISSIONS); + + setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID); + dpm.setActiveAdmin(admin1, false); + assertTrue(dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM)); + + mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS); + } } diff --git a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java index 264088903777..31182fcf46ae 100644 --- a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java @@ -155,16 +155,12 @@ public class RankingHelperTest extends AndroidTestCase { @SmallTest public void testTopicImportanceExtractor() throws Exception { - mHelper.setTopicImportance("package", 0, new Notification.Topic("A", "a"), - IMPORTANCE_MAX); + mHelper.setImportance("package", 0, new Notification.Topic("A", "a"), IMPORTANCE_MAX); // There is no B. There never was a b. Moving on... - mHelper.setTopicImportance("package", 0, new Notification.Topic("C", "c"), - IMPORTANCE_HIGH); - mHelper.setTopicImportance("package", 0, new Notification.Topic("D", "d"), - IMPORTANCE_LOW); + mHelper.setImportance("package", 0, new Notification.Topic("C", "c"), IMPORTANCE_HIGH); + mHelper.setImportance("package", 0, new Notification.Topic("D", "d"), IMPORTANCE_LOW); // watch out: different package. - mHelper.setTopicImportance("package2", 0, new Notification.Topic("E", "e"), - IMPORTANCE_NONE); + mHelper.setImportance("package2", 0, new Notification.Topic("E", "e"), IMPORTANCE_NONE); TopicImportanceExtractor validator = mHelper.findExtractor(TopicImportanceExtractor.class); validator.process(mRecordGroupGSortA); diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index aa95e1d69a10..385929428a52 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -24,7 +24,6 @@ import android.os.Handler; import java.lang.String; import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; @@ -210,7 +209,22 @@ public final class Call { * Call sends responses through connection. * @hide */ - public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00400000; + public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00200000; + + /** + * When set, prevents a video {@code Call} from being downgraded to an audio-only call. + * <p> + * Should be set when the VideoState has the {@link VideoProfile#STATE_TX_ENABLED} or + * {@link VideoProfile#STATE_RX_ENABLED} bits set to indicate that the connection cannot be + * downgraded from a video call back to a VideoState of + * {@link VideoProfile#STATE_AUDIO_ONLY}. + * <p> + * Intuitively, a call which can be downgraded to audio should also have local and remote + * video + * capabilities (see {@link #CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL} and + * {@link #CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL}). + */ + public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00400000; //****************************************************************************************** // Next CAPABILITY value: 0x00800000 @@ -332,6 +346,9 @@ public final class Call { if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_TX)) { builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_TX"); } + if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) { + builder.append(" CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO"); + } if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) { builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL"); } diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index deb98f4211b1..fa7a59d76f09 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -255,8 +255,23 @@ public abstract class Connection extends Conferenceable { */ public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00400000; + /** + * When set, prevents a video call from being downgraded to an audio-only call. + * <p> + * Should be set when the VideoState has the {@link VideoProfile#STATE_TX_ENABLED} or + * {@link VideoProfile#STATE_RX_ENABLED} bits set to indicate that the connection cannot be + * downgraded from a video call back to a VideoState of + * {@link VideoProfile#STATE_AUDIO_ONLY}. + * <p> + * Intuitively, a call which can be downgraded to audio should also have local and remote + * video + * capabilities (see {@link #CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL} and + * {@link #CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL}). + */ + public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00800000; + //********************************************************************************************** - // Next CAPABILITY value: 0x00800000 + // Next CAPABILITY value: 0x01000000 //********************************************************************************************** /** @@ -371,6 +386,9 @@ public abstract class Connection extends Conferenceable { if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) { builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL"); } + if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) { + builder.append(" CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO"); + } if (can(capabilities, CAPABILITY_HIGH_DEF_AUDIO)) { builder.append(" CAPABILITY_HIGH_DEF_AUDIO"); } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 320d27457f20..630daccb8671 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -746,7 +746,7 @@ public class CarrierConfigManager { */ @Nullable public PersistableBundle getConfig() { - return getConfigForSubId(SubscriptionManager.getDefaultSubId()); + return getConfigForSubId(SubscriptionManager.getDefaultSubscriptionId()); } /** diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java index 74f11719d237..90d2aa0223a0 100644 --- a/telephony/java/android/telephony/CellIdentityGsm.java +++ b/telephony/java/android/telephony/CellIdentityGsm.java @@ -38,10 +38,6 @@ public final class CellIdentityGsm implements Parcelable { private final int mLac; // 16-bit GSM Cell Identity described in TS 27.007, 0..65535 private final int mCid; - // 16-bit GSM Absolute RF Channel Number - private final int mArfcn; - // 6-bit Base Station Identity Code - private final int mBsic; /** * @hide @@ -51,8 +47,6 @@ public final class CellIdentityGsm implements Parcelable { mMnc = Integer.MAX_VALUE; mLac = Integer.MAX_VALUE; mCid = Integer.MAX_VALUE; - mArfcn = Integer.MAX_VALUE; - mBsic = Integer.MAX_VALUE; } /** * public constructor @@ -64,27 +58,10 @@ public final class CellIdentityGsm implements Parcelable { * @hide */ public CellIdentityGsm (int mcc, int mnc, int lac, int cid) { - this(mcc, mnc, lac, cid, Integer.MAX_VALUE, Integer.MAX_VALUE); - } - - /** - * public constructor - * @param mcc 3-digit Mobile Country Code, 0..999 - * @param mnc 2 or 3-digit Mobile Network Code, 0..999 - * @param lac 16-bit Location Area Code, 0..65535 - * @param cid 16-bit GSM Cell Identity or 28-bit UMTS Cell Identity - * @param arfcn 16-bit GSM Absolute RF Channel Number - * @param bsic 6-bit Base Station Identity Code - * - * @hide - */ - public CellIdentityGsm (int mcc, int mnc, int lac, int cid, int arfcn, int bsic) { mMcc = mcc; mMnc = mnc; mLac = lac; mCid = cid; - mArfcn = arfcn; - mBsic = bsic; } private CellIdentityGsm(CellIdentityGsm cid) { @@ -92,8 +69,6 @@ public final class CellIdentityGsm implements Parcelable { mMnc = cid.mMnc; mLac = cid.mLac; mCid = cid.mCid; - mArfcn = cid.mArfcn; - mBsic = cid.mBsic; } CellIdentityGsm copy() { @@ -131,21 +106,6 @@ public final class CellIdentityGsm implements Parcelable { } /** - * @return 16-bit GSM Absolute RF Channel Number, Integer.MAX_VALUE if unknown - */ - public int getArfcn() { - return mArfcn; - } - - /** - * @return 6-bit Base Station Identity Code, Integer.MAX_VALUE if unknown - */ - public int getBsic() { - return mBsic; - } - - - /** * @return Integer.MAX_VALUE, undefined for GSM */ @Deprecated @@ -172,9 +132,7 @@ public final class CellIdentityGsm implements Parcelable { return mMcc == o.mMcc && mMnc == o.mMnc && mLac == o.mLac && - mCid == o.mCid && - mArfcn == o.mArfcn && - mBsic == o.mBsic; + mCid == o.mCid; } @Override @@ -184,8 +142,6 @@ public final class CellIdentityGsm implements Parcelable { sb.append(" mMnc=").append(mMnc); sb.append(" mLac=").append(mLac); sb.append(" mCid=").append(mCid); - sb.append(" mArfcn=").append(mArfcn); - sb.append(" mBsic=").append("0x").append(Integer.toHexString(mBsic)); sb.append("}"); return sb.toString(); @@ -205,8 +161,6 @@ public final class CellIdentityGsm implements Parcelable { dest.writeInt(mMnc); dest.writeInt(mLac); dest.writeInt(mCid); - dest.writeInt(mArfcn); - dest.writeInt(mBsic); } /** Construct from Parcel, type has already been processed */ @@ -215,8 +169,6 @@ public final class CellIdentityGsm implements Parcelable { mMnc = in.readInt(); mLac = in.readInt(); mCid = in.readInt(); - mArfcn = in.readInt(); - mBsic = in.readInt(); if (DBG) log("CellIdentityGsm(Parcel): " + toString()); } diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java index ce7438354a50..1e7ac08b3e43 100644 --- a/telephony/java/android/telephony/CellIdentityLte.java +++ b/telephony/java/android/telephony/CellIdentityLte.java @@ -40,8 +40,6 @@ public final class CellIdentityLte implements Parcelable { private final int mPci; // 16-bit tracking area code private final int mTac; - // 18-bit Absolute RF Channel Number - private final int mEarfcn; /** * @hide @@ -52,7 +50,6 @@ public final class CellIdentityLte implements Parcelable { mCi = Integer.MAX_VALUE; mPci = Integer.MAX_VALUE; mTac = Integer.MAX_VALUE; - mEarfcn = Integer.MAX_VALUE; } /** @@ -66,27 +63,11 @@ public final class CellIdentityLte implements Parcelable { * @hide */ public CellIdentityLte (int mcc, int mnc, int ci, int pci, int tac) { - this(mcc, mnc, ci, pci, tac, Integer.MAX_VALUE); - } - - /** - * - * @param mcc 3-digit Mobile Country Code, 0..999 - * @param mnc 2 or 3-digit Mobile Network Code, 0..999 - * @param ci 28-bit Cell Identity - * @param pci Physical Cell Id 0..503 - * @param tac 16-bit Tracking Area Code - * @param earfcn 18-bit LTE Absolute RF Channel Number - * - * @hide - */ - public CellIdentityLte (int mcc, int mnc, int ci, int pci, int tac, int earfcn) { mMcc = mcc; mMnc = mnc; mCi = ci; mPci = pci; mTac = tac; - mEarfcn = earfcn; } private CellIdentityLte(CellIdentityLte cid) { @@ -95,7 +76,6 @@ public final class CellIdentityLte implements Parcelable { mCi = cid.mCi; mPci = cid.mPci; mTac = cid.mTac; - mEarfcn = cid.mEarfcn; } CellIdentityLte copy() { @@ -137,13 +117,6 @@ public final class CellIdentityLte implements Parcelable { return mTac; } - /** - * @return 18-bit Absolute RF Channel Number, Integer.MAX_VALUE if unknown - */ - public int getEarfcn() { - return mEarfcn; - } - @Override public int hashCode() { return Objects.hash(mMcc, mMnc, mCi, mPci, mTac); @@ -164,8 +137,7 @@ public final class CellIdentityLte implements Parcelable { mMnc == o.mMnc && mCi == o.mCi && mPci == o.mPci && - mTac == o.mTac && - mEarfcn == o.mEarfcn; + mTac == o.mTac; } @Override @@ -176,7 +148,6 @@ public final class CellIdentityLte implements Parcelable { sb.append(" mCi="); sb.append(mCi); sb.append(" mPci="); sb.append(mPci); sb.append(" mTac="); sb.append(mTac); - sb.append(" mEarfcn="); sb.append(mEarfcn); sb.append("}"); return sb.toString(); @@ -197,7 +168,6 @@ public final class CellIdentityLte implements Parcelable { dest.writeInt(mCi); dest.writeInt(mPci); dest.writeInt(mTac); - dest.writeInt(mEarfcn); } /** Construct from Parcel, type has already been processed */ @@ -207,7 +177,6 @@ public final class CellIdentityLte implements Parcelable { mCi = in.readInt(); mPci = in.readInt(); mTac = in.readInt(); - mEarfcn = in.readInt(); if (DBG) log("CellIdentityLte(Parcel): " + toString()); } diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java index 0d13efd279c9..56ee8c9df138 100644 --- a/telephony/java/android/telephony/CellIdentityWcdma.java +++ b/telephony/java/android/telephony/CellIdentityWcdma.java @@ -40,8 +40,6 @@ public final class CellIdentityWcdma implements Parcelable { private final int mCid; // 9-bit UMTS Primary Scrambling Code described in TS 25.331, 0..511 private final int mPsc; - // 16-bit UMTS Absolute RF Channel Number - private final int mUarfcn; /** * @hide @@ -52,7 +50,6 @@ public final class CellIdentityWcdma implements Parcelable { mLac = Integer.MAX_VALUE; mCid = Integer.MAX_VALUE; mPsc = Integer.MAX_VALUE; - mUarfcn = Integer.MAX_VALUE; } /** * public constructor @@ -65,27 +62,11 @@ public final class CellIdentityWcdma implements Parcelable { * @hide */ public CellIdentityWcdma (int mcc, int mnc, int lac, int cid, int psc) { - this(mcc, mnc, lac, cid, psc, Integer.MAX_VALUE); - } - - /** - * public constructor - * @param mcc 3-digit Mobile Country Code, 0..999 - * @param mnc 2 or 3-digit Mobile Network Code, 0..999 - * @param lac 16-bit Location Area Code, 0..65535 - * @param cid 28-bit UMTS Cell Identity - * @param psc 9-bit UMTS Primary Scrambling Code - * @param uarfcn 16-bit UMTS Absolute RF Channel Number - * - * @hide - */ - public CellIdentityWcdma (int mcc, int mnc, int lac, int cid, int psc, int uarfcn) { mMcc = mcc; mMnc = mnc; mLac = lac; mCid = cid; mPsc = psc; - mUarfcn = uarfcn; } private CellIdentityWcdma(CellIdentityWcdma cid) { @@ -94,7 +75,6 @@ public final class CellIdentityWcdma implements Parcelable { mLac = cid.mLac; mCid = cid.mCid; mPsc = cid.mPsc; - mUarfcn = cid.mUarfcn; } CellIdentityWcdma copy() { @@ -143,13 +123,6 @@ public final class CellIdentityWcdma implements Parcelable { return Objects.hash(mMcc, mMnc, mLac, mCid, mPsc); } - /** - * @return 16-bit UMTS Absolute RF Channel Number, Integer.MAX_VALUE if unknown - */ - public int getUarfcn() { - return mUarfcn; - } - @Override public boolean equals(Object other) { if (this == other) { @@ -165,8 +138,7 @@ public final class CellIdentityWcdma implements Parcelable { mMnc == o.mMnc && mLac == o.mLac && mCid == o.mCid && - mPsc == o.mPsc && - mUarfcn == o.mUarfcn; + mPsc == o.mPsc; } @Override @@ -177,7 +149,6 @@ public final class CellIdentityWcdma implements Parcelable { sb.append(" mLac=").append(mLac); sb.append(" mCid=").append(mCid); sb.append(" mPsc=").append(mPsc); - sb.append(" mUarfcn=").append(mUarfcn); sb.append("}"); return sb.toString(); @@ -198,7 +169,6 @@ public final class CellIdentityWcdma implements Parcelable { dest.writeInt(mLac); dest.writeInt(mCid); dest.writeInt(mPsc); - dest.writeInt(mUarfcn); } /** Construct from Parcel, type has already been processed */ @@ -208,7 +178,6 @@ public final class CellIdentityWcdma implements Parcelable { mLac = in.readInt(); mCid = in.readInt(); mPsc = in.readInt(); - mUarfcn = in.readInt(); if (DBG) log("CellIdentityWcdma(Parcel): " + toString()); } diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java index addf7ef025aa..d27fcec38351 100644 --- a/telephony/java/android/telephony/CellSignalStrengthGsm.java +++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java @@ -34,7 +34,6 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P private int mSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5 private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 - private int mTimingAdvance; /** * Empty constructor @@ -76,22 +75,6 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P public void initialize(int ss, int ber) { mSignalStrength = ss; mBitErrorRate = ber; - mTimingAdvance = Integer.MAX_VALUE; - } - - /** - * Initialize all the values - * - * @param ss SignalStrength as ASU value - * @param ber is Bit Error Rate - * @param ta timing advance - * - * @hide - */ - public void initialize(int ss, int ber, int ta) { - mSignalStrength = ss; - mBitErrorRate = ber; - mTimingAdvance = ta; } /** @@ -100,7 +83,6 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P protected void copyFrom(CellSignalStrengthGsm s) { mSignalStrength = s.mSignalStrength; mBitErrorRate = s.mBitErrorRate; - mTimingAdvance = s.mTimingAdvance; } /** @@ -116,7 +98,6 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P public void setDefaultValues() { mSignalStrength = Integer.MAX_VALUE; mBitErrorRate = Integer.MAX_VALUE; - mTimingAdvance = Integer.MAX_VALUE; } /** @@ -193,8 +174,7 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P return false; } - return mSignalStrength == s.mSignalStrength && mBitErrorRate == s.mBitErrorRate && - s.mTimingAdvance == mTimingAdvance; + return mSignalStrength == s.mSignalStrength && mBitErrorRate == s.mBitErrorRate; } /** @@ -204,8 +184,7 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P public String toString() { return "CellSignalStrengthGsm:" + " ss=" + mSignalStrength - + " ber=" + mBitErrorRate - + " mTa=" + mTimingAdvance; + + " ber=" + mBitErrorRate; } /** Implement the Parcelable interface */ @@ -214,7 +193,6 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P if (DBG) log("writeToParcel(Parcel, int): " + toString()); dest.writeInt(mSignalStrength); dest.writeInt(mBitErrorRate); - dest.writeInt(mTimingAdvance); } /** @@ -224,7 +202,6 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P private CellSignalStrengthGsm(Parcel in) { mSignalStrength = in.readInt(); mBitErrorRate = in.readInt(); - mTimingAdvance = in.readInt(); if (DBG) log("CellSignalStrengthGsm(Parcel): " + toString()); } diff --git a/telephony/java/android/telephony/DataConnectionRealTimeInfo.java b/telephony/java/android/telephony/DataConnectionRealTimeInfo.java index 96069213bc13..f71f58d01ea8 100644 --- a/telephony/java/android/telephony/DataConnectionRealTimeInfo.java +++ b/telephony/java/android/telephony/DataConnectionRealTimeInfo.java @@ -28,10 +28,10 @@ import android.os.Parcelable; public class DataConnectionRealTimeInfo implements Parcelable { private long mTime; // Time the info was collected since boot in nanos; - public static int DC_POWER_STATE_LOW = 1; - public static int DC_POWER_STATE_MEDIUM = 2; - public static int DC_POWER_STATE_HIGH = 3; - public static int DC_POWER_STATE_UNKNOWN = Integer.MAX_VALUE; + public static final int DC_POWER_STATE_LOW = 1; + public static final int DC_POWER_STATE_MEDIUM = 2; + public static final int DC_POWER_STATE_HIGH = 3; + public static final int DC_POWER_STATE_UNKNOWN = Integer.MAX_VALUE; private int mDcPowerState; // DC_POWER_STATE_[LOW | MEDIUM | HIGH | UNKNOWN] diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index 553221d579ec..b089387aabd1 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -2082,7 +2082,7 @@ public class PhoneNumberUtils * to read the VM number. */ public static boolean isVoiceMailNumber(String number) { - return isVoiceMailNumber(SubscriptionManager.getDefaultSubId(), number); + return isVoiceMailNumber(SubscriptionManager.getDefaultSubscriptionId(), number); } /** @@ -2977,7 +2977,7 @@ public class PhoneNumberUtils * Returns Default voice subscription Id. */ private static int getDefaultVoiceSubId() { - return SubscriptionManager.getDefaultVoiceSubId(); + return SubscriptionManager.getDefaultVoiceSubscriptionId(); } //==== End of utility methods used only in compareStrictly() ===== } diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java index 16472c814fc6..ae130d4ac34f 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/telephony/java/android/telephony/PhoneStateListener.java @@ -194,10 +194,12 @@ public class PhoneStateListener { * {@more} * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE * READ_PRECISE_PHONE_STATE} - * * @see #onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo) + * + * @deprecated Use {@link TelephonyManager#getModemActivityInfo()} * @hide */ + @Deprecated public static final int LISTEN_DATA_CONNECTION_REAL_TIME_INFO = 0x00002000; /** diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 99989377c374..ff8c71c867c2 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -53,7 +53,6 @@ public class SubscriptionManager { private static final boolean VDBG = false; /** An invalid subscription identifier */ - /** @hide */ public static final int INVALID_SUBSCRIPTION_ID = -1; /** Base value for Dummy SUBSCRIPTION_ID's. */ @@ -455,8 +454,9 @@ public class SubscriptionManager { } /** - * Get the active SubscriptionInfo with the subId key - * @param subId The unique SubscriptionInfo key in database + * Get the active SubscriptionInfo with the input subId. + * + * @param subId The unique SubscriptionInfo key in database. * @return SubscriptionInfo, maybe null if its not active. */ public SubscriptionInfo getActiveSubscriptionInfo(int subId) { @@ -898,12 +898,15 @@ public class SubscriptionManager { } /** - * @return the "system" defaultSubId on a voice capable device this - * will be getDefaultVoiceSubId() and on a data only device it will be - * getDefaultDataSubId(). - * @hide + * Returns the system's default subscription id. + * + * For a voice capable device, it will return getDefaultVoiceSubscriptionId. + * For a data only device, it will return the getDefaultDataSubscriptionId. + * May return an INVALID_SUBSCRIPTION_ID on error. + * + * @return the "system" default subscription id. */ - public static int getDefaultSubId() { + public static int getDefaultSubscriptionId() { int subId = INVALID_SUBSCRIPTION_ID; try { @@ -919,8 +922,14 @@ public class SubscriptionManager { return subId; } - /** @hide */ - public static int getDefaultVoiceSubId() { + /** + * Returns the system's default voice subscription id. + * + * On a data only device or on error, will return INVALID_SUBSCRIPTION_ID. + * + * @return the default voice subscription Id. + */ + public static int getDefaultVoiceSubscriptionId() { int subId = INVALID_SUBSCRIPTION_ID; try { @@ -932,7 +941,7 @@ public class SubscriptionManager { // ignore it } - if (VDBG) logd("getDefaultVoiceSubId, sub id = " + subId); + if (VDBG) logd("getDefaultVoiceSubscriptionId, sub id = " + subId); return subId; } @@ -949,23 +958,31 @@ public class SubscriptionManager { } } - /** @hide */ + /** + * Return the SubscriptionInfo for default voice subscription. + * + * Will return null on data only devices, or on error. + * + * @return the SubscriptionInfo for the default voice subscription. + * @hide + */ public SubscriptionInfo getDefaultVoiceSubscriptionInfo() { - return getActiveSubscriptionInfo(getDefaultVoiceSubId()); + return getActiveSubscriptionInfo(getDefaultVoiceSubscriptionId()); } /** @hide */ public static int getDefaultVoicePhoneId() { - return getPhoneId(getDefaultVoiceSubId()); + return getPhoneId(getDefaultVoiceSubscriptionId()); } /** - * @return subId of the DefaultSms subscription or - * a value < 0 if an error. + * Returns the system's default SMS subscription id. * - * @hide + * On a data only device or on error, will return INVALID_SUBSCRIPTION_ID. + * + * @return the default SMS subscription Id. */ - public static int getDefaultSmsSubId() { + public static int getDefaultSmsSubscriptionId() { int subId = INVALID_SUBSCRIPTION_ID; try { @@ -977,7 +994,7 @@ public class SubscriptionManager { // ignore it } - if (VDBG) logd("getDefaultSmsSubId, sub id = " + subId); + if (VDBG) logd("getDefaultSmsSubscriptionId, sub id = " + subId); return subId; } @@ -994,18 +1011,31 @@ public class SubscriptionManager { } } - /** @hide */ + /** + * Return the SubscriptionInfo for default voice subscription. + * + * Will return null on data only devices, or on error. + * + * @return the SubscriptionInfo for the default SMS subscription. + * @hide + */ public SubscriptionInfo getDefaultSmsSubscriptionInfo() { - return getActiveSubscriptionInfo(getDefaultSmsSubId()); + return getActiveSubscriptionInfo(getDefaultSmsSubscriptionId()); } /** @hide */ public int getDefaultSmsPhoneId() { - return getPhoneId(getDefaultSmsSubId()); + return getPhoneId(getDefaultSmsSubscriptionId()); } - /** @hide */ - public static int getDefaultDataSubId() { + /** + * Returns the system's default data subscription id. + * + * On a voice only device or on error, will return INVALID_SUBSCRIPTION_ID. + * + * @return the default data subscription Id. + */ + public static int getDefaultDataSubscriptionId() { int subId = INVALID_SUBSCRIPTION_ID; try { @@ -1017,7 +1047,7 @@ public class SubscriptionManager { // ignore it } - if (VDBG) logd("getDefaultDataSubId, sub id = " + subId); + if (VDBG) logd("getDefaultDataSubscriptionId, sub id = " + subId); return subId; } @@ -1034,14 +1064,21 @@ public class SubscriptionManager { } } - /** @hide */ + /** + * Return the SubscriptionInfo for default data subscription. + * + * Will return null on voice only devices, or on error. + * + * @return the SubscriptionInfo for the default data subscription. + * @hide + */ public SubscriptionInfo getDefaultDataSubscriptionInfo() { - return getActiveSubscriptionInfo(getDefaultDataSubId()); + return getActiveSubscriptionInfo(getDefaultDataSubscriptionId()); } /** @hide */ public int getDefaultDataPhoneId() { - return getPhoneId(getDefaultDataSubId()); + return getPhoneId(getDefaultDataSubscriptionId()); } /** @hide */ @@ -1061,13 +1098,13 @@ public class SubscriptionManager { //FIXME this is vulnerable to race conditions /** @hide */ public boolean allDefaultsSelected() { - if (!isValidSubscriptionId(getDefaultDataSubId())) { + if (!isValidSubscriptionId(getDefaultDataSubscriptionId())) { return false; } - if (!isValidSubscriptionId(getDefaultSmsSubId())) { + if (!isValidSubscriptionId(getDefaultSmsSubscriptionId())) { return false; } - if (!isValidSubscriptionId(getDefaultVoiceSubId())) { + if (!isValidSubscriptionId(getDefaultVoiceSubscriptionId())) { return false; } return true; diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 7eaa6a69d073..fcb42a48048f 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -1232,7 +1232,6 @@ public class TelephonyManager { * on a CDMA network). * @param subId */ - /** {@hide} */ public String getNetworkOperatorName(int subId) { int phoneId = SubscriptionManager.getPhoneId(subId); return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_ALPHA, ""); @@ -1259,8 +1258,7 @@ public class TelephonyManager { * * @param subId */ - /** {@hide} */ - public String getNetworkOperatorForSubscription(int subId) { + public String getNetworkOperator(int subId) { int phoneId = SubscriptionManager.getPhoneId(subId); return getNetworkOperatorForPhone(phoneId); } @@ -1276,7 +1274,7 @@ public class TelephonyManager { * @param phoneId * @hide **/ - public String getNetworkOperatorForPhone(int phoneId) { + public String getNetworkOperatorForPhone(int phoneId) { return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, ""); } @@ -1298,7 +1296,6 @@ public class TelephonyManager { * * @param subId */ - /** {@hide} */ public boolean isNetworkRoaming(int subId) { int phoneId = SubscriptionManager.getPhoneId(subId); return Boolean.parseBoolean(getTelephonyProperty(phoneId, @@ -1327,8 +1324,7 @@ public class TelephonyManager { * * @param subId for which Network CountryIso is returned */ - /** {@hide} */ - public String getNetworkCountryIsoForSubscription(int subId) { + public String getNetworkCountryIso(int subId) { int phoneId = SubscriptionManager.getPhoneId(subId); return getNetworkCountryIsoForPhone(phoneId); } @@ -1436,7 +1432,6 @@ public class TelephonyManager { * Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} */ - /** {@hide} */ public int getNetworkType(int subId) { try { ITelephony telephony = getITelephony(); @@ -1497,7 +1492,6 @@ public class TelephonyManager { * Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} */ - /** {@hide} */ public int getDataNetworkType(int subId) { try{ ITelephony telephony = getITelephony(); @@ -1535,7 +1529,6 @@ public class TelephonyManager { * Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} */ - /** {@hide} */ public int getVoiceNetworkType(int subId) { try{ ITelephony telephony = getITelephony(); @@ -1804,10 +1797,9 @@ public class TelephonyManager { * @see #getSimState * * @param subId for which SimOperator is returned - * @hide */ public String getSimOperator(int subId) { - return getSimOperatorNumericForSubscription(subId); + return getSimOperatorNumeric(subId); } /** @@ -1820,17 +1812,17 @@ public class TelephonyManager { * @hide */ public String getSimOperatorNumeric() { - int subId = SubscriptionManager.getDefaultDataSubId(); + int subId = SubscriptionManager.getDefaultDataSubscriptionId(); if (!SubscriptionManager.isUsableSubIdValue(subId)) { - subId = SubscriptionManager.getDefaultSmsSubId(); + subId = SubscriptionManager.getDefaultSmsSubscriptionId(); if (!SubscriptionManager.isUsableSubIdValue(subId)) { - subId = SubscriptionManager.getDefaultVoiceSubId(); + subId = SubscriptionManager.getDefaultVoiceSubscriptionId(); if (!SubscriptionManager.isUsableSubIdValue(subId)) { - subId = SubscriptionManager.getDefaultSubId(); + subId = SubscriptionManager.getDefaultSubscriptionId(); } } } - return getSimOperatorNumericForSubscription(subId); + return getSimOperatorNumeric(subId); } /** @@ -1844,7 +1836,7 @@ public class TelephonyManager { * @param subId for which SimOperator is returned * @hide */ - public String getSimOperatorNumericForSubscription(int subId) { + public String getSimOperatorNumeric(int subId) { int phoneId = SubscriptionManager.getPhoneId(subId); return getSimOperatorNumericForPhone(phoneId); } @@ -1881,9 +1873,8 @@ public class TelephonyManager { * @see #getSimState * * @param subId for which SimOperatorName is returned - * @hide */ - public String getSimOperatorNameForSubscription(int subId) { + public String getSimOperatorName(int subId) { int phoneId = SubscriptionManager.getPhoneId(subId); return getSimOperatorNameForPhone(phoneId); } @@ -1909,21 +1900,8 @@ public class TelephonyManager { * Returns the ISO country code equivalent for the SIM provider's country code. * * @param subId for which SimCountryIso is returned - * - * @hide */ public String getSimCountryIso(int subId) { - return getSimCountryIsoForSubscription(subId); - } - - /** - * Returns the ISO country code equivalent for the SIM provider's country code. - * - * @param subId for which SimCountryIso is returned - * - * @hide - */ - public String getSimCountryIsoForSubscription(int subId) { int phoneId = SubscriptionManager.getPhoneId(subId); return getSimCountryIsoForPhone(phoneId); } @@ -1957,7 +1935,6 @@ public class TelephonyManager { * Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} */ - /** {@hide} */ public String getSimSerialNumber(int subId) { try { IPhoneSubInfo info = getSubscriberInfo(); @@ -2046,7 +2023,6 @@ public class TelephonyManager { * * @param subId whose subscriber id is returned */ - /** {@hide} */ public String getSubscriberId(int subId) { try { IPhoneSubInfo info = getSubscriberInfo(); @@ -2089,9 +2065,8 @@ public class TelephonyManager { * Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * - * @param subscription whose subscriber id is returned + * @param subId whose subscriber id is returned */ - /** {@hide} */ public String getGroupIdLevel1(int subId) { try { IPhoneSubInfo info = getSubscriberInfo(); @@ -2118,7 +2093,7 @@ public class TelephonyManager { * The default SMS app can also use this. */ public String getLine1Number() { - return getLine1NumberForSubscriber(getDefaultSubscription()); + return getLine1Number(getDefaultSubscription()); } /** @@ -2134,8 +2109,7 @@ public class TelephonyManager { * * @param subId whose phone number for line 1 is returned */ - /** {@hide} */ - public String getLine1NumberForSubscriber(int subId) { + public String getLine1Number(int subId) { String number = null; try { ITelephony telephony = getITelephony(); @@ -2174,7 +2148,7 @@ public class TelephonyManager { * @return true if the operation was executed correctly. */ public boolean setLine1NumberForDisplay(String alphaTag, String number) { - return setLine1NumberForDisplayForSubscriber(getDefaultSubscription(), alphaTag, number); + return setLine1NumberForDisplay(getDefaultSubscription(), alphaTag, number); } /** @@ -2190,9 +2164,8 @@ public class TelephonyManager { * @param alphaTag alpha-tagging of the dailing nubmer * @param number The dialing number * @return true if the operation was executed correctly. - * @hide */ - public boolean setLine1NumberForDisplayForSubscriber(int subId, String alphaTag, String number) { + public boolean setLine1NumberForDisplay(int subId, String alphaTag, String number) { try { ITelephony telephony = getITelephony(); if (telephony != null) @@ -2213,7 +2186,7 @@ public class TelephonyManager { * nobody seems to call this. */ public String getLine1AlphaTag() { - return getLine1AlphaTagForSubscriber(getDefaultSubscription()); + return getLine1AlphaTag(getDefaultSubscription()); } /** @@ -2226,8 +2199,7 @@ public class TelephonyManager { * @param subId whose alphabetic identifier associated with line 1 is returned * nobody seems to call this. */ - /** {@hide} */ - public String getLine1AlphaTagForSubscriber(int subId) { + public String getLine1AlphaTag(int subId) { String alphaTag = null; try { ITelephony telephony = getITelephony(); @@ -2327,7 +2299,6 @@ public class TelephonyManager { * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * @param subId whose voice mail number is returned */ - /** {@hide} */ public String getVoiceMailNumber(int subId) { try { IPhoneSubInfo info = getSubscriberInfo(); @@ -2400,7 +2371,6 @@ public class TelephonyManager { * @param alphaTag The alpha tag to display. * @param number The voicemail number. */ - /** {@hide} */ public boolean setVoiceMailNumber(int subId, String alphaTag, String number) { try { ITelephony telephony = getITelephony(); @@ -2466,7 +2436,6 @@ public class TelephonyManager { * @param subId whose alphabetic identifier associated with the * voice mail number is returned */ - /** {@hide} */ public String getVoiceMailAlphaTag(int subId) { try { IPhoneSubInfo info = getSubscriberInfo(); @@ -2584,7 +2553,6 @@ public class TelephonyManager { * * @param subId whose call state is returned */ - /** {@hide} */ public int getCallState(int subId) { try { ITelephony telephony = getITelephony(); @@ -3225,19 +3193,19 @@ public class TelephonyManager { * Returns Default subscription. */ private static int getDefaultSubscription() { - return SubscriptionManager.getDefaultSubId(); + return SubscriptionManager.getDefaultSubscriptionId(); } /** * Returns Default phone. */ private static int getDefaultPhone() { - return SubscriptionManager.getPhoneId(SubscriptionManager.getDefaultSubId()); + return SubscriptionManager.getPhoneId(SubscriptionManager.getDefaultSubscriptionId()); } /** {@hide} */ public int getDefaultSim() { - return SubscriptionManager.getSlotId(SubscriptionManager.getDefaultSubId()); + return SubscriptionManager.getSlotId(SubscriptionManager.getDefaultSubscriptionId()); } /** @@ -4182,7 +4150,7 @@ public class TelephonyManager { /** @hide */ @SystemApi public void setDataEnabled(boolean enable) { - setDataEnabled(SubscriptionManager.getDefaultDataSubId(), enable); + setDataEnabled(SubscriptionManager.getDefaultDataSubscriptionId(), enable); } /** @hide */ @@ -4201,7 +4169,7 @@ public class TelephonyManager { /** @hide */ @SystemApi public boolean getDataEnabled() { - return getDataEnabled(SubscriptionManager.getDefaultDataSubId()); + return getDataEnabled(SubscriptionManager.getDefaultDataSubscriptionId()); } /** @hide */ diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java index b9d7297587a1..5f3f773b062c 100644 --- a/telephony/java/com/android/internal/telephony/CallerInfo.java +++ b/telephony/java/com/android/internal/telephony/CallerInfo.java @@ -327,7 +327,7 @@ public class CallerInfo { public static CallerInfo getCallerInfo(Context context, String number) { if (VDBG) Rlog.v(TAG, "getCallerInfo() based on number..."); - int subId = SubscriptionManager.getDefaultSubId(); + int subId = SubscriptionManager.getDefaultSubscriptionId(); return getCallerInfo(context, number, subId); } @@ -444,7 +444,7 @@ public class CallerInfo { // string in the phone number field. /* package */ CallerInfo markAsVoiceMail() { - int subId = SubscriptionManager.getDefaultSubId(); + int subId = SubscriptionManager.getDefaultSubscriptionId(); return markAsVoiceMail(subId); } diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java index c75406804a09..05cb31e1b7a3 100644 --- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java +++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java @@ -387,7 +387,7 @@ public class CallerInfoAsyncQuery { public static CallerInfoAsyncQuery startQuery(int token, Context context, String number, OnQueryCompleteListener listener, Object cookie) { - int subId = SubscriptionManager.getDefaultSubId(); + int subId = SubscriptionManager.getDefaultSubscriptionId(); return startQuery(token, context, number, listener, cookie, subId); } diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl index 76b69cea8ae6..907d76e816c0 100644 --- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -21,7 +21,6 @@ import android.net.LinkProperties; import android.net.NetworkCapabilities; import android.os.Bundle; import android.telephony.CellInfo; -import android.telephony.DataConnectionRealTimeInfo; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.CellInfo; @@ -65,7 +64,6 @@ interface ITelephonyRegistry { void notifyPreciseDataConnectionFailed(String reason, String apnType, String apn, String failCause); void notifyCellInfoForSubscriber(in int subId, in List<CellInfo> cellInfo); - void notifyDataConnectionRealTimeInfo(in DataConnectionRealTimeInfo dcRtInfo); void notifyVoLteServiceStateChanged(in VoLteServiceState lteState); void notifyOemHookRawEventForSubscriber(in int subId, in byte[] rawData); void notifySubscriptionInfoChanged(); diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java index 0c360631c294..4c12c2d5da7c 100644 --- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java +++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java @@ -16,8 +16,9 @@ package android.security.net.config; -import java.util.Set; +import android.util.ArraySet; import java.security.cert.X509Certificate; +import java.util.Set; import com.android.org.conscrypt.TrustedCertificateIndex; @@ -33,10 +34,12 @@ public class TestCertificateSource implements CertificateSource { } } + @Override public Set<X509Certificate> getCertificates() { return mCertificates; } + @Override public X509Certificate findBySubjectAndPublicKey(X509Certificate cert) { java.security.cert.TrustAnchor anchor = mIndex.findBySubjectAndPublicKey(cert); if (anchor == null) { @@ -45,6 +48,7 @@ public class TestCertificateSource implements CertificateSource { return anchor.getTrustedCert(); } + @Override public X509Certificate findByIssuerAndSignature(X509Certificate cert) { java.security.cert.TrustAnchor anchor = mIndex.findByIssuerAndSignature(cert); if (anchor == null) { @@ -52,4 +56,13 @@ public class TestCertificateSource implements CertificateSource { } return anchor.getTrustedCert(); } + + @Override + public Set<X509Certificate> findAllByIssuerAndSignature(X509Certificate cert) { + Set<X509Certificate> certs = new ArraySet<X509Certificate>(); + for (java.security.cert.TrustAnchor anchor : mIndex.findAllByIssuerAndSignature(cert)) { + certs.add(anchor.getTrustedCert()); + } + return certs; + } } diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp index f3a9ea319216..3ecb2c4113d5 100644 --- a/tools/aapt2/link/Link.cpp +++ b/tools/aapt2/link/Link.cpp @@ -907,6 +907,8 @@ int link(const std::vector<StringPiece>& args) { Maybe<std::string> versionCode, versionName; Maybe<std::string> customJavaPackage; std::vector<std::string> extraJavaPackages; + bool legacyXFlag = false; + bool requireLocalization = false; Flags flags = Flags() .requiredFlag("-o", "Output path", &options.outputPath) .requiredFlag("--manifest", "Path to the Android manifest to build", @@ -922,6 +924,10 @@ int link(const std::vector<StringPiece>& args) { .optionalSwitch("--no-auto-version", "Disables automatic style and layout SDK versioning", &options.noAutoVersion) + .optionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01", + &legacyXFlag) + .optionalSwitch("-z", "Require localization of strings marked 'suggested'", + &requireLocalization) .optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified " "by -o", &options.outputToDirectory) diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java index 6e423911f09c..605e8c0b0b1e 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -15,12 +15,14 @@ */ package android.net.wifi; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.security.Credentials; import android.text.TextUtils; import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; @@ -72,6 +74,13 @@ public class WifiEnterpriseConfig implements Parcelable { public static final String KEYSTORE_URI = "keystore://"; /** + * String representing the keystore URI used for wpa_supplicant, + * Unlike #KEYSTORE_URI, this supports a list of space-delimited aliases + * @hide + */ + public static final String KEYSTORES_URI = "keystores://"; + + /** * String to set the engine value to when it should be enabled. * @hide */ @@ -101,10 +110,12 @@ public class WifiEnterpriseConfig implements Parcelable { public static final String REALM_KEY = "realm"; /** @hide */ public static final String PLMN_KEY = "plmn"; + /** @hide */ + public static final String CA_CERT_ALIAS_DELIMITER = " "; private HashMap<String, String> mFields = new HashMap<String, String>(); - private X509Certificate mCaCert; + private X509Certificate[] mCaCerts; private PrivateKey mClientPrivateKey; private X509Certificate mClientCertificate; @@ -136,7 +147,7 @@ public class WifiEnterpriseConfig implements Parcelable { dest.writeString(entry.getValue()); } - writeCertificate(dest, mCaCert); + writeCertificates(dest, mCaCerts); if (mClientPrivateKey != null) { String algorithm = mClientPrivateKey.getAlgorithm(); @@ -151,6 +162,17 @@ public class WifiEnterpriseConfig implements Parcelable { writeCertificate(dest, mClientCertificate); } + private void writeCertificates(Parcel dest, X509Certificate[] cert) { + if (cert != null && cert.length != 0) { + dest.writeInt(cert.length); + for (int i = 0; i < cert.length; i++) { + writeCertificate(dest, cert[i]); + } + } else { + dest.writeInt(0); + } + } + private void writeCertificate(Parcel dest, X509Certificate cert) { if (cert != null) { try { @@ -176,7 +198,7 @@ public class WifiEnterpriseConfig implements Parcelable { enterpriseConfig.mFields.put(key, value); } - enterpriseConfig.mCaCert = readCertificate(in); + enterpriseConfig.mCaCerts = readCertificates(in); PrivateKey userKey = null; int len = in.readInt(); @@ -199,6 +221,18 @@ public class WifiEnterpriseConfig implements Parcelable { return enterpriseConfig; } + private X509Certificate[] readCertificates(Parcel in) { + X509Certificate[] certs = null; + int len = in.readInt(); + if (len > 0) { + certs = new X509Certificate[len]; + for (int i = 0; i < len; i++) { + certs[i] = readCertificate(in); + } + } + return certs; + } + private X509Certificate readCertificate(Parcel in) { X509Certificate cert = null; int len = in.readInt(); @@ -399,6 +433,36 @@ public class WifiEnterpriseConfig implements Parcelable { } /** + * Encode a CA certificate alias so it does not contain illegal character. + * @hide + */ + public static String encodeCaCertificateAlias(String alias) { + byte[] bytes = alias.getBytes(StandardCharsets.UTF_8); + StringBuilder sb = new StringBuilder(bytes.length * 2); + for (byte o : bytes) { + sb.append(String.format("%02x", o & 0xFF)); + } + return sb.toString(); + } + + /** + * Decode a previously-encoded CA certificate alias. + * @hide + */ + public static String decodeCaCertificateAlias(String alias) { + byte[] data = new byte[alias.length() >> 1]; + for (int n = 0, position = 0; n < alias.length(); n += 2, position++) { + data[position] = (byte) Integer.parseInt(alias.substring(n, n + 2), 16); + } + try { + return new String(data, StandardCharsets.UTF_8); + } catch (NumberFormatException e) { + e.printStackTrace(); + return alias; + } + } + + /** * Set CA certificate alias. * * <p> See the {@link android.security.KeyChain} for details on installing or choosing @@ -412,6 +476,35 @@ public class WifiEnterpriseConfig implements Parcelable { } /** + * Set CA certificate aliases. When creating installing the corresponding certificate to + * the keystore, please use alias encoded by {@link #encodeCaCertificateAlias(String)}. + * + * <p> See the {@link android.security.KeyChain} for details on installing or choosing + * a certificate. + * </p> + * @param aliases identifies the certificate + * @hide + */ + public void setCaCertificateAliases(@Nullable String[] aliases) { + if (aliases == null) { + setFieldValue(CA_CERT_KEY, null, CA_CERT_PREFIX); + } else if (aliases.length == 1) { + // Backwards compatibility: use the original cert prefix if setting only one alias. + setCaCertificateAlias(aliases[0]); + } else { + // Use KEYSTORES_URI which supports multiple aliases. + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < aliases.length; i++) { + if (i > 0) { + sb.append(CA_CERT_ALIAS_DELIMITER); + } + sb.append(encodeCaCertificateAlias(Credentials.CA_CERTIFICATE + aliases[i])); + } + setFieldValue(CA_CERT_KEY, sb.toString(), KEYSTORES_URI); + } + } + + /** * Get CA certificate alias * @return alias to the CA certificate * @hide @@ -421,6 +514,32 @@ public class WifiEnterpriseConfig implements Parcelable { } /** + * Get CA certificate aliases + * @return alias to the CA certificate + * @hide + */ + @Nullable public String[] getCaCertificateAliases() { + String value = getFieldValue(CA_CERT_KEY, ""); + if (value.startsWith(CA_CERT_PREFIX)) { + // Backwards compatibility: parse the original alias prefix. + return new String[] {getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX)}; + } else if (value.startsWith(KEYSTORES_URI)) { + String values = value.substring(KEYSTORES_URI.length()); + + String[] aliases = TextUtils.split(values, CA_CERT_ALIAS_DELIMITER); + for (int i = 0; i < aliases.length; i++) { + aliases[i] = decodeCaCertificateAlias(aliases[i]); + if (aliases[i].startsWith(Credentials.CA_CERTIFICATE)) { + aliases[i] = aliases[i].substring(Credentials.CA_CERTIFICATE.length()); + } + } + return aliases.length != 0 ? aliases : null; + } else { + return TextUtils.isEmpty(value) ? null : new String[] {value}; + } + } + + /** * Specify a X.509 certificate that identifies the server. * * <p>A default name is automatically assigned to the certificate and used @@ -431,31 +550,76 @@ public class WifiEnterpriseConfig implements Parcelable { * @param cert X.509 CA certificate * @throws IllegalArgumentException if not a CA certificate */ - public void setCaCertificate(X509Certificate cert) { + public void setCaCertificate(@Nullable X509Certificate cert) { if (cert != null) { if (cert.getBasicConstraints() >= 0) { - mCaCert = cert; + mCaCerts = new X509Certificate[] {cert}; } else { throw new IllegalArgumentException("Not a CA certificate"); } } else { - mCaCert = null; + mCaCerts = null; } } /** - * Get CA certificate + * Get CA certificate. If multiple CA certificates are configured previously, + * return the first one. * @return X.509 CA certificate */ - public X509Certificate getCaCertificate() { - return mCaCert; + @Nullable public X509Certificate getCaCertificate() { + if (mCaCerts != null && mCaCerts.length > 0) { + return mCaCerts[0]; + } else { + return null; + } + } + + /** + * Specify a list of X.509 certificates that identifies the server. The validation + * passes if the CA of server certificate matches one of the given certificates. + + * <p>Default names are automatically assigned to the certificates and used + * with this configuration. The framework takes care of installing the + * certificates when the config is saved and removing the certificates when + * the config is removed. + * + * @param certs X.509 CA certificates + * @throws IllegalArgumentException if any of the provided certificates is + * not a CA certificate + */ + public void setCaCertificates(@Nullable X509Certificate[] certs) { + if (certs != null) { + X509Certificate[] newCerts = new X509Certificate[certs.length]; + for (int i = 0; i < certs.length; i++) { + if (certs[i].getBasicConstraints() >= 0) { + newCerts[i] = certs[i]; + } else { + throw new IllegalArgumentException("Not a CA certificate"); + } + } + mCaCerts = newCerts; + } else { + mCaCerts = null; + } + } + + /** + * Get CA certificates. + */ + @Nullable public X509Certificate[] getCaCertificates() { + if (mCaCerts != null || mCaCerts.length > 0) { + return mCaCerts; + } else { + return null; + } } /** * @hide */ public void resetCaCertificate() { - mCaCert = null; + mCaCerts = null; } /** Set Client certificate alias. diff --git a/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl b/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl deleted file mode 100644 index 50bec33ea6d2..000000000000 --- a/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (c) 2014, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.wifi.passpoint; - -import android.net.wifi.ScanResult; -import android.net.wifi.passpoint.WifiPasspointPolicy; -import android.net.wifi.passpoint.WifiPasspointCredential; -import android.os.Messenger; - -/** - * Interface that allows controlling and querying Wifi Passpoint connectivity. - * - * {@hide} - */ -interface IWifiPasspointManager -{ - Messenger getMessenger(); - - int getPasspointState(); - - List<WifiPasspointPolicy> requestCredentialMatch(in List<ScanResult> requested); - - List<WifiPasspointCredential> getCredentials(); - - boolean addCredential(in WifiPasspointCredential cred); - - boolean updateCredential(in WifiPasspointCredential cred); - - boolean removeCredential(in WifiPasspointCredential cred); -} - diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java deleted file mode 100644 index a100aedb3d1d..000000000000 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java +++ /dev/null @@ -1,665 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.wifi.passpoint; - -import android.net.wifi.WifiEnterpriseConfig; -import android.os.Parcelable; -import android.os.Parcel; - -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - - -/** - * A class representing a Wi-Fi Passpoint credential. - * @hide - */ -public class WifiPasspointCredential implements Parcelable { - - private final static String TAG = "PasspointCredential"; - private final static boolean DBG = true; - - /** Wi-Fi nodes**/ - private String mWifiSpFqdn; - - /** PerProviderSubscription nodes **/ - private String mCredentialName; - - /** SubscriptionUpdate nodes **/ - private String mSubscriptionUpdateInterval; - private String mSubscriptionUpdateMethod; - private String mSubscriptionUpdateRestriction; - private String mSubscriptionUpdateURI; - private String mSubscriptionUpdateUsername; - private String mSubscriptionUpdatePassword; - - /** HomeSP nodes **/ - private String mHomeSpFqdn; - private String mFriendlyName; - private Collection<WifiPasspointDmTree.HomeOIList> mHomeOIList; - private Collection<WifiPasspointDmTree.OtherHomePartners> mOtherHomePartnerList; - - /** SubscriptionParameters nodes**/ - private String mCreationDate; - private String mExpirationDate; - - /** Credential nodes **/ - private String mType; - private String mInnerMethod; - private String mCertType; - private String mCertSha256Fingerprint; - private String mUpdateIdentifier; - private String mUsername; - private String mPasswd; - private String mRealm; - private String mImsi; - private String mMcc; - private String mMnc; - private String mCaRootCert; - private String mClientCert; - private boolean mCheckAaaServerCertStatus; - - /** Policy nodes **/ - private String mPolicyUpdateUri; - private String mPolicyUpdateInterval; - private String mPolicyUpdateUsername; - private String mPolicyUpdatePassword; - private String mPolicyUpdateRestriction; - private String mPolicyUpdateMethod; - private Collection<WifiPasspointDmTree.PreferredRoamingPartnerList> mPreferredRoamingPartnerList; - private Collection<WifiPasspointDmTree.MinBackhaulThresholdNetwork> mMinBackhaulThresholdNetwork; - private Collection<WifiPasspointDmTree.SPExclusionList> mSpExclusionList; - private Collection<WifiPasspointDmTree.RequiredProtoPortTuple> mRequiredProtoPortTuple; - private String mMaxBssLoad; - - /** CrednetialPriority node **/ - private int mCrednetialPriority; - - /** AAAServerTrustRoot nodes **/ - private String mAaaCertUrl; - private String mAaaSha256Fingerprint; - - /** Others **/ - private boolean mIsMachineRemediation; - private boolean mUserPreferred = false; - private String mWifiTreePath; - private WifiEnterpriseConfig mEnterpriseConfig; - - /** @hide */ - public WifiPasspointCredential() {} - - /** - * Constructor - * @param realm Realm of the passpoint credential - * @param fqdn Fully qualified domain name (FQDN) of the credential - * @param config Enterprise config, must be either EAP-TLS or EAP-TTLS - * @see WifiEnterpriseConfig - */ - public WifiPasspointCredential(String realm, String fqdn, WifiEnterpriseConfig config) { - mRealm = realm; - switch (config.getEapMethod()) { - case WifiEnterpriseConfig.Eap.TLS: - case WifiEnterpriseConfig.Eap.TTLS: - mEnterpriseConfig = new WifiEnterpriseConfig(config); - break; - default: - // ignore - } - } - - /** @hide */ - public WifiPasspointCredential(String type, - String caroot, - String clientcert, - String mcc, - String mnc, - WifiPasspointDmTree.SpFqdn sp, - WifiPasspointDmTree.CredentialInfo credinfo) { - - if (credinfo == null) { - return; - } - - mType = type; - mCaRootCert = caroot; - mClientCert = clientcert; - - mWifiSpFqdn = sp.nodeName; - mUpdateIdentifier = sp.perProviderSubscription.UpdateIdentifier; - - mCredentialName = credinfo.nodeName; - mOtherHomePartnerList = credinfo.homeSP.otherHomePartners.values(); - - Set set = credinfo.aAAServerTrustRoot.entrySet(); - Iterator i = set.iterator(); - if (i.hasNext()) { - Map.Entry entry3 = (Map.Entry) i.next(); - WifiPasspointDmTree.AAAServerTrustRoot aaa = (WifiPasspointDmTree.AAAServerTrustRoot) entry3.getValue(); - mAaaCertUrl = aaa.CertURL; - mAaaSha256Fingerprint = aaa.CertSHA256Fingerprint; - } - - mCertType = credinfo.credential.digitalCertificate.CertificateType; - mCertSha256Fingerprint = credinfo.credential.digitalCertificate.CertSHA256Fingerprint; - mUsername = credinfo.credential.usernamePassword.Username; - mPasswd = credinfo.credential.usernamePassword.Password; - mIsMachineRemediation = credinfo.credential.usernamePassword.MachineManaged; - mInnerMethod = credinfo.credential.usernamePassword.eAPMethod.InnerMethod; - mImsi = credinfo.credential.sim.IMSI; - mMcc = mcc; - mMnc = mnc; - mCreationDate = credinfo.credential.CreationDate; - mExpirationDate = credinfo.credential.ExpirationDate; - mRealm = credinfo.credential.Realm; - - if (credinfo.credentialPriority == null) { - mCrednetialPriority = 128; - } else { - mCrednetialPriority = Integer.parseInt(credinfo.credentialPriority); - } - - mHomeSpFqdn = credinfo.homeSP.FQDN; - - mSubscriptionUpdateInterval = credinfo.subscriptionUpdate.UpdateInterval; - mSubscriptionUpdateMethod = credinfo.subscriptionUpdate.UpdateMethod; - mSubscriptionUpdateRestriction = credinfo.subscriptionUpdate.Restriction; - mSubscriptionUpdateURI = credinfo.subscriptionUpdate.URI; - mSubscriptionUpdateUsername = credinfo.subscriptionUpdate.usernamePassword.Username; - mSubscriptionUpdatePassword = credinfo.subscriptionUpdate.usernamePassword.Password; - - mPolicyUpdateUri = credinfo.policy.policyUpdate.URI; - mPolicyUpdateInterval = credinfo.policy.policyUpdate.UpdateInterval; - mPolicyUpdateUsername = credinfo.policy.policyUpdate.usernamePassword.Username; - mPolicyUpdatePassword = credinfo.policy.policyUpdate.usernamePassword.Password; - mPolicyUpdateRestriction = credinfo.policy.policyUpdate.Restriction; - mPolicyUpdateMethod = credinfo.policy.policyUpdate.UpdateMethod; - mPreferredRoamingPartnerList = credinfo.policy.preferredRoamingPartnerList.values(); - mMinBackhaulThresholdNetwork = credinfo.policy.minBackhaulThreshold.values(); - mRequiredProtoPortTuple = credinfo.policy.requiredProtoPortTuple.values(); - mMaxBssLoad = credinfo.policy.maximumBSSLoadValue; - mSpExclusionList = credinfo.policy.sPExclusionList.values(); - - mHomeOIList = credinfo.homeSP.homeOIList.values(); - mFriendlyName = credinfo.homeSP.FriendlyName; - mCheckAaaServerCertStatus = credinfo.credential.CheckAAAServerCertStatus; - } - - /** @hide */ - public String getUpdateIdentifier() { - return mUpdateIdentifier; - } - - /** @hide */ - public String getUpdateMethod() { - return mSubscriptionUpdateMethod; - } - - /** @hide */ - public void setUpdateMethod(String method) { - mSubscriptionUpdateMethod = method; - } - - /** @hide */ - public String getWifiSpFqdn() { - return mWifiSpFqdn; - } - - /** @hide */ - public String getCredName() { - return mCredentialName; - } - - /** @hide */ - public String getType() { - return mType; - } - - /** - * Get enterprise config of this Passpoint credential. - * @return Enterprise config - * @see WifiEnterpriseConfig - */ - public WifiEnterpriseConfig getEnterpriseConfig() { - return new WifiEnterpriseConfig(mEnterpriseConfig); - } - - /** - * Set enterprise config of this Passpoint credential. - * @param config Enterprise config, must be either EAP-TLS or EAP-TTLS - * @see WifiEnterpriseConfig - */ - public void setEnterpriseConfig(WifiEnterpriseConfig config) { - // TODO - } - - /** @hide */ - public String getCertType() { - return mCertType; - } - - /** @hide */ - public String getCertSha256Fingerprint() { - return mCertSha256Fingerprint; - } - - /** @hide */ - public String getUserName() { - return mUsername; - } - - /** @hide */ - public String getPassword() { - // TODO: guarded by connectivity internal - return mPasswd; - } - - /** @hide */ - public String getImsi() { - return mImsi; - } - - /** @hide */ - public String getMcc() { - return mMcc; - } - - /** @hide */ - public String getMnc() { - return mMnc; - } - - /** @hide */ - public String getCaRootCertPath() { - return mCaRootCert; - } - - /** @hide */ - public String getClientCertPath() { - return mClientCert; - } - - /** - * Get the realm of this Passpoint credential. - * @return Realm - */ - public String getRealm() { - return mRealm; - } - - /** - * Set the ream of this Passpoint credential. - * @param realm Realm - */ - public void setRealm(String realm) { - mRealm = realm; - } - - /** @hide */ - public int getPriority() { - if (mUserPreferred) { - return 0; - } - - return mCrednetialPriority; - } - - /** - * Get the fully qualified domain name (FQDN) of this Passpoint credential. - * @return FQDN - */ - public String getHomeSpFqdn() { - return mHomeSpFqdn; - } - - /** - * Set the fully qualified domain name (FQDN) of this Passpoint credential. - * @param fqdn FQDN - */ - public void setHomeFqdn(String fqdn) { - mHomeSpFqdn = fqdn; - } - - - /** @hide */ - public Collection<WifiPasspointDmTree.OtherHomePartners> getOtherHomePartnerList() { - return mOtherHomePartnerList; - } - - /** @hide */ - public String getSubscriptionUpdateUsername() { - return mSubscriptionUpdateUsername; - } - - /** @hide */ - public String getSubscriptionUpdatePassword() { - return mSubscriptionUpdatePassword; - } - - /** @hide */ - public String getPolicyUpdateUri() { - return mPolicyUpdateUri; - } - - /** @hide */ - public String getPolicyUpdateInterval() { - return mPolicyUpdateInterval; - } - - /** @hide */ - public String getPolicyUpdateUsername() { - return mPolicyUpdateUsername; - } - - /** @hide */ - public String getPolicyUpdatePassword() { - return mPolicyUpdatePassword; - } - - /** @hide */ - public String getPolicyUpdateRestriction() { - return mPolicyUpdateRestriction; - } - - /** @hide */ - public String getPolicyUpdateMethod() { - return mPolicyUpdateMethod; - } - - /** @hide */ - public String getCreationDate() { - return mCreationDate; - } - - /** @hide */ - public String getExpirationDate() { - return mExpirationDate; - } - - /** @hide */ - public void setExpirationDate(String expirationdate) { - mExpirationDate = expirationdate; - } - - /** @hide */ - public Collection<WifiPasspointDmTree.PreferredRoamingPartnerList> getPreferredRoamingPartnerList() { - return mPreferredRoamingPartnerList; - } - - /** @hide */ - public Collection<WifiPasspointDmTree.HomeOIList> getHomeOiList() { - return mHomeOIList; - } - - /** @hide */ - public Collection<WifiPasspointDmTree.MinBackhaulThresholdNetwork> getBackhaulThresholdList() { - return mMinBackhaulThresholdNetwork; - } - - /** @hide */ - public Collection<WifiPasspointDmTree.RequiredProtoPortTuple> getRequiredProtoPortList() { - return mRequiredProtoPortTuple; - } - - /** @hide */ - public Collection<WifiPasspointDmTree.SPExclusionList> getSPExclusionList() { - return mSpExclusionList; - } - - /** @hide */ - public boolean getIsMachineRemediation() { - return mIsMachineRemediation; - } - - /** @hide */ - public String getAaaCertUrl() { - return mAaaCertUrl; - } - - /** @hide */ - public String getAaaSha256Fingerprint() { - return mAaaSha256Fingerprint; - } - - /** @hide */ - public String getSubscriptionUpdateRestriction() { - return mSubscriptionUpdateRestriction; - } - - /** @hide */ - public String getSubscriptionUpdateURI() { - return mSubscriptionUpdateURI; - } - - /** @hide */ - public String getSubscriptionUpdateInterval() { - return mSubscriptionUpdateInterval; - } - - /** @hide */ - public String getFriendlyName() { - return mFriendlyName; - } - - /** @hide */ - public String getMaxBssLoad() { - return mMaxBssLoad; - } - - /** @hide */ - public boolean getUserPreference() { - return mUserPreferred; - } - - /** @hide */ - public boolean getCheckAaaServerCertStatus() { - return mCheckAaaServerCertStatus; - } - - /** @hide */ - public void setUserPreference(boolean value) { - mUserPreferred = value; - } - - @Override - /** @hide */ - public boolean equals(Object obj) { - boolean result = false; - if (obj instanceof WifiPasspointCredential) { - final WifiPasspointCredential other = (WifiPasspointCredential) obj; - if (this.mType.equals(other.mType)) { - if (this.mType.equals("TTLS")) { - result = this.mUsername.equals(other.mUsername) && - this.mPasswd.equals(other.mPasswd) && - this.mRealm.equals(other.mRealm) && - this.mHomeSpFqdn.equals(other.mHomeSpFqdn); - } - if (this.mType.equals("TLS")) { - result = this.mRealm.equals(other.mRealm) && - this.mHomeSpFqdn.equals(other.mHomeSpFqdn) && - this.mClientCert.equals(other.mClientCert); - } - if (this.mType.equals("SIM")) { - result = this.mMcc.equals(other.mMcc) && - this.mMnc.equals(other.mMnc) && - this.mImsi.equals(other.mImsi) && - this.mHomeSpFqdn.equals(other.mHomeSpFqdn); - } - } - } - return result; - } - - @Override - /** @hide */ - public String toString() { - StringBuffer sb = new StringBuffer(); - String none = "<none>"; - - if (!DBG) { - sb.append(none); - } else { - sb.append(", UpdateIdentifier: ") - .append(mUpdateIdentifier == null ? none : mUpdateIdentifier) - .append(", SubscriptionUpdateMethod: ") - .append(mSubscriptionUpdateMethod == null ? none : mSubscriptionUpdateMethod) - .append(", Type: ").append(mType == null ? none : mType) - .append(", Username: ").append(mUsername == null ? none : mUsername) - .append(", Passwd: ").append(mPasswd == null ? none : mPasswd) - .append(", SubDMAccUsername: ") - .append(mSubscriptionUpdateUsername == null ? none : mSubscriptionUpdateUsername) - .append(", SubDMAccPassword: ") - .append(mSubscriptionUpdatePassword == null ? none : mSubscriptionUpdatePassword) - .append(", PolDMAccUsername: ") - .append(mPolicyUpdateUsername == null ? none : mPolicyUpdateUsername) - .append(", PolDMAccPassword: ") - .append(mPolicyUpdatePassword == null ? none : mPolicyUpdatePassword) - .append(", Imsi: ").append(mImsi == null ? none : mImsi) - .append(", Mcc: ").append(mMcc == null ? none : mMcc) - .append(", Mnc: ").append(mMnc == null ? none : mMnc) - .append(", CaRootCert: ").append(mCaRootCert == null ? none : mCaRootCert) - .append(", Realm: ").append(mRealm == null ? none : mRealm) - .append(", Priority: ").append(mCrednetialPriority) - .append(", Fqdn: ").append(mHomeSpFqdn == null ? none : mHomeSpFqdn) - .append(", Otherhomepartners: ") - .append(mOtherHomePartnerList == null ? none : mOtherHomePartnerList) - .append(", ExpirationDate: ") - .append(mExpirationDate == null ? none : mExpirationDate) - .append(", MaxBssLoad: ").append(mMaxBssLoad == null ? none : mMaxBssLoad) - .append(", SPExclusionList: ").append(mSpExclusionList); - - if (mPreferredRoamingPartnerList != null) { - sb.append("PreferredRoamingPartnerList:"); - for (WifiPasspointDmTree.PreferredRoamingPartnerList prpListItem : mPreferredRoamingPartnerList) { - sb.append("[fqdnmatch:").append(prpListItem.FQDN_Match). - append(", priority:").append(prpListItem.Priority). - append(", country:").append(prpListItem.Country).append("]"); - } - } - - if (mHomeOIList != null) { - sb.append("HomeOIList:"); - for (WifiPasspointDmTree.HomeOIList HomeOIListItem : mHomeOIList) { - sb.append("[HomeOI:").append(HomeOIListItem.HomeOI). - append(", HomeOIRequired:").append(HomeOIListItem.HomeOIRequired). - append("]"); - } - } - - if (mMinBackhaulThresholdNetwork != null) { - sb.append("BackHaulThreshold:"); - for (WifiPasspointDmTree.MinBackhaulThresholdNetwork BhtListItem : mMinBackhaulThresholdNetwork) { - sb.append("[networkType:").append(BhtListItem.NetworkType). - append(", dlBandwidth:").append(BhtListItem.DLBandwidth). - append(", ulBandwidth:").append(BhtListItem.ULBandwidth). - append("]"); - } - } - - if (mRequiredProtoPortTuple != null) { - sb.append("WifiMORequiredProtoPortTupleList:"); - for (WifiPasspointDmTree.RequiredProtoPortTuple RpptListItem : mRequiredProtoPortTuple) { - sb.append("[IPProtocol:").append(RpptListItem.IPProtocol). - append(", PortNumber:").append(RpptListItem.PortNumber). - append("]"); - } - } - } - return sb.toString(); - } - - /** Implement the Parcelable interface {@hide} */ - public int describeContents() { - return 0; - } - - /** Implement the Parcelable interface {@hide} */ - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(mWifiSpFqdn); - dest.writeString(mCredentialName); - dest.writeString(mType); - dest.writeInt(mCrednetialPriority); - dest.writeString(mHomeSpFqdn); - dest.writeString(mRealm); - } - - /** Implement the Parcelable interface {@hide} */ - public void readFromParcel(Parcel in) { - mWifiSpFqdn = in.readString(); - mCredentialName = in.readString(); - mType = in.readString(); - mCrednetialPriority = in.readInt(); - mHomeSpFqdn = in.readString(); - mRealm = in.readString(); - } - - /** Implement the Parcelable interface {@hide} */ - public static final Creator<WifiPasspointCredential> CREATOR = - new Creator<WifiPasspointCredential>() { - public WifiPasspointCredential createFromParcel(Parcel in) { - WifiPasspointCredential pc = new WifiPasspointCredential(); - pc.mWifiSpFqdn = in.readString(); - pc.mCredentialName = in.readString(); - pc.mType = in.readString(); - pc.mCrednetialPriority = in.readInt(); - pc.mHomeSpFqdn = in.readString(); - pc.mRealm = in.readString(); - return pc; - } - - public WifiPasspointCredential[] newArray(int size) { - return new WifiPasspointCredential[size]; - } - }; - - /** @hide */ - public int compareTo(WifiPasspointCredential another) { - - //The smaller the higher - if (mCrednetialPriority < another.mCrednetialPriority) { - return -1; - } else if (mCrednetialPriority == another.mCrednetialPriority) { - return this.mType.compareTo(another.mType); - } else { - return 1; - } - } - - @Override - /** @hide */ - public int hashCode() { - int hash = 208; - if (mType != null) { - hash += mType.hashCode(); - } - if (mRealm != null) { - hash += mRealm.hashCode(); - } - if (mHomeSpFqdn != null) { - hash += mHomeSpFqdn.hashCode(); - } - if (mUsername != null) { - hash += mUsername.hashCode(); - } - if (mPasswd != null) { - hash += mPasswd.hashCode(); - } - - return hash; - } -} diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java deleted file mode 100644 index 427c84c78b56..000000000000 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java +++ /dev/null @@ -1,1375 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.wifi.passpoint; - -import android.os.Parcelable; -import android.os.Parcel; -import java.util.HashMap; - -/** - * Required Mobile Device Management Tree Structure - * - * +----------+ - * | ./(Root) | - * +----+-----+ - * | - * +---------+ | +---------+ +---------+ - * | DevInfo |-----------+---------| Wi-Fi |--|SP FQDN* | - * +---------+ | +---------+ +---------+ - * +---------+ | | - * |DevDetail|-----------+ +-----------------------+ - * +---------+ |PerproviderSubscription|--<X>+ - * +-----------------------+ - * - * This class contains all nodes start from Wi-Fi - * @hide - **/ -public class WifiPasspointDmTree implements Parcelable { - private final static String TAG = "WifiTree"; - public int PpsMoId;//plugfest used only - public HashMap<String, SpFqdn> spFqdn = new HashMap<String, SpFqdn>();//Maps.newHashMap(); - - public SpFqdn createSpFqdn(String name) { - SpFqdn obj = new SpFqdn(name); - spFqdn.put(name, obj); - return obj; - } - - public static class SpFqdn implements Parcelable { - public String nodeName; - public PerProviderSubscription perProviderSubscription = new PerProviderSubscription(); - - public SpFqdn(String name) { - nodeName = name; - } - - public SpFqdn() { - } - - public SpFqdn(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeString(nodeName); - out.writeParcelable(perProviderSubscription, flags); - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - nodeName = in.readString(); - perProviderSubscription = in.readParcelable(PerProviderSubscription.class - .getClassLoader()); - } - } - - public static final Parcelable.Creator<SpFqdn> CREATOR = new Parcelable.Creator<SpFqdn>() { - public SpFqdn createFromParcel(Parcel in) { - return new SpFqdn(in); - } - - public SpFqdn[] newArray(int size) { - return new SpFqdn[size]; - } - }; - } - - /** - * PerProviderSubscription - **/ - public static class PerProviderSubscription implements Parcelable { - /** - * PerProviderSubscription/UpdateIdentifier - **/ - public String UpdateIdentifier; - public HashMap<String, CredentialInfo> credentialInfo = new HashMap<String, CredentialInfo>(); - - public CredentialInfo createCredentialInfo(String name) { - CredentialInfo obj = new CredentialInfo(name); - credentialInfo.put(name, obj); - return obj; - } - - public PerProviderSubscription() { - } - - public PerProviderSubscription(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeString(UpdateIdentifier); - out.writeMap(credentialInfo); - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - UpdateIdentifier = in.readString(); - in.readMap(credentialInfo, CredentialInfo.class.getClassLoader()); - } - } - - public static final Parcelable.Creator<PerProviderSubscription> CREATOR = new Parcelable.Creator<PerProviderSubscription>() { - public PerProviderSubscription createFromParcel(Parcel in) { - return new PerProviderSubscription(in); - } - - public PerProviderSubscription[] newArray(int size) { - return new PerProviderSubscription[size]; - } - }; - - } - - /** - * PerProviderSubscription/<X+> - * This interior node contains the Home SP information, subscription policy, management and credential information. - **/ - public static class CredentialInfo implements Parcelable { - public String nodeName; - public Policy policy = new Policy(); - public String credentialPriority; - public HashMap<String, AAAServerTrustRoot> aAAServerTrustRoot = new HashMap<String, AAAServerTrustRoot>(); - public SubscriptionUpdate subscriptionUpdate = new SubscriptionUpdate(); - public HomeSP homeSP = new HomeSP(); - public SubscriptionParameters subscriptionParameters = new SubscriptionParameters(); - public Credential credential = new Credential(); - public Extension extension = new Extension(); - - public CredentialInfo(String nn) { - nodeName = nn; - } - - public AAAServerTrustRoot createAAAServerTrustRoot(String name, String url, String fp) { - AAAServerTrustRoot obj = new AAAServerTrustRoot(name, url, fp); - aAAServerTrustRoot.put(name, obj); - return obj; - } - - public CredentialInfo() { - } - - public CredentialInfo(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeString(nodeName); - out.writeParcelable(policy, flags); - out.writeString(credentialPriority); - out.writeMap(aAAServerTrustRoot); - out.writeParcelable(subscriptionUpdate, flags); - out.writeParcelable(homeSP, flags); - out.writeParcelable(subscriptionParameters, flags); - out.writeParcelable(credential, flags); - //out.writeParcelable(extension, flags); - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - nodeName = in.readString(); - policy = in.readParcelable(Policy.class.getClassLoader()); - credentialPriority = in.readString(); - in.readMap(aAAServerTrustRoot, AAAServerTrustRoot.class.getClassLoader()); - subscriptionUpdate = in.readParcelable(SubscriptionUpdate.class.getClassLoader()); - homeSP = in.readParcelable(HomeSP.class.getClassLoader()); - subscriptionParameters = in.readParcelable(SubscriptionParameters.class - .getClassLoader()); - credential = in.readParcelable(Credential.class.getClassLoader()); - //extension = in.readParcelable(Extension.class.getClassLoader()); - } - } - - public static final Parcelable.Creator<CredentialInfo> CREATOR = new Parcelable.Creator<CredentialInfo>() { - public CredentialInfo createFromParcel(Parcel in) { - return new CredentialInfo(in); - } - - public CredentialInfo[] newArray(int size) { - return new CredentialInfo[size]; - } - }; - - } - - /** - * PerProviderSubscription/<X+>/Policy - **/ - public static class Policy implements Parcelable { - public HashMap<String, PreferredRoamingPartnerList> preferredRoamingPartnerList = new HashMap<String, PreferredRoamingPartnerList>(); - public HashMap<String, MinBackhaulThresholdNetwork> minBackhaulThreshold = new HashMap<String, MinBackhaulThresholdNetwork>(); - public PolicyUpdate policyUpdate = new PolicyUpdate(); - public HashMap<String, SPExclusionList> sPExclusionList = new HashMap<String, SPExclusionList>(); - public HashMap<String, RequiredProtoPortTuple> requiredProtoPortTuple = new HashMap<String, RequiredProtoPortTuple>(); - public String maximumBSSLoadValue; - - public PreferredRoamingPartnerList createPreferredRoamingPartnerList(String name, - String fqdn, String priority, String country) { - PreferredRoamingPartnerList obj = new PreferredRoamingPartnerList(name, fqdn, priority, - country); - preferredRoamingPartnerList.put(name, obj); - return obj; - } - - public MinBackhaulThresholdNetwork createMinBackhaulThreshold(String name, String type, - String dl, String ul) { - MinBackhaulThresholdNetwork obj = new MinBackhaulThresholdNetwork(name, type, dl, ul); - minBackhaulThreshold.put(name, obj); - return obj; - } - - public SPExclusionList createSPExclusionList(String name, String ssid) { - SPExclusionList obj = new SPExclusionList(name, ssid); - sPExclusionList.put(name, obj); - return obj; - } - - public RequiredProtoPortTuple createRequiredProtoPortTuple(String name, String proto, - String port) { - RequiredProtoPortTuple obj = new RequiredProtoPortTuple(name, proto, port); - requiredProtoPortTuple.put(name, obj); - return obj; - } - - public Policy() { - } - - public Policy(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeMap(preferredRoamingPartnerList); - out.writeMap(minBackhaulThreshold); - out.writeParcelable(policyUpdate, flags); - out.writeMap(sPExclusionList); - out.writeMap(requiredProtoPortTuple); - out.writeString(maximumBSSLoadValue); - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - in.readMap(preferredRoamingPartnerList, - PreferredRoamingPartnerList.class.getClassLoader()); - in.readMap(minBackhaulThreshold, MinBackhaulThresholdNetwork.class.getClassLoader()); - policyUpdate = in.readParcelable(PolicyUpdate.class.getClassLoader()); - in.readMap(sPExclusionList, SPExclusionList.class.getClassLoader()); - in.readMap(requiredProtoPortTuple, RequiredProtoPortTuple.class.getClassLoader()); - maximumBSSLoadValue = in.readString(); - - } - } - - public static final Parcelable.Creator<Policy> CREATOR = new Parcelable.Creator<Policy>() { - public Policy createFromParcel(Parcel in) { - return new Policy(in); - } - - public Policy[] newArray(int size) { - return new Policy[size]; - } - }; - - } - - /** - * PerProviderSubscription/<X+>/Policy/PreferredRoamingPartnerList/<X+> - **/ - public static class PreferredRoamingPartnerList implements Parcelable { - public String nodeName; - public String FQDN_Match; //maximum 255 + ",includeSubdomains", equals 273 - public String Priority; - public String Country; // maximum 600 octets - - public PreferredRoamingPartnerList(String nn, String f, String p, String c) { - nodeName = nn; - FQDN_Match = f; - Priority = p; - Country = c; - } - - public PreferredRoamingPartnerList() { - } - - public PreferredRoamingPartnerList(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeString(nodeName); - out.writeString(FQDN_Match); - out.writeString(Priority); - out.writeString(Country); - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - nodeName = in.readString(); - FQDN_Match = in.readString(); - Priority = in.readString(); - Country = in.readString(); - } - } - - public static final Parcelable.Creator<PreferredRoamingPartnerList> CREATOR = new Parcelable.Creator<PreferredRoamingPartnerList>() { - public PreferredRoamingPartnerList createFromParcel(Parcel in) { - return new PreferredRoamingPartnerList(in); - } - - public PreferredRoamingPartnerList[] newArray(int size) { - return new PreferredRoamingPartnerList[size]; - } - }; - } - - /** - * PerProviderSubscription/<X+>/Policy/MinBackhaulThreshold - **/ - public static class MinBackhaulThresholdNetwork implements Parcelable { - public String nodeName; - public String NetworkType; - public String DLBandwidth; - public String ULBandwidth; - - public MinBackhaulThresholdNetwork(String nn, String nt, String d, String u) { - nodeName = nn; - NetworkType = nt; - DLBandwidth = d; - ULBandwidth = u; - } - - public MinBackhaulThresholdNetwork() { - } - - public MinBackhaulThresholdNetwork(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeString(nodeName); - out.writeString(NetworkType); - out.writeString(DLBandwidth); - out.writeString(ULBandwidth); - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - nodeName = in.readString(); - NetworkType = in.readString(); - DLBandwidth = in.readString(); - ULBandwidth = in.readString(); - } - } - - public static final Parcelable.Creator<MinBackhaulThresholdNetwork> CREATOR = new Parcelable.Creator<MinBackhaulThresholdNetwork>() { - public MinBackhaulThresholdNetwork createFromParcel(Parcel in) { - return new MinBackhaulThresholdNetwork(in); - } - - public MinBackhaulThresholdNetwork[] newArray(int size) { - return new MinBackhaulThresholdNetwork[size]; - } - }; - - } - - /** - * PerProviderSubscription/<X+>/Policy/PolicyUpdate - **/ - public static class PolicyUpdate implements Parcelable { - public String UpdateInterval; - public String UpdateMethod; - public String Restriction; - public String URI; - public UsernamePassword usernamePassword = new UsernamePassword(); - public String Other; - public TrustRoot trustRoot = new TrustRoot(); - - public PolicyUpdate() { - } - - public PolicyUpdate(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeString(UpdateInterval); - out.writeString(UpdateMethod); - out.writeString(Restriction); - out.writeString(URI); - out.writeParcelable(usernamePassword, flags); - out.writeString(Other); - out.writeParcelable(trustRoot, flags); - - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - UpdateInterval = in.readString(); - UpdateMethod = in.readString(); - Restriction = in.readString(); - URI = in.readString(); - usernamePassword = in.readParcelable(UsernamePassword.class.getClassLoader()); - Other = in.readString(); - trustRoot = in.readParcelable(TrustRoot.class.getClassLoader()); - } - } - - public static final Parcelable.Creator<PolicyUpdate> CREATOR = new Parcelable.Creator<PolicyUpdate>() { - public PolicyUpdate createFromParcel(Parcel in) { - return new PolicyUpdate(in); - } - - public PolicyUpdate[] newArray(int size) { - return new PolicyUpdate[size]; - } - }; - } - - /** - * PerProviderSubscription/<X+>/Policy/SPExclusionList - **/ - public static class SPExclusionList implements Parcelable { - public String nodeName; - public String SSID; - - public SPExclusionList(String nn, String s) { - nodeName = nn; - SSID = s; - } - - public SPExclusionList() { - } - - public SPExclusionList(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeString(nodeName); - out.writeString(SSID); - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - nodeName = in.readString(); - SSID = in.readString(); - } - } - - public static final Parcelable.Creator<SPExclusionList> CREATOR = new Parcelable.Creator<SPExclusionList>() { - public SPExclusionList createFromParcel(Parcel in) { - return new SPExclusionList(in); - } - - public SPExclusionList[] newArray(int size) { - return new SPExclusionList[size]; - } - }; - } - - /** - * PerProviderSubscription/<X+>/Policy/RequiredProtoPortTuple - **/ - public static class RequiredProtoPortTuple implements Parcelable { - public String nodeName; - public String IPProtocol; - public String PortNumber; - - public RequiredProtoPortTuple() { - } - - public RequiredProtoPortTuple(String nn, String protocol, String port) { - nodeName = nn; - IPProtocol = protocol; - PortNumber = port; - } - - public RequiredProtoPortTuple(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeString(nodeName); - out.writeString(IPProtocol); - out.writeString(PortNumber); - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - nodeName = in.readString(); - IPProtocol = in.readString(); - PortNumber = in.readString(); - } - } - - public static final Parcelable.Creator<RequiredProtoPortTuple> CREATOR = new Parcelable.Creator<RequiredProtoPortTuple>() { - public RequiredProtoPortTuple createFromParcel(Parcel in) { - return new RequiredProtoPortTuple(in); - } - - public RequiredProtoPortTuple[] newArray(int size) { - return new RequiredProtoPortTuple[size]; - } - }; - } - - /** - * PerProviderSubscription/<X+>/AAAServerTrustRoot - **/ - public static class AAAServerTrustRoot implements Parcelable { - public String nodeName; - public String CertURL; - public String CertSHA256Fingerprint; - - public AAAServerTrustRoot(String nn, String url, String fp) { - nodeName = nn; - CertURL = url; - CertSHA256Fingerprint = fp; - } - - public AAAServerTrustRoot() { - } - - public AAAServerTrustRoot(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeString(nodeName); - out.writeString(CertURL); - out.writeString(CertSHA256Fingerprint); - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - nodeName = in.readString(); - CertURL = in.readString(); - CertSHA256Fingerprint = in.readString(); - } - } - - public static final Parcelable.Creator<AAAServerTrustRoot> CREATOR = new Parcelable.Creator<AAAServerTrustRoot>() { - public AAAServerTrustRoot createFromParcel(Parcel in) { - return new AAAServerTrustRoot(in); - } - - public AAAServerTrustRoot[] newArray(int size) { - return new AAAServerTrustRoot[size]; - } - }; - } - - /** - * PerProviderSubscription/<X+>/SubscriptionUpdate - **/ - public static class SubscriptionUpdate implements Parcelable { - public String UpdateInterval; - public String UpdateMethod; - public String Restriction; - public String URI; - public UsernamePassword usernamePassword = new UsernamePassword(); - public String Other; - public TrustRoot trustRoot = new TrustRoot(); - - public SubscriptionUpdate() { - } - - public SubscriptionUpdate(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeString(UpdateInterval); - out.writeString(UpdateMethod); - out.writeString(Restriction); - out.writeString(URI); - out.writeParcelable(usernamePassword, flags); - out.writeString(Other); - out.writeParcelable(trustRoot, flags); - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - UpdateInterval = in.readString(); - UpdateMethod = in.readString(); - Restriction = in.readString(); - URI = in.readString(); - usernamePassword = in.readParcelable(UsernamePassword.class.getClassLoader()); - Other = in.readString(); - trustRoot = in.readParcelable(TrustRoot.class.getClassLoader()); - } - } - - public static final Parcelable.Creator<SubscriptionUpdate> CREATOR = new Parcelable.Creator<SubscriptionUpdate>() { - public SubscriptionUpdate createFromParcel(Parcel in) { - return new SubscriptionUpdate(in); - } - - public SubscriptionUpdate[] newArray(int size) { - return new SubscriptionUpdate[size]; - } - }; - - } - - /** - * PerProviderSubscription/<X+>/Policy/PolicyUpdate/TrustRoot - * PerProviderSubscription/<X+>/SubscriptionUpdate/TrustRoot - * PerProviderSubscription/<X+>/AAAServerTrustRoot/<X+> - **/ - public static class TrustRoot implements Parcelable { - public String CertURL; - public String CertSHA256Fingerprint; - - public TrustRoot() { - } - - public TrustRoot(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeString(CertURL); - out.writeString(CertSHA256Fingerprint); - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - CertURL = in.readString(); - CertSHA256Fingerprint = in.readString(); - } - } - - public static final Parcelable.Creator<TrustRoot> CREATOR = new Parcelable.Creator<TrustRoot>() { - public TrustRoot createFromParcel(Parcel in) { - return new TrustRoot(in); - } - - public TrustRoot[] newArray(int size) { - return new TrustRoot[size]; - } - }; - } - - /** - * PerProviderSubscription/<X+>/Policy/PolicyUpdate/UsernamePassword - * PerProviderSubscription/<X+>/SubscriptionUpdate/UsernamePassword - * PerProviderSubscription/<X+>/Credential/UsernamePassword - **/ - public static class UsernamePassword implements Parcelable { - public String Username; - public String Password; - //following are Credential node used only - public boolean MachineManaged; - public String SoftTokenApp; - public String AbleToShare; - public EAPMethod eAPMethod = new EAPMethod(); - - public UsernamePassword() { - } - - public UsernamePassword(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeString(Username); - out.writeString(Password); - out.writeInt(MachineManaged ? 1 : 0); - out.writeString(SoftTokenApp); - out.writeString(AbleToShare); - out.writeParcelable(eAPMethod, flags); - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - Username = in.readString(); - Password = in.readString(); - MachineManaged = (in.readInt() == 1) ? true : false; - SoftTokenApp = in.readString(); - AbleToShare = in.readString(); - eAPMethod = in.readParcelable(EAPMethod.class.getClassLoader()); - } - } - - public static final Parcelable.Creator<UsernamePassword> CREATOR = new Parcelable.Creator<UsernamePassword>() { - public UsernamePassword createFromParcel(Parcel in) { - return new UsernamePassword(in); - } - - public UsernamePassword[] newArray(int size) { - return new UsernamePassword[size]; - } - }; - - } - - /** - * PerProviderSubscription/<X+>/Credential/UsernamePassword/EAPMethod - **/ - public static class EAPMethod implements Parcelable { - public String EAPType; - public String VendorId; - public String VendorType; - public String InnerEAPType; - public String InnerVendorId; - public String InnerVendorType; - public String InnerMethod; - - public EAPMethod() { - } - - public EAPMethod(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeString(EAPType); - out.writeString(VendorId); - out.writeString(VendorType); - out.writeString(InnerEAPType); - out.writeString(InnerVendorId); - out.writeString(InnerVendorType); - out.writeString(InnerMethod); - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - EAPType = in.readString(); - VendorId = in.readString(); - VendorType = in.readString(); - InnerEAPType = in.readString(); - InnerVendorId = in.readString(); - InnerVendorType = in.readString(); - InnerMethod = in.readString(); - } - } - - public static final Parcelable.Creator<EAPMethod> CREATOR = new Parcelable.Creator<EAPMethod>() { - public EAPMethod createFromParcel(Parcel in) { - return new EAPMethod(in); - } - - public EAPMethod[] newArray(int size) { - return new EAPMethod[size]; - } - }; - } - - /** - * PerProviderSubscription/<X+>/HomeSP - **/ - public static class HomeSP implements Parcelable { - public HashMap<String, NetworkID> networkID = new HashMap<String, NetworkID>(); - public String FriendlyName; - public String IconURL; - public String FQDN; - public HashMap<String, HomeOIList> homeOIList = new HashMap<String, HomeOIList>(); - public HashMap<String, OtherHomePartners> otherHomePartners = new HashMap<String, OtherHomePartners>(); - public String RoamingConsortiumOI; - - public NetworkID createNetworkID(String name, String ssid, String hessid) { - NetworkID obj = new NetworkID(name, ssid, hessid); - networkID.put(name, obj); - return obj; - } - - public HomeOIList createHomeOIList(String name, String homeoi, boolean required) { - HomeOIList obj = new HomeOIList(name, homeoi, required); - homeOIList.put(name, obj); - return obj; - } - - public OtherHomePartners createOtherHomePartners(String name, String fqdn) { - OtherHomePartners obj = new OtherHomePartners(name, fqdn); - otherHomePartners.put(name, obj); - return obj; - } - - public HomeSP() { - } - - public HomeSP(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeMap(networkID); - out.writeString(FriendlyName); - out.writeString(IconURL); - out.writeString(FQDN); - out.writeMap(homeOIList); - out.writeMap(otherHomePartners); - out.writeString(RoamingConsortiumOI); - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - in.readMap(networkID, NetworkID.class.getClassLoader()); - FriendlyName = in.readString(); - IconURL = in.readString(); - FQDN = in.readString(); - in.readMap(homeOIList, HomeOIList.class.getClassLoader()); - in.readMap(otherHomePartners, OtherHomePartners.class.getClassLoader()); - RoamingConsortiumOI = in.readString(); - } - } - - public static final Parcelable.Creator<HomeSP> CREATOR = new Parcelable.Creator<HomeSP>() { - public HomeSP createFromParcel(Parcel in) { - return new HomeSP(in); - } - - public HomeSP[] newArray(int size) { - return new HomeSP[size]; - } - }; - - } - - /** - * PerProviderSubscription/<X+>/HomeSP/NetworkID - **/ - public static class NetworkID implements Parcelable { - public String nodeName; - public String SSID; - public String HESSID; - - public NetworkID(String nn, String s, String h) { - nodeName = nn; - SSID = s; - HESSID = h; - } - - public NetworkID() { - } - - public NetworkID(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeString(nodeName); - out.writeString(SSID); - out.writeString(HESSID); - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - nodeName = in.readString(); - SSID = in.readString(); - HESSID = in.readString(); - } - } - - public static final Parcelable.Creator<NetworkID> CREATOR = new Parcelable.Creator<NetworkID>() { - public NetworkID createFromParcel(Parcel in) { - return new NetworkID(in); - } - - public NetworkID[] newArray(int size) { - return new NetworkID[size]; - } - }; - - } - - /** - * PerProviderSubscription/<X+>/HomeSP/HomeOIList - **/ - public static class HomeOIList implements Parcelable { - public String nodeName; - public String HomeOI; - public boolean HomeOIRequired; - - public HomeOIList(String nn, String h, boolean r) { - nodeName = nn; - HomeOI = h; - HomeOIRequired = r; - } - - public HomeOIList() { - } - - public HomeOIList(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeString(nodeName); - out.writeString(HomeOI); - out.writeInt(HomeOIRequired ? 1 : 0); - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - nodeName = in.readString(); - HomeOI = in.readString(); - HomeOIRequired = (in.readInt() == 1) ? true : false; - } - } - - public static final Parcelable.Creator<HomeOIList> CREATOR = new Parcelable.Creator<HomeOIList>() { - public HomeOIList createFromParcel(Parcel in) { - return new HomeOIList(in); - } - - public HomeOIList[] newArray(int size) { - return new HomeOIList[size]; - } - }; - - } - - /** - * PerProviderSubscription/<X+>/HomeSP/OtherHomePartners - **/ - public static class OtherHomePartners implements Parcelable { - public String nodeName; - public String FQDN; - - public OtherHomePartners(String nn, String f) { - nodeName = nn; - FQDN = f; - } - - public OtherHomePartners() { - } - - public OtherHomePartners(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeString(nodeName); - out.writeString(FQDN); - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - nodeName = in.readString(); - FQDN = in.readString(); - } - } - - public static final Parcelable.Creator<OtherHomePartners> CREATOR = new Parcelable.Creator<OtherHomePartners>() { - public OtherHomePartners createFromParcel(Parcel in) { - return new OtherHomePartners(in); - } - - public OtherHomePartners[] newArray(int size) { - return new OtherHomePartners[size]; - } - }; - - } - - /** - * PerProviderSubscription/<X+>/SubscriptionParameters - **/ - public static class SubscriptionParameters implements Parcelable { - public String CreationDate; - public String ExpirationDate; - public String TypeOfSubscription; - public UsageLimits usageLimits = new UsageLimits(); - - public SubscriptionParameters() { - } - - public SubscriptionParameters(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeString(CreationDate); - out.writeString(ExpirationDate); - out.writeString(TypeOfSubscription); - out.writeParcelable(usageLimits, flags); - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - CreationDate = in.readString(); - ExpirationDate = in.readString(); - TypeOfSubscription = in.readString(); - usageLimits = in.readParcelable(UsageLimits.class.getClassLoader()); - } - } - - public static final Parcelable.Creator<SubscriptionParameters> CREATOR = new Parcelable.Creator<SubscriptionParameters>() { - public SubscriptionParameters createFromParcel(Parcel in) { - return new SubscriptionParameters(in); - } - - public SubscriptionParameters[] newArray(int size) { - return new SubscriptionParameters[size]; - } - }; - - } - - /** - * PerProviderSubscription/<X+>/SubscriptionParameters/UsageLimits - **/ - public static class UsageLimits implements Parcelable { - public String DataLimit; - public String StartDate; - public String TimeLimit; - public String UsageTimePeriod; - - public UsageLimits() { - } - - public UsageLimits(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeString(DataLimit); - out.writeString(StartDate); - out.writeString(TimeLimit); - out.writeString(UsageTimePeriod); - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - DataLimit = in.readString(); - StartDate = in.readString(); - TimeLimit = in.readString(); - UsageTimePeriod = in.readString(); - } - } - - public static final Parcelable.Creator<UsageLimits> CREATOR = new Parcelable.Creator<UsageLimits>() { - public UsageLimits createFromParcel(Parcel in) { - return new UsageLimits(in); - } - - public UsageLimits[] newArray(int size) { - return new UsageLimits[size]; - } - }; - } - - /** - * PerProviderSubscription/<X+>/Credential - **/ - public static class Credential implements Parcelable { - public String CreationDate; - public String ExpirationDate; - public UsernamePassword usernamePassword = new UsernamePassword(); - public DigitalCertificate digitalCertificate = new DigitalCertificate(); - public String Realm; - public boolean CheckAAAServerCertStatus; - public SIM sim = new SIM(); - - public Credential() { - } - - public Credential(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeString(CreationDate); - out.writeString(ExpirationDate); - out.writeParcelable(usernamePassword, flags); - out.writeParcelable(digitalCertificate, flags); - out.writeString(Realm); - out.writeInt(CheckAAAServerCertStatus ? 1 : 0); - out.writeParcelable(sim, flags); - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - CreationDate = in.readString(); - ExpirationDate = in.readString(); - usernamePassword = in.readParcelable(UsernamePassword.class.getClassLoader()); - digitalCertificate = in.readParcelable(DigitalCertificate.class.getClassLoader()); - Realm = in.readString(); - CheckAAAServerCertStatus = (in.readInt() == 1) ? true : false; - sim = in.readParcelable(SIM.class.getClassLoader()); - } - } - - public static final Parcelable.Creator<Credential> CREATOR = new Parcelable.Creator<Credential>() { - public Credential createFromParcel(Parcel in) { - return new Credential(in); - } - - public Credential[] newArray(int size) { - return new Credential[size]; - } - }; - } - - /** - * PerProviderSubscription/<X+>/Credential/DigitalCertificate - **/ - public static class DigitalCertificate implements Parcelable { - public String CertificateType; - public String CertSHA256Fingerprint; - - public DigitalCertificate() { - } - - public DigitalCertificate(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeString(CertificateType); - out.writeString(CertSHA256Fingerprint); - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - CertificateType = in.readString(); - CertSHA256Fingerprint = in.readString(); - } - } - - public static final Parcelable.Creator<DigitalCertificate> CREATOR = new Parcelable.Creator<DigitalCertificate>() { - public DigitalCertificate createFromParcel(Parcel in) { - return new DigitalCertificate(in); - } - - public DigitalCertificate[] newArray(int size) { - return new DigitalCertificate[size]; - } - }; - - } - - /** - * PerProviderSubscription/<X+>/Credential/SIM - **/ - public static class SIM implements Parcelable { - public String IMSI; - public String EAPType; - - public SIM() { - } - - public SIM(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeString(IMSI); - out.writeString(EAPType); - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - IMSI = in.readString(); - EAPType = in.readString(); - } - } - - public static final Parcelable.Creator<SIM> CREATOR = new Parcelable.Creator<SIM>() { - public SIM createFromParcel(Parcel in) { - return new SIM(in); - } - - public SIM[] newArray(int size) { - return new SIM[size]; - } - }; - - } - - /** - * PerProviderSubscription/<X+>/Extension - **/ - public static class Extension { - public String empty; - } - - public WifiPasspointDmTree() { - } - - public WifiPasspointDmTree(Parcel in) { - readFromParcel(in); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeMap(spFqdn); - } - - public void readFromParcel(Parcel in) { - if (in == null) { - //log here - } else { - in.readMap(spFqdn, SpFqdn.class.getClassLoader()); - } - } - - public static final Parcelable.Creator<WifiPasspointDmTree> CREATOR = new Parcelable.Creator<WifiPasspointDmTree>() { - public WifiPasspointDmTree createFromParcel(Parcel in) { - return new WifiPasspointDmTree(in); - } - - public WifiPasspointDmTree[] newArray(int size) { - return new WifiPasspointDmTree[size]; - } - }; - -} diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java deleted file mode 100644 index 33db3f5491f8..000000000000 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java +++ /dev/null @@ -1,559 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.wifi.passpoint; - -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.ArrayList; -import java.util.List; - -/** @hide */ -public class WifiPasspointInfo implements Parcelable { - - /** TODO doc */ - public static final int ANQP_CAPABILITY = 1 << 0; - - /** TODO doc */ - public static final int VENUE_NAME = 1 << 1; - - /** TODO doc */ - public static final int NETWORK_AUTH_TYPE = 1 << 2; - - /** TODO doc */ - public static final int ROAMING_CONSORTIUM = 1 << 3; - - /** TODO doc */ - public static final int IP_ADDR_TYPE_AVAILABILITY = 1 << 4; - - /** TODO doc */ - public static final int NAI_REALM = 1 << 5; - - /** TODO doc */ - public static final int CELLULAR_NETWORK = 1 << 6; - - /** TODO doc */ - public static final int DOMAIN_NAME = 1 << 7; - - /** TODO doc */ - public static final int HOTSPOT_CAPABILITY = 1 << 8; - - /** TODO doc */ - public static final int OPERATOR_FRIENDLY_NAME = 1 << 9; - - /** TODO doc */ - public static final int WAN_METRICS = 1 << 10; - - /** TODO doc */ - public static final int CONNECTION_CAPABILITY = 1 << 11; - - /** TODO doc */ - public static final int OSU_PROVIDER = 1 << 12; - - /** TODO doc */ - public static final int PRESET_CRED_MATCH = - ANQP_CAPABILITY | - HOTSPOT_CAPABILITY | - NAI_REALM | - CELLULAR_NETWORK | - DOMAIN_NAME; - - /** TODO doc */ - public static final int PRESET_ALL = - ANQP_CAPABILITY | - VENUE_NAME | - NETWORK_AUTH_TYPE | - ROAMING_CONSORTIUM | - IP_ADDR_TYPE_AVAILABILITY | - NAI_REALM | - CELLULAR_NETWORK | - DOMAIN_NAME | - HOTSPOT_CAPABILITY | - OPERATOR_FRIENDLY_NAME | - WAN_METRICS | - CONNECTION_CAPABILITY | - OSU_PROVIDER; - - - public static class WanMetrics { - public static final int STATUS_RESERVED = 0; - public static final int STATUS_UP = 1; - public static final int STATUS_DOWN = 2; - public static final int STATUS_TEST = 3; - - public int wanInfo; - public long downlinkSpeed; - public long uplinkSpeed; - public int downlinkLoad; - public int uplinkLoad; - public int lmd; - - public int getLinkStatus() { - return wanInfo & 0x3; - } - - public boolean getSymmetricLink() { - return (wanInfo & (1 << 2)) != 0; - } - - public boolean getAtCapacity() { - return (wanInfo & (1 << 3)) != 0; - } - - @Override - public String toString() { - return wanInfo + "," + downlinkSpeed + "," + uplinkSpeed + "," + - downlinkLoad + "," + uplinkLoad + "," + lmd; - } - } - - public static class IpProtoPort { - public static final int STATUS_CLOSED = 0; - public static final int STATUS_OPEN = 1; - public static final int STATUS_UNKNOWN = 2; - - public int proto; - public int port; - public int status; - - @Override - public String toString() { - return proto + "," + port + "," + status; - } - } - - public static class NetworkAuthType { - public static final int TYPE_TERMS_AND_CONDITION = 0; - public static final int TYPE_ONLINE_ENROLLMENT = 1; - public static final int TYPE_HTTP_REDIRECTION = 2; - public static final int TYPE_DNS_REDIRECTION = 3; - - public int type; - public String redirectUrl; - - @Override - public String toString() { - return type + "," + redirectUrl; - } - } - - public static class IpAddressType { - public static final int IPV6_NOT_AVAILABLE = 0; - public static final int IPV6_AVAILABLE = 1; - public static final int IPV6_UNKNOWN = 2; - - public static final int IPV4_NOT_AVAILABLE = 0; - public static final int IPV4_PUBLIC = 1; - public static final int IPV4_PORT_RESTRICTED = 2; - public static final int IPV4_SINGLE_NAT = 3; - public static final int IPV4_DOUBLE_NAT = 4; - public static final int IPV4_PORT_RESTRICTED_SINGLE_NAT = 5; - public static final int IPV4_PORT_RESTRICTED_DOUBLE_NAT = 6; - public static final int IPV4_PORT_UNKNOWN = 7; - - private static final int NULL_VALUE = -1; - - public int availability; - - public int getIpv6Availability() { - return availability & 0x3; - } - - public int getIpv4Availability() { - return (availability & 0xFF) >> 2; - } - - @Override - public String toString() { - return getIpv6Availability() + "," + getIpv4Availability(); - } - } - - public static class NaiRealm { - public static final int ENCODING_RFC4282 = 0; - public static final int ENCODING_UTF8 = 1; - - public int encoding; - public String realm; - - @Override - public String toString() { - return encoding + "," + realm; - } - } - - public static class CellularNetwork { - public String mcc; - public String mnc; - - @Override - public String toString() { - return mcc + "," + mnc; - } - } - - /** BSSID */ - public String bssid; - - /** venue name */ - public String venueName; - - /** list of network authentication types */ - public List<NetworkAuthType> networkAuthTypeList; - - /** list of roaming consortium OIs */ - public List<String> roamingConsortiumList; - - /** IP address availability */ - public IpAddressType ipAddrTypeAvailability; - - /** list of NAI realm */ - public List<NaiRealm> naiRealmList; - - /** list of 3GPP cellular network */ - public List<CellularNetwork> cellularNetworkList; - - /** list of fully qualified domain name (FQDN) */ - public List<String> domainNameList; - - /** HS 2.0 operator friendly name */ - public String operatorFriendlyName; - - /** HS 2.0 wan metrics */ - public WanMetrics wanMetrics; - - /** list of HS 2.0 IP proto port */ - public List<IpProtoPort> connectionCapabilityList; - - /** list of HS 2.0 OSU providers */ - public List<WifiPasspointOsuProvider> osuProviderList; - - /** - * Convert mask to ANQP subtypes, for supplicant command use. - * - * @param mask The ANQP subtypes mask. - * @return String of ANQP subtypes, good for supplicant command use - * @hide - */ - public static String toAnqpSubtypes(int mask) { - StringBuilder sb = new StringBuilder(); - if ((mask & ANQP_CAPABILITY) != 0) - sb.append("257,"); - if ((mask & VENUE_NAME) != 0) - sb.append("258,"); - if ((mask & NETWORK_AUTH_TYPE) != 0) - sb.append("260,"); - if ((mask & ROAMING_CONSORTIUM) != 0) - sb.append("261,"); - if ((mask & IP_ADDR_TYPE_AVAILABILITY) != 0) - sb.append("262,"); - if ((mask & NAI_REALM) != 0) - sb.append("263,"); - if ((mask & CELLULAR_NETWORK) != 0) - sb.append("264,"); - if ((mask & DOMAIN_NAME) != 0) - sb.append("268,"); - if ((mask & HOTSPOT_CAPABILITY) != 0) - sb.append("hs20:2,"); - if ((mask & OPERATOR_FRIENDLY_NAME) != 0) - sb.append("hs20:3,"); - if ((mask & WAN_METRICS) != 0) - sb.append("hs20:4,"); - if ((mask & CONNECTION_CAPABILITY) != 0) - sb.append("hs20:5,"); - if ((mask & OSU_PROVIDER) != 0) - sb.append("hs20:8,"); - if (sb.length() > 0) - sb.deleteCharAt(sb.length() - 1); - return sb.toString(); - } - - @Override - public String toString() { - StringBuffer sb = new StringBuffer(); - - sb.append("BSSID: ").append("(").append(bssid).append(")"); - - if (venueName != null) - sb.append(" venueName: ").append("(") - .append(venueName.replace("\n", "\\n")).append(")"); - - if (networkAuthTypeList != null) { - sb.append(" networkAuthType: "); - for (NetworkAuthType auth : networkAuthTypeList) - sb.append("(").append(auth.toString()).append(")"); - } - - if (roamingConsortiumList != null) { - sb.append(" roamingConsortium: "); - for (String oi : roamingConsortiumList) - sb.append("(").append(oi).append(")"); - } - - if (ipAddrTypeAvailability != null) { - sb.append(" ipAddrTypeAvaibility: ").append("(") - .append(ipAddrTypeAvailability.toString()).append(")"); - } - - if (naiRealmList != null) { - sb.append(" naiRealm: "); - for (NaiRealm realm : naiRealmList) - sb.append("(").append(realm.toString()).append(")"); - } - - if (cellularNetworkList != null) { - sb.append(" cellularNetwork: "); - for (CellularNetwork plmn : cellularNetworkList) - sb.append("(").append(plmn.toString()).append(")"); - } - - if (domainNameList != null) { - sb.append(" domainName: "); - for (String fqdn : domainNameList) - sb.append("(").append(fqdn).append(")"); - } - - if (operatorFriendlyName != null) - sb.append(" operatorFriendlyName: ").append("(") - .append(operatorFriendlyName).append(")"); - - if (wanMetrics != null) - sb.append(" wanMetrics: ").append("(") - .append(wanMetrics.toString()).append(")"); - - if (connectionCapabilityList != null) { - sb.append(" connectionCapability: "); - for (IpProtoPort ip : connectionCapabilityList) - sb.append("(").append(ip.toString()).append(")"); - } - - if (osuProviderList != null) { - sb.append(" osuProviderList: "); - for (WifiPasspointOsuProvider osu : osuProviderList) - sb.append("(").append(osu.toString()).append(")"); - } - - return sb.toString(); - } - - /** Implement the Parcelable interface {@hide} */ - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeString(bssid); - out.writeString(venueName); - - if (networkAuthTypeList == null) { - out.writeInt(0); - } else { - out.writeInt(networkAuthTypeList.size()); - for (NetworkAuthType auth : networkAuthTypeList) { - out.writeInt(auth.type); - out.writeString(auth.redirectUrl); - } - } - - if (roamingConsortiumList == null) { - out.writeInt(0); - } else { - out.writeInt(roamingConsortiumList.size()); - for (String oi : roamingConsortiumList) - out.writeString(oi); - } - - if (ipAddrTypeAvailability == null) { - out.writeInt(IpAddressType.NULL_VALUE); - } else { - out.writeInt(ipAddrTypeAvailability.availability); - } - - if (naiRealmList == null) { - out.writeInt(0); - } else { - out.writeInt(naiRealmList.size()); - for (NaiRealm realm : naiRealmList) { - out.writeInt(realm.encoding); - out.writeString(realm.realm); - } - } - - if (cellularNetworkList == null) { - out.writeInt(0); - } else { - out.writeInt(cellularNetworkList.size()); - for (CellularNetwork plmn : cellularNetworkList) { - out.writeString(plmn.mcc); - out.writeString(plmn.mnc); - } - } - - - if (domainNameList == null) { - out.writeInt(0); - } else { - out.writeInt(domainNameList.size()); - for (String fqdn : domainNameList) - out.writeString(fqdn); - } - - out.writeString(operatorFriendlyName); - - if (wanMetrics == null) { - out.writeInt(0); - } else { - out.writeInt(1); - out.writeInt(wanMetrics.wanInfo); - out.writeLong(wanMetrics.downlinkSpeed); - out.writeLong(wanMetrics.uplinkSpeed); - out.writeInt(wanMetrics.downlinkLoad); - out.writeInt(wanMetrics.uplinkLoad); - out.writeInt(wanMetrics.lmd); - } - - if (connectionCapabilityList == null) { - out.writeInt(0); - } else { - out.writeInt(connectionCapabilityList.size()); - for (IpProtoPort ip : connectionCapabilityList) { - out.writeInt(ip.proto); - out.writeInt(ip.port); - out.writeInt(ip.status); - } - } - - if (osuProviderList == null) { - out.writeInt(0); - } else { - out.writeInt(osuProviderList.size()); - for (WifiPasspointOsuProvider osu : osuProviderList) - osu.writeToParcel(out, flags); - } - } - - /** Implement the Parcelable interface {@hide} */ - @Override - public int describeContents() { - return 0; - } - - /** Implement the Parcelable interface {@hide} */ - public static final Parcelable.Creator<WifiPasspointInfo> CREATOR = - new Parcelable.Creator<WifiPasspointInfo>() { - @Override - public WifiPasspointInfo createFromParcel(Parcel in) { - WifiPasspointInfo p = new WifiPasspointInfo(); - int n; - - p.bssid = in.readString(); - p.venueName = in.readString(); - - n = in.readInt(); - if (n > 0) { - p.networkAuthTypeList = new ArrayList<NetworkAuthType>(); - for (int i = 0; i < n; i++) { - NetworkAuthType auth = new NetworkAuthType(); - auth.type = in.readInt(); - auth.redirectUrl = in.readString(); - p.networkAuthTypeList.add(auth); - } - } - - n = in.readInt(); - if (n > 0) { - p.roamingConsortiumList = new ArrayList<String>(); - for (int i = 0; i < n; i++) - p.roamingConsortiumList.add(in.readString()); - } - - n = in.readInt(); - if (n != IpAddressType.NULL_VALUE) { - p.ipAddrTypeAvailability = new IpAddressType(); - p.ipAddrTypeAvailability.availability = n; - } - - n = in.readInt(); - if (n > 0) { - p.naiRealmList = new ArrayList<NaiRealm>(); - for (int i = 0; i < n; i++) { - NaiRealm realm = new NaiRealm(); - realm.encoding = in.readInt(); - realm.realm = in.readString(); - p.naiRealmList.add(realm); - } - } - - n = in.readInt(); - if (n > 0) { - p.cellularNetworkList = new ArrayList<CellularNetwork>(); - for (int i = 0; i < n; i++) { - CellularNetwork plmn = new CellularNetwork(); - plmn.mcc = in.readString(); - plmn.mnc = in.readString(); - p.cellularNetworkList.add(plmn); - } - } - - n = in.readInt(); - if (n > 0) { - p.domainNameList = new ArrayList<String>(); - for (int i = 0; i < n; i++) - p.domainNameList.add(in.readString()); - } - - p.operatorFriendlyName = in.readString(); - - n = in.readInt(); - if (n > 0) { - p.wanMetrics = new WanMetrics(); - p.wanMetrics.wanInfo = in.readInt(); - p.wanMetrics.downlinkSpeed = in.readLong(); - p.wanMetrics.uplinkSpeed = in.readLong(); - p.wanMetrics.downlinkLoad = in.readInt(); - p.wanMetrics.uplinkLoad = in.readInt(); - p.wanMetrics.lmd = in.readInt(); - } - - n = in.readInt(); - if (n > 0) { - p.connectionCapabilityList = new ArrayList<IpProtoPort>(); - for (int i = 0; i < n; i++) { - IpProtoPort ip = new IpProtoPort(); - ip.proto = in.readInt(); - ip.port = in.readInt(); - ip.status = in.readInt(); - p.connectionCapabilityList.add(ip); - } - } - - n = in.readInt(); - if (n > 0) { - p.osuProviderList = new ArrayList<WifiPasspointOsuProvider>(); - for (int i = 0; i < n; i++) { - WifiPasspointOsuProvider osu = WifiPasspointOsuProvider.CREATOR - .createFromParcel(in); - p.osuProviderList.add(osu); - } - } - - return p; - } - - @Override - public WifiPasspointInfo[] newArray(int size) { - return new WifiPasspointInfo[size]; - } - }; -} diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java deleted file mode 100644 index 0245a3d0617a..000000000000 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java +++ /dev/null @@ -1,567 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.wifi.passpoint; - -import android.content.Context; -import android.net.wifi.ScanResult; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.Messenger; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.RemoteException; -import android.util.Log; - -import com.android.internal.util.AsyncChannel; -import com.android.internal.util.Protocol; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; - -/** - * Provides APIs for managing Wifi Passpoint credentials. - * @hide - */ -public class WifiPasspointManager { - - private static final String TAG = "PasspointManager"; - - private static final boolean DBG = true; - - /* Passpoint states values */ - - /** Passpoint is in an unknown state. This should only occur in boot time */ - public static final int PASSPOINT_STATE_UNKNOWN = 0; - - /** Passpoint is disabled. This occurs when wifi is disabled */ - public static final int PASSPOINT_STATE_DISABLED = 1; - - /** Passpoint is enabled and in discovery state */ - public static final int PASSPOINT_STATE_DISCOVERY = 2; - - /** Passpoint is enabled and in access state */ - public static final int PASSPOINT_STATE_ACCESS = 3; - - /** Passpoint is enabled and in provisioning state */ - public static final int PASSPOINT_STATE_PROVISION = 4; - - /* Passpoint callback error codes */ - - /** Indicates that the operation failed due to an internal error */ - public static final int REASON_ERROR = 0; - - /** Indicates that the operation failed because wifi is disabled */ - public static final int REASON_WIFI_DISABLED = 1; - - /** Indicates that the operation failed because the framework is busy */ - public static final int REASON_BUSY = 2; - - /** Indicates that the operation failed because parameter is invalid */ - public static final int REASON_INVALID_PARAMETER = 3; - - /** Indicates that the operation failed because the server is not trusted */ - public static final int REASON_NOT_TRUSTED = 4; - - /** - * protocol supported for Passpoint - */ - public static final String PROTOCOL_DM = "OMA-DM-ClientInitiated"; - - /** - * protocol supported for Passpoint - */ - public static final String PROTOCOL_SOAP = "SPP-ClientInitiated"; - - /* Passpoint broadcasts */ - - /** - * Broadcast intent action indicating that the state of Passpoint - * connectivity has changed - */ - public static final String PASSPOINT_STATE_CHANGED_ACTION = - "android.net.wifi.passpoint.STATE_CHANGE"; - - /** - * Broadcast intent action indicating that the saved Passpoint credential - * list has changed - */ - public static final String PASSPOINT_CRED_CHANGED_ACTION = - "android.net.wifi.passpoint.CRED_CHANGE"; - - /** - * Broadcast intent action indicating that Passpoint online sign up is - * avaiable. - */ - public static final String PASSPOINT_OSU_AVAILABLE_ACTION = - "android.net.wifi.passpoint.OSU_AVAILABLE"; - - /** - * Broadcast intent action indicating that user remediation is required - */ - public static final String PASSPOINT_USER_REM_REQ_ACTION = - "android.net.wifi.passpoint.USER_REM_REQ"; - - /** - * Interface for callback invocation when framework channel is lost - */ - public interface ChannelListener { - /** - * The channel to the framework has been disconnected. Application could - * try re-initializing using {@link #initialize} - */ - public void onChannelDisconnected(); - } - - /** - * Interface for callback invocation on an application action - */ - public interface ActionListener { - /** The operation succeeded */ - public void onSuccess(); - - /** - * The operation failed - * - * @param reason The reason for failure could be one of - * {@link #WIFI_DISABLED}, {@link #ERROR} or {@link #BUSY} - */ - public void onFailure(int reason); - } - - /** - * Interface for callback invocation when doing OSU or user remediation - */ - public interface OsuRemListener { - /** The operation succeeded */ - public void onSuccess(); - - /** - * The operation failed - * - * @param reason The reason for failure could be one of - * {@link #WIFI_DISABLED}, {@link #ERROR} or {@link #BUSY} - */ - public void onFailure(int reason); - - /** - * Browser launch is requried for user interaction. When this callback - * is called, app should launch browser / webview to the given URI. - * - * @param uri URI for browser launch - */ - public void onBrowserLaunch(String uri); - - /** - * When this is called, app should dismiss the previously lanched browser. - */ - public void onBrowserDismiss(); - } - - /** - * A channel that connects the application to the wifi passpoint framework. - * Most passpoint operations require a Channel as an argument. - * An instance of Channel is obtained by doing a call on {@link #initialize} - */ - public static class Channel { - private final static int INVALID_LISTENER_KEY = 0; - - private ChannelListener mChannelListener; - - private HashMap<Integer, Object> mListenerMap = new HashMap<Integer, Object>(); - private HashMap<Integer, Integer> mListenerMapCount = new HashMap<Integer, Integer>(); - private Object mListenerMapLock = new Object(); - private int mListenerKey = 0; - - private List<ScanResult> mAnqpRequest = new LinkedList<ScanResult>(); - private Object mAnqpRequestLock = new Object(); - - private AsyncChannel mAsyncChannel; - private PasspointHandler mHandler; - Context mContext; - - Channel(Context context, Looper looper, ChannelListener l) { - mAsyncChannel = new AsyncChannel(); - mHandler = new PasspointHandler(looper); - mChannelListener = l; - mContext = context; - } - - private int putListener(Object listener) { - return putListener(listener, 1); - } - - private int putListener(Object listener, int count) { - if (listener == null || count <= 0) - return INVALID_LISTENER_KEY; - int key; - synchronized (mListenerMapLock) { - do { - key = mListenerKey++; - } while (key == INVALID_LISTENER_KEY); - mListenerMap.put(key, listener); - mListenerMapCount.put(key, count); - } - return key; - } - - private Object peekListener(int key) { - Log.d(TAG, "peekListener() key=" + key); - if (key == INVALID_LISTENER_KEY) - return null; - synchronized (mListenerMapLock) { - return mListenerMap.get(key); - } - } - - - private Object getListener(int key, boolean forceRemove) { - Log.d(TAG, "getListener() key=" + key + " force=" + forceRemove); - if (key == INVALID_LISTENER_KEY) - return null; - synchronized (mListenerMapLock) { - if (!forceRemove) { - int count = mListenerMapCount.get(key); - Log.d(TAG, "count=" + count); - mListenerMapCount.put(key, --count); - if (count > 0) - return null; - } - Log.d(TAG, "remove key"); - mListenerMapCount.remove(key); - return mListenerMap.remove(key); - } - } - - private void anqpRequestStart(ScanResult sr) { - Log.d(TAG, "anqpRequestStart sr.bssid=" + sr.BSSID); - synchronized (mAnqpRequestLock) { - mAnqpRequest.add(sr); - } - } - - private void anqpRequestFinish(WifiPasspointInfo result) { - Log.d(TAG, "anqpRequestFinish pi.bssid=" + result.bssid); - synchronized (mAnqpRequestLock) { - for (ScanResult sr : mAnqpRequest) - if (sr.BSSID.equals(result.bssid)) { - Log.d(TAG, "find hit " + result.bssid); - /* sr.passpoint = result; */ - mAnqpRequest.remove(sr); - Log.d(TAG, "mAnqpRequest.len=" + mAnqpRequest.size()); - break; - } - } - } - - private void anqpRequestFinish(ScanResult sr) { - Log.d(TAG, "anqpRequestFinish sr.bssid=" + sr.BSSID); - synchronized (mAnqpRequestLock) { - for (ScanResult sr1 : mAnqpRequest) - if (sr1.BSSID.equals(sr.BSSID)) { - mAnqpRequest.remove(sr1); - break; - } - } - } - - class PasspointHandler extends Handler { - PasspointHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message message) { - Object listener = null; - - switch (message.what) { - case AsyncChannel.CMD_CHANNEL_DISCONNECTED: - if (mChannelListener != null) { - mChannelListener.onChannelDisconnected(); - mChannelListener = null; - } - break; - - case REQUEST_ANQP_INFO_SUCCEEDED: - WifiPasspointInfo result = (WifiPasspointInfo) message.obj; - anqpRequestFinish(result); - listener = getListener(message.arg2, false); - if (listener != null) { - ((ActionListener) listener).onSuccess(); - } - break; - - case REQUEST_ANQP_INFO_FAILED: - anqpRequestFinish((ScanResult) message.obj); - listener = getListener(message.arg2, false); - if (listener == null) - getListener(message.arg2, true); - if (listener != null) { - ((ActionListener) listener).onFailure(message.arg1); - } - break; - - case START_OSU_SUCCEEDED: - listener = getListener(message.arg2, true); - if (listener != null) { - ((OsuRemListener) listener).onSuccess(); - } - break; - - case START_OSU_FAILED: - listener = getListener(message.arg2, true); - if (listener != null) { - ((OsuRemListener) listener).onFailure(message.arg1); - } - break; - - case START_OSU_BROWSER: - listener = peekListener(message.arg2); - if (listener != null) { - ParcelableString str = (ParcelableString) message.obj; - if (str == null || str.string == null) - ((OsuRemListener) listener).onBrowserDismiss(); - else - ((OsuRemListener) listener).onBrowserLaunch(str.string); - } - break; - - default: - Log.d(TAG, "Ignored " + message); - break; - } - } - } - - } - - public static class ParcelableString implements Parcelable { - public String string; - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeString(string); - } - - public static final Parcelable.Creator<ParcelableString> CREATOR = - new Parcelable.Creator<ParcelableString>() { - @Override - public ParcelableString createFromParcel(Parcel in) { - ParcelableString ret = new ParcelableString(); - ret.string = in.readString(); - return ret; - } - @Override - public ParcelableString[] newArray(int size) { - return new ParcelableString[size]; - } - }; - } - - private static final int BASE = Protocol.BASE_WIFI_PASSPOINT_MANAGER; - - public static final int REQUEST_ANQP_INFO = BASE + 1; - public static final int REQUEST_ANQP_INFO_FAILED = BASE + 2; - public static final int REQUEST_ANQP_INFO_SUCCEEDED = BASE + 3; - public static final int REQUEST_OSU_ICON = BASE + 4; - public static final int REQUEST_OSU_ICON_FAILED = BASE + 5; - public static final int REQUEST_OSU_ICON_SUCCEEDED = BASE + 6; - public static final int START_OSU = BASE + 7; - public static final int START_OSU_BROWSER = BASE + 8; - public static final int START_OSU_FAILED = BASE + 9; - public static final int START_OSU_SUCCEEDED = BASE + 10; - - private Context mContext; - IWifiPasspointManager mService; - - /** - * TODO: doc - * @param context - * @param service - */ - public WifiPasspointManager(Context context, IWifiPasspointManager service) { - mContext = context; - mService = service; - } - - /** - * Registers the application with the framework. This function must be the - * first to be called before any async passpoint operations are performed. - * - * @param srcContext is the context of the source - * @param srcLooper is the Looper on which the callbacks are receivied - * @param listener for callback at loss of framework communication. Can be - * null. - * @return Channel instance that is necessary for performing any further - * passpoint operations - * - */ - public Channel initialize(Context srcContext, Looper srcLooper, ChannelListener listener) { - Messenger messenger = getMessenger(); - if (messenger == null) - return null; - - Channel c = new Channel(srcContext, srcLooper, listener); - if (c.mAsyncChannel.connectSync(srcContext, c.mHandler, messenger) - == AsyncChannel.STATUS_SUCCESSFUL) { - return c; - } else { - return null; - } - } - - /** - * STOPSHIP: temp solution, should use supplicant manager instead, check - * with b/13931972 - */ - public Messenger getMessenger() { - try { - return mService.getMessenger(); - } catch (RemoteException e) { - return null; - } - } - - public int getPasspointState() { - try { - return mService.getPasspointState(); - } catch (RemoteException e) { - return PASSPOINT_STATE_UNKNOWN; - } - } - - public void requestAnqpInfo(Channel c, List<ScanResult> requested, int mask, - ActionListener listener) { - Log.d(TAG, "requestAnqpInfo start"); - Log.d(TAG, "requested.size=" + requested.size()); - checkChannel(c); - List<ScanResult> list = new ArrayList<ScanResult>(); - for (ScanResult sr : requested) - if (sr.capabilities.contains("[HS20]")) { - list.add(sr); - c.anqpRequestStart(sr); - Log.d(TAG, "adding " + sr.BSSID); - } - int count = list.size(); - Log.d(TAG, "after filter, count=" + count); - if (count == 0) { - if (DBG) - Log.d(TAG, "ANQP info request contains no HS20 APs, skipped"); - listener.onSuccess(); - return; - } - int key = c.putListener(listener, count); - for (ScanResult sr : list) - c.mAsyncChannel.sendMessage(REQUEST_ANQP_INFO, mask, key, sr); - Log.d(TAG, "requestAnqpInfo end"); - } - - public void requestOsuIcons(Channel c, List<WifiPasspointOsuProvider> requested, - int resolution, ActionListener listener) { - } - - public List<WifiPasspointPolicy> requestCredentialMatch(List<ScanResult> requested) { - try { - return mService.requestCredentialMatch(requested); - } catch (RemoteException e) { - return null; - } - } - - /** - * Get a list of saved Passpoint credentials. Only those credentials owned - * by the caller will be returned. - * - * @return The list of credentials - */ - public List<WifiPasspointCredential> getCredentials() { - try { - return mService.getCredentials(); - } catch (RemoteException e) { - return null; - } - } - - /** - * Add a new Passpoint credential. - * - * @param cred The credential to be added - * @return {@code true} if the operation succeeds, {@code false} otherwise - */ - public boolean addCredential(WifiPasspointCredential cred) { - try { - return mService.addCredential(cred); - } catch (RemoteException e) { - return false; - } - } - - /** - * Update an existing Passpoint credential. Only system or the owner of this - * credential has the permission to do this. - * - * @param cred The credential to be updated - * @return {@code true} if the operation succeeds, {@code false} otherwise - */ - public boolean updateCredential(WifiPasspointCredential cred) { - try { - return mService.updateCredential(cred); - } catch (RemoteException e) { - return false; - } - } - - /** - * Remove an existing Passpoint credential. Only system or the owner of this - * credential has the permission to do this. - * - * @param cred The credential to be removed - * @return {@code true} if the operation succeeds, {@code false} otherwise - */ - public boolean removeCredential(WifiPasspointCredential cred) { - try { - return mService.removeCredential(cred); - } catch (RemoteException e) { - return false; - } - } - - public void startOsu(Channel c, WifiPasspointOsuProvider osu, OsuRemListener listener) { - Log.d(TAG, "startOsu start"); - checkChannel(c); - int key = c.putListener(listener); - c.mAsyncChannel.sendMessage(START_OSU, 0, key, osu); - Log.d(TAG, "startOsu end"); - } - - public void startRemediation(Channel c, OsuRemListener listener) { - } - - public void connect(WifiPasspointPolicy policy) { - } - - private static void checkChannel(Channel c) { - if (c == null) throw new IllegalArgumentException("Channel needs to be initialized"); - } -} diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java deleted file mode 100644 index b54b70ceb087..000000000000 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.wifi.passpoint; - -import android.os.Parcel; -import android.os.Parcelable; - -/** @hide */ -public class WifiPasspointOsuProvider implements Parcelable { - - /** TODO: doc - * @hide - */ - public static final int OSU_METHOD_UNKNOWN = -1; - - /** TODO: doc - * @hide - */ - public static final int OSU_METHOD_OMADM = 0; - - /** TODO: doc - * @hide - */ - public static final int OSU_METHOD_SOAP = 1; - - /** TODO: doc */ - public String ssid; - - /** TODO: doc */ - public String friendlyName; - - /** TODO: doc - * @hide - */ - public String serverUri; - - /** TODO: doc - * @hide - */ - public int osuMethod = OSU_METHOD_UNKNOWN; - - /** TODO: doc */ - public int iconWidth; - - /** TODO: doc */ - public int iconHeight; - - /** TODO: doc */ - public String iconType; - - /** TODO: doc */ - public String iconFileName; - - /** TODO: doc */ - public Object icon; // TODO: should change to image format - - /** TODO: doc */ - public String osuNai; - - /** TODO: doc */ - public String osuService; - - /** default constructor @hide */ - public WifiPasspointOsuProvider() { - // TODO - } - - /** copy constructor @hide */ - public WifiPasspointOsuProvider(WifiPasspointOsuProvider source) { - // TODO - } - - @Override - public String toString() { - StringBuffer sb = new StringBuffer(); - sb.append("SSID: ").append("<").append(ssid).append(">"); - if (friendlyName != null) - sb.append(" friendlyName: ").append("<").append(friendlyName).append(">"); - if (serverUri != null) - sb.append(" serverUri: ").append("<").append(serverUri).append(">"); - sb.append(" osuMethod: ").append("<").append(osuMethod).append(">"); - if (iconFileName != null) { - sb.append(" icon: <").append(iconWidth).append("x") - .append(iconHeight).append(" ") - .append(iconType).append(" ") - .append(iconFileName).append(">"); - } - if (osuNai != null) - sb.append(" osuNai: ").append("<").append(osuNai).append(">"); - if (osuService != null) - sb.append(" osuService: ").append("<").append(osuService).append(">"); - return sb.toString(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeString(ssid); - out.writeString(friendlyName); - out.writeString(serverUri); - out.writeInt(osuMethod); - out.writeInt(iconWidth); - out.writeInt(iconHeight); - out.writeString(iconType); - out.writeString(iconFileName); - out.writeString(osuNai); - out.writeString(osuService); - // TODO: icon image? - } - - public static final Parcelable.Creator<WifiPasspointOsuProvider> CREATOR = - new Parcelable.Creator<WifiPasspointOsuProvider>() { - @Override - public WifiPasspointOsuProvider createFromParcel(Parcel in) { - WifiPasspointOsuProvider osu = new WifiPasspointOsuProvider(); - osu.ssid = in.readString(); - osu.friendlyName = in.readString(); - osu.serverUri = in.readString(); - osu.osuMethod = in.readInt(); - osu.iconWidth = in.readInt(); - osu.iconHeight = in.readInt(); - osu.iconType = in.readString(); - osu.iconFileName = in.readString(); - osu.osuNai = in.readString(); - osu.osuService = in.readString(); - return osu; - } - - @Override - public WifiPasspointOsuProvider[] newArray(int size) { - return new WifiPasspointOsuProvider[size]; - } - }; -} diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.aidl b/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.aidl deleted file mode 100644 index 1d61da00ed15..000000000000 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2014, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.wifi.passpoint; - -parcelable WifiPasspointPolicy; diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java deleted file mode 100644 index c08877e8116b..000000000000 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java +++ /dev/null @@ -1,384 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.wifi.passpoint; - -import android.net.wifi.WifiConfiguration; -import android.os.Parcelable; -import android.os.Parcel; -import android.security.Credentials; -import android.util.Log; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; - - -/** @hide */ -public class WifiPasspointPolicy implements Parcelable { - - private final static String TAG = "PasspointPolicy"; - - /** @hide */ - public static final int HOME_SP = 0; - - /** @hide */ - public static final int ROAMING_PARTNER = 1; - - /** @hide */ - public static final int UNRESTRICTED = 2; - - private String mName; - private int mCredentialPriority; - private int mRoamingPriority; - private String mBssid; - private String mSsid; - private WifiPasspointCredential mCredential; - private int mRestriction;// Permitted values are "HomeSP", "RoamingPartner", or "Unrestricted" - private boolean mIsHomeSp; - - private final String INT_PRIVATE_KEY = "private_key"; - private final String INT_PHASE2 = "phase2"; - private final String INT_PASSWORD = "password"; - private final String INT_IDENTITY = "identity"; - private final String INT_EAP = "eap"; - private final String INT_CLIENT_CERT = "client_cert"; - private final String INT_CA_CERT = "ca_cert"; - private final String INT_ANONYMOUS_IDENTITY = "anonymous_identity"; - private final String INT_SIM_SLOT = "sim_slot"; - private final String INT_ENTERPRISEFIELD_NAME ="android.net.wifi.WifiConfiguration$EnterpriseField"; - private final String ISO8601DATEFORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; - private final String ENTERPRISE_PHASE2_MSCHAPV2 = "auth=MSCHAPV2"; - private final String ENTERPRISE_PHASE2_MSCHAP = "auth=MSCHAP"; - - /** @hide */ - public WifiPasspointPolicy(String name, String ssid, - String bssid, WifiPasspointCredential pc, - int restriction, boolean ishomesp) { - mName = name; - if (pc != null) { - mCredentialPriority = pc.getPriority(); - } - //PerProviderSubscription/<X+>/Policy/PreferredRoamingPartnerList/<X+>/Priority - mRoamingPriority = 128; //default priority value of 128 - mSsid = ssid; - mCredential = pc; - mBssid = bssid; - mRestriction = restriction; - mIsHomeSp = ishomesp; - } - - public String getSsid() { - return mSsid; - } - - /** @hide */ - public void setBssid(String bssid) { - mBssid = bssid; - } - - public String getBssid() { - return mBssid; - } - - /** @hide */ - public void setRestriction(int r) { - mRestriction = r; - } - - /** @hide */ - public int getRestriction() { - return mRestriction; - } - - /** @hide */ - public void setHomeSp(boolean b) { - mIsHomeSp = b; - } - - /** @hide */ - public boolean isHomeSp() { - return mIsHomeSp; - } - - /** @hide */ - public void setCredential(WifiPasspointCredential newCredential) { - mCredential = newCredential; - } - - public WifiPasspointCredential getCredential() { - // TODO: return a copy - return mCredential; - } - - /** @hide */ - public void setCredentialPriority(int priority) { - mCredentialPriority = priority; - } - - /** @hide */ - public void setRoamingPriority(int priority) { - mRoamingPriority = priority; - } - - public int getCredentialPriority() { - return mCredentialPriority; - } - - public int getRoamingPriority() { - return mRoamingPriority; - } - - public WifiConfiguration createWifiConfiguration() { - WifiConfiguration wfg = new WifiConfiguration(); - if (mBssid != null) { - Log.d(TAG, "create bssid:" + mBssid); - wfg.BSSID = mBssid; - } - - if (mSsid != null) { - Log.d(TAG, "create ssid:" + mSsid); - wfg.SSID = mSsid; - } - //TODO: 1. add pmf configuration - // 2. add ocsp configuration - // 3. add eap-sim configuration - /*Key management*/ - wfg.status = WifiConfiguration.Status.ENABLED; - wfg.allowedKeyManagement.clear(); - wfg.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); - wfg.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X); - - /*Group Ciphers*/ - wfg.allowedGroupCiphers.clear(); - wfg.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); - wfg.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP); - - /*Protocols*/ - wfg.allowedProtocols.clear(); - wfg.allowedProtocols.set(WifiConfiguration.Protocol.RSN); - wfg.allowedProtocols.set(WifiConfiguration.Protocol.WPA); - - Class[] enterpriseFieldArray = WifiConfiguration.class.getClasses(); - Class<?> enterpriseFieldClass = null; - - - for(Class<?> myClass : enterpriseFieldArray) { - if(myClass.getName().equals(INT_ENTERPRISEFIELD_NAME)) { - enterpriseFieldClass = myClass; - break; - } - } - Log.d(TAG, "class chosen " + enterpriseFieldClass.getName() ); - - - Field anonymousId = null, caCert = null, clientCert = null, - eap = null, identity = null, password = null, - phase2 = null, privateKey = null; - - Field[] fields = WifiConfiguration.class.getFields(); - - - for (Field tempField : fields) { - if (tempField.getName().trim().equals(INT_ANONYMOUS_IDENTITY)) { - anonymousId = tempField; - Log.d(TAG, "field " + anonymousId.getName() ); - } else if (tempField.getName().trim().equals(INT_CA_CERT)) { - caCert = tempField; - } else if (tempField.getName().trim().equals(INT_CLIENT_CERT)) { - clientCert = tempField; - Log.d(TAG, "field " + clientCert.getName() ); - } else if (tempField.getName().trim().equals(INT_EAP)) { - eap = tempField; - Log.d(TAG, "field " + eap.getName() ); - } else if (tempField.getName().trim().equals(INT_IDENTITY)) { - identity = tempField; - Log.d(TAG, "field " + identity.getName() ); - } else if (tempField.getName().trim().equals(INT_PASSWORD)) { - password = tempField; - Log.d(TAG, "field " + password.getName() ); - } else if (tempField.getName().trim().equals(INT_PHASE2)) { - phase2 = tempField; - Log.d(TAG, "field " + phase2.getName() ); - - } else if (tempField.getName().trim().equals(INT_PRIVATE_KEY)) { - privateKey = tempField; - } - } - - - Method setValue = null; - - for(Method m: enterpriseFieldClass.getMethods()) { - if(m.getName().trim().equals("setValue")) { - Log.d(TAG, "method " + m.getName() ); - setValue = m; - break; - } - } - - try { - // EAP - String eapmethod = mCredential.getType(); - Log.d(TAG, "eapmethod:" + eapmethod); - setValue.invoke(eap.get(wfg), eapmethod); - - // Username, password, EAP Phase 2 - if ("TTLS".equals(eapmethod)) { - setValue.invoke(phase2.get(wfg), ENTERPRISE_PHASE2_MSCHAPV2); - setValue.invoke(identity.get(wfg), mCredential.getUserName()); - setValue.invoke(password.get(wfg), mCredential.getPassword()); - setValue.invoke(anonymousId.get(wfg), "anonymous@" + mCredential.getRealm()); - } - - // EAP CA Certificate - String cacertificate = null; - String rootCA = mCredential.getCaRootCertPath(); - if (rootCA == null){ - cacertificate = null; - } else { - cacertificate = "keystore://" + Credentials.WIFI + "HS20" + Credentials.CA_CERTIFICATE + rootCA; - } - Log.d(TAG, "cacertificate:" + cacertificate); - setValue.invoke(caCert.get(wfg), cacertificate); - - //User certificate - if ("TLS".equals(eapmethod)) { - String usercertificate = null; - String privatekey = null; - String clientCertPath = mCredential.getClientCertPath(); - if (clientCertPath != null){ - privatekey = "keystore://" + Credentials.WIFI + "HS20" + Credentials.USER_PRIVATE_KEY + clientCertPath; - usercertificate = "keystore://" + Credentials.WIFI + "HS20" + Credentials.USER_CERTIFICATE + clientCertPath; - } - Log.d(TAG, "privatekey:" + privatekey); - Log.d(TAG, "usercertificate:" + usercertificate); - if (privatekey != null && usercertificate != null) { - setValue.invoke(privateKey.get(wfg), privatekey); - setValue.invoke(clientCert.get(wfg), usercertificate); - } - } - } catch (Exception e) { - Log.d(TAG, "createWifiConfiguration err:" + e); - } - - return wfg; - } - - /** {@inheritDoc} @hide */ - public int compareTo(WifiPasspointPolicy another) { - Log.d(TAG, "this:" + this); - Log.d(TAG, "another:" + another); - - if (another == null) { - return -1; - } else if (this.mIsHomeSp == true && another.isHomeSp() == false) { - //home sp priority is higher then roaming - Log.d(TAG, "compare HomeSP first, this is HomeSP, another isn't"); - return -1; - } else if ((this.mIsHomeSp == true && another.isHomeSp() == true)) { - Log.d(TAG, "both HomeSP"); - //if both home sp, compare credential priority - if (this.mCredentialPriority < another.getCredentialPriority()) { - Log.d(TAG, "this priority is higher"); - return -1; - } else if (this.mCredentialPriority == another.getCredentialPriority()) { - Log.d(TAG, "both priorities equal"); - //if priority still the same, compare name(ssid) - if (this.mName.compareTo(another.mName) != 0) { - Log.d(TAG, "compare mName return:" + this.mName.compareTo(another.mName)); - return this.mName.compareTo(another.mName); - } - /** - *if name still the same, compare credential - *the device may has two more credentials(TLS,SIM..etc) - *it can associate to one AP(same ssid). so we should compare by credential - */ - if (this.mCredential != null && another.mCredential != null) { - if (this.mCredential.compareTo(another.mCredential) != 0) { - Log.d(TAG, - "compare mCredential return:" + this.mName.compareTo(another.mName)); - return this.mCredential.compareTo(another.mCredential); - } - } - } else { - return 1; - } - } else if ((this.mIsHomeSp == false && another.isHomeSp() == false)) { - Log.d(TAG, "both RoamingSp"); - //if both roaming sp, compare roaming priority(preferredRoamingPartnerList/<X+>/priority) - if (this.mRoamingPriority < another.getRoamingPriority()) { - Log.d(TAG, "this priority is higher"); - return -1; - } else if (this.mRoamingPriority == another.getRoamingPriority()) {//priority equals, compare name - Log.d(TAG, "both priorities equal"); - //if priority still the same, compare name(ssid) - if (this.mName.compareTo(another.mName) != 0) { - Log.d(TAG, "compare mName return:" + this.mName.compareTo(another.mName)); - return this.mName.compareTo(another.mName); - } - //if name still the same, compare credential - if (this.mCredential != null && another.mCredential != null) { - if (this.mCredential.compareTo(another.mCredential) != 0) { - Log.d(TAG, - "compare mCredential return:" - + this.mCredential.compareTo(another.mCredential)); - return this.mCredential.compareTo(another.mCredential); - } - } - } else { - return 1; - } - } - - Log.d(TAG, "both policies equal"); - return 0; - } - - @Override - /** @hide */ - public String toString() { - return "PasspointPolicy: name=" + mName + " CredentialPriority=" + mCredentialPriority + - " mRoamingPriority" + mRoamingPriority + - " ssid=" + mSsid + " restriction=" + mRestriction + - " ishomesp=" + mIsHomeSp + " Credential=" + mCredential; - } - - /** Implement the Parcelable interface {@hide} */ - @Override - public int describeContents() { - return 0; - } - - /** Implement the Parcelable interface {@hide} */ - @Override - public void writeToParcel(Parcel dest, int flags) { - // TODO - } - - /** Implement the Parcelable interface {@hide} */ - public static final Creator<WifiPasspointPolicy> CREATOR = - new Creator<WifiPasspointPolicy>() { - @Override - public WifiPasspointPolicy createFromParcel(Parcel in) { - return null; - } - - @Override - public WifiPasspointPolicy[] newArray(int size) { - return new WifiPasspointPolicy[size]; - } - }; -} |