Merge "Make Fingerprint rename and delete user-aware"
diff --git a/Android.mk b/Android.mk
index 53e892f..1d797c4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -417,7 +417,6 @@
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 @@
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 da45517..93884aa 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -67,7 +67,7 @@
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";
@@ -6105,6 +6105,7 @@
method public void onCreate();
method public void onDestroy();
method public void onFullBackup(android.app.backup.FullBackupDataOutput) throws java.io.IOException;
+ method public void onQuotaExceeded(long, long);
method public abstract void onRestore(android.app.backup.BackupDataInput, int, android.os.ParcelFileDescriptor) throws java.io.IOException;
method public void onRestoreFile(android.os.ParcelFileDescriptor, long, java.io.File, int, long, long) throws java.io.IOException;
method public void onRestoreFinished();
@@ -10108,7 +10109,6 @@
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 +13841,7 @@
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 +20858,7 @@
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
}
@@ -21745,7 +21747,11 @@
method public android.media.session.MediaSession.Token getSessionToken();
method public boolean isConnected();
method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
+ method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void unsubscribe(java.lang.String);
+ method public void unsubscribe(java.lang.String, android.os.Bundle);
+ field public static final java.lang.String EXTRA_PAGE = "android.media.browse.extra.PAGE";
+ field public static final java.lang.String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
}
public static class MediaBrowser.ConnectionCallback {
@@ -21778,7 +21784,9 @@
public static abstract class MediaBrowser.SubscriptionCallback {
ctor public MediaBrowser.SubscriptionCallback();
method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
+ method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>, android.os.Bundle);
method public void onError(java.lang.String);
+ method public void onError(java.lang.String, android.os.Bundle);
}
}
@@ -22415,6 +22423,13 @@
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);
@@ -22666,6 +22681,7 @@
field public static final int FORMAT_AUDIBLE = 47364; // 0xb904
field public static final int FORMAT_AVI = 12298; // 0x300a
field public static final int FORMAT_BMP = 14340; // 0x3804
+ field public static final int FORMAT_DEFINED = 14336; // 0x3800
field public static final int FORMAT_DNG = 14353; // 0x3811
field public static final int FORMAT_DPOF = 12294; // 0x3006
field public static final int FORMAT_EXECUTABLE = 12291; // 0x3003
@@ -23955,6 +23971,7 @@
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();
@@ -23967,6 +23984,7 @@
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);
@@ -30849,10 +30867,14 @@
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";
@@ -33786,9 +33808,11 @@
method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public android.media.session.MediaSession.Token getSessionToken();
method public void notifyChildrenChanged(java.lang.String);
+ method public void notifyChildrenChanged(java.lang.String, android.os.Bundle);
method public android.os.IBinder onBind(android.content.Intent);
method public abstract android.service.media.MediaBrowserService.BrowserRoot onGetRoot(java.lang.String, int, android.os.Bundle);
method public abstract void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
+ method public void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>, android.os.Bundle);
method public void onLoadItem(java.lang.String, android.service.media.MediaBrowserService.Result<android.media.browse.MediaBrowser.MediaItem>);
method public void setSessionToken(android.media.session.MediaSession.Token);
field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
@@ -35247,6 +35271,7 @@
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
@@ -35387,6 +35412,7 @@
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
@@ -36401,10 +36427,15 @@
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 {
@@ -36416,32 +36447,49 @@
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();
@@ -36452,6 +36500,7 @@
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();
@@ -36460,9 +36509,11 @@
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";
@@ -37391,9 +37442,23 @@
public class Html {
method public static java.lang.String escapeHtml(java.lang.CharSequence);
- method public static android.text.Spanned fromHtml(java.lang.String);
- method public static android.text.Spanned fromHtml(java.lang.String, android.text.Html.ImageGetter, android.text.Html.TagHandler);
- method public static java.lang.String toHtml(android.text.Spanned);
+ method public static deprecated android.text.Spanned fromHtml(java.lang.String);
+ method public static android.text.Spanned fromHtml(java.lang.String, int);
+ method public static deprecated android.text.Spanned fromHtml(java.lang.String, android.text.Html.ImageGetter, android.text.Html.TagHandler);
+ method public static android.text.Spanned fromHtml(java.lang.String, int, android.text.Html.ImageGetter, android.text.Html.TagHandler);
+ method public static deprecated java.lang.String toHtml(android.text.Spanned);
+ method public static java.lang.String toHtml(android.text.Spanned, int);
+ field public static final int FROM_HTML_MODE_COMPACT = 63; // 0x3f
+ field public static final int FROM_HTML_MODE_LEGACY = 0; // 0x0
+ field public static final int FROM_HTML_OPTION_USE_CSS_COLORS = 256; // 0x100
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_BLOCKQUOTE = 32; // 0x20
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_DIV = 16; // 0x10
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_HEADING = 2; // 0x2
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST = 8; // 0x8
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM = 4; // 0x4
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH = 1; // 0x1
+ field public static final int TO_HTML_PARAGRAPH_LINES_CONSECUTIVE = 0; // 0x0
+ field public static final int TO_HTML_PARAGRAPH_LINES_INDIVIDUAL = 1; // 0x1
}
public static abstract interface Html.ImageGetter {
@@ -50926,6 +50991,7 @@
method public java.lang.Object invoke(java.lang.Object, java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.reflect.InvocationTargetException;
method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
method public boolean isBridge();
+ method public boolean isDefault();
method public boolean isSynthetic();
method public boolean isVarArgs();
method public java.lang.String toGenericString();
diff --git a/api/system-current.txt b/api/system-current.txt
index 302ebe1..3fc6437 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -95,7 +95,7 @@
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";
@@ -6254,6 +6254,7 @@
method public void onCreate();
method public void onDestroy();
method public void onFullBackup(android.app.backup.FullBackupDataOutput) throws java.io.IOException;
+ method public void onQuotaExceeded(long, long);
method public abstract void onRestore(android.app.backup.BackupDataInput, int, android.os.ParcelFileDescriptor) throws java.io.IOException;
method public void onRestoreFile(android.os.ParcelFileDescriptor, long, java.io.File, int, long, long) throws java.io.IOException;
method public void onRestoreFinished();
@@ -6316,6 +6317,7 @@
field public static final int ERROR_PACKAGE_NOT_FOUND = -2002; // 0xfffff82e
field public static final int ERROR_TRANSPORT_ABORTED = -1000; // 0xfffffc18
field public static final int ERROR_TRANSPORT_PACKAGE_REJECTED = -1002; // 0xfffffc16
+ field public static final int ERROR_TRANSPORT_QUOTA_EXCEEDED = -1005; // 0xfffffc13
field public static final int SUCCESS = 0; // 0x0
}
@@ -6348,6 +6350,7 @@
method public int finishBackup();
method public void finishRestore();
method public android.app.backup.RestoreSet[] getAvailableRestoreSets();
+ method public long getBackupQuota(java.lang.String, boolean);
method public android.os.IBinder getBinder();
method public long getCurrentRestoreSet();
method public int getNextFullRestoreDataChunk(android.os.ParcelFileDescriptor);
@@ -6373,6 +6376,7 @@
field public static final int TRANSPORT_NOT_INITIALIZED = -1001; // 0xfffffc17
field public static final int TRANSPORT_OK = 0; // 0x0
field public static final int TRANSPORT_PACKAGE_REJECTED = -1002; // 0xfffffc16
+ field public static final int TRANSPORT_QUOTA_EXCEEDED = -1005; // 0xfffffc13
}
public class FileBackupHelper extends android.app.backup.FileBackupHelperBase implements android.app.backup.BackupHelper {
@@ -10504,7 +10508,6 @@
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 +14245,7 @@
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 +22210,7 @@
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
}
@@ -23162,7 +23167,11 @@
method public android.media.session.MediaSession.Token getSessionToken();
method public boolean isConnected();
method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
+ method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void unsubscribe(java.lang.String);
+ method public void unsubscribe(java.lang.String, android.os.Bundle);
+ field public static final java.lang.String EXTRA_PAGE = "android.media.browse.extra.PAGE";
+ field public static final java.lang.String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
}
public static class MediaBrowser.ConnectionCallback {
@@ -23195,7 +23204,9 @@
public static abstract class MediaBrowser.SubscriptionCallback {
ctor public MediaBrowser.SubscriptionCallback();
method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
+ method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>, android.os.Bundle);
method public void onError(java.lang.String);
+ method public void onError(java.lang.String, android.os.Bundle);
}
}
@@ -23869,10 +23880,10 @@
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();
@@ -23902,6 +23913,18 @@
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);
@@ -24268,6 +24291,7 @@
field public static final int FORMAT_AUDIBLE = 47364; // 0xb904
field public static final int FORMAT_AVI = 12298; // 0x300a
field public static final int FORMAT_BMP = 14340; // 0x3804
+ field public static final int FORMAT_DEFINED = 14336; // 0x3800
field public static final int FORMAT_DNG = 14353; // 0x3811
field public static final int FORMAT_DPOF = 12294; // 0x3006
field public static final int FORMAT_EXECUTABLE = 12291; // 0x3003
@@ -25837,6 +25861,7 @@
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();
@@ -25849,6 +25874,7 @@
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);
@@ -32933,10 +32959,14 @@
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";
@@ -36003,9 +36033,11 @@
method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public android.media.session.MediaSession.Token getSessionToken();
method public void notifyChildrenChanged(java.lang.String);
+ method public void notifyChildrenChanged(java.lang.String, android.os.Bundle);
method public android.os.IBinder onBind(android.content.Intent);
method public abstract android.service.media.MediaBrowserService.BrowserRoot onGetRoot(java.lang.String, int, android.os.Bundle);
method public abstract void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
+ method public void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>, android.os.Bundle);
method public void onLoadItem(java.lang.String, android.service.media.MediaBrowserService.Result<android.media.browse.MediaBrowser.MediaItem>);
method public void setSessionToken(android.media.session.MediaSession.Token);
field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
@@ -37540,6 +37572,7 @@
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
@@ -37691,6 +37724,7 @@
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
@@ -38790,10 +38824,15 @@
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 {
@@ -38814,6 +38853,7 @@
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();
@@ -38826,30 +38866,46 @@
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);
@@ -38864,6 +38920,7 @@
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();
@@ -38879,11 +38936,13 @@
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);
@@ -39844,9 +39903,23 @@
public class Html {
method public static java.lang.String escapeHtml(java.lang.CharSequence);
- method public static android.text.Spanned fromHtml(java.lang.String);
- method public static android.text.Spanned fromHtml(java.lang.String, android.text.Html.ImageGetter, android.text.Html.TagHandler);
- method public static java.lang.String toHtml(android.text.Spanned);
+ method public static deprecated android.text.Spanned fromHtml(java.lang.String);
+ method public static android.text.Spanned fromHtml(java.lang.String, int);
+ method public static deprecated android.text.Spanned fromHtml(java.lang.String, android.text.Html.ImageGetter, android.text.Html.TagHandler);
+ method public static android.text.Spanned fromHtml(java.lang.String, int, android.text.Html.ImageGetter, android.text.Html.TagHandler);
+ method public static deprecated java.lang.String toHtml(android.text.Spanned);
+ method public static java.lang.String toHtml(android.text.Spanned, int);
+ field public static final int FROM_HTML_MODE_COMPACT = 63; // 0x3f
+ field public static final int FROM_HTML_MODE_LEGACY = 0; // 0x0
+ field public static final int FROM_HTML_OPTION_USE_CSS_COLORS = 256; // 0x100
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_BLOCKQUOTE = 32; // 0x20
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_DIV = 16; // 0x10
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_HEADING = 2; // 0x2
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST = 8; // 0x8
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM = 4; // 0x4
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH = 1; // 0x1
+ field public static final int TO_HTML_PARAGRAPH_LINES_CONSECUTIVE = 0; // 0x0
+ field public static final int TO_HTML_PARAGRAPH_LINES_INDIVIDUAL = 1; // 0x1
}
public static abstract interface Html.ImageGetter {
@@ -53715,6 +53788,7 @@
method public java.lang.Object invoke(java.lang.Object, java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.reflect.InvocationTargetException;
method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
method public boolean isBridge();
+ method public boolean isDefault();
method public boolean isSynthetic();
method public boolean isVarArgs();
method public java.lang.String toGenericString();
diff --git a/api/test-current.txt b/api/test-current.txt
index 050caa0..e2ad761 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -67,7 +67,7 @@
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";
@@ -6107,6 +6107,7 @@
method public void onCreate();
method public void onDestroy();
method public void onFullBackup(android.app.backup.FullBackupDataOutput) throws java.io.IOException;
+ method public void onQuotaExceeded(long, long);
method public abstract void onRestore(android.app.backup.BackupDataInput, int, android.os.ParcelFileDescriptor) throws java.io.IOException;
method public void onRestoreFile(android.os.ParcelFileDescriptor, long, java.io.File, int, long, long) throws java.io.IOException;
method public void onRestoreFinished();
@@ -10116,7 +10117,6 @@
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 +13849,7 @@
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 +20866,7 @@
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
}
@@ -21753,7 +21755,11 @@
method public android.media.session.MediaSession.Token getSessionToken();
method public boolean isConnected();
method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
+ method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void unsubscribe(java.lang.String);
+ method public void unsubscribe(java.lang.String, android.os.Bundle);
+ field public static final java.lang.String EXTRA_PAGE = "android.media.browse.extra.PAGE";
+ field public static final java.lang.String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
}
public static class MediaBrowser.ConnectionCallback {
@@ -21786,7 +21792,9 @@
public static abstract class MediaBrowser.SubscriptionCallback {
ctor public MediaBrowser.SubscriptionCallback();
method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
+ method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>, android.os.Bundle);
method public void onError(java.lang.String);
+ method public void onError(java.lang.String, android.os.Bundle);
}
}
@@ -22423,6 +22431,13 @@
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);
@@ -22674,6 +22689,7 @@
field public static final int FORMAT_AUDIBLE = 47364; // 0xb904
field public static final int FORMAT_AVI = 12298; // 0x300a
field public static final int FORMAT_BMP = 14340; // 0x3804
+ field public static final int FORMAT_DEFINED = 14336; // 0x3800
field public static final int FORMAT_DNG = 14353; // 0x3811
field public static final int FORMAT_DPOF = 12294; // 0x3006
field public static final int FORMAT_EXECUTABLE = 12291; // 0x3003
@@ -23963,6 +23979,7 @@
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();
@@ -23975,6 +23992,7 @@
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);
@@ -30861,10 +30879,14 @@
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";
@@ -33800,9 +33822,11 @@
method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public android.media.session.MediaSession.Token getSessionToken();
method public void notifyChildrenChanged(java.lang.String);
+ method public void notifyChildrenChanged(java.lang.String, android.os.Bundle);
method public android.os.IBinder onBind(android.content.Intent);
method public abstract android.service.media.MediaBrowserService.BrowserRoot onGetRoot(java.lang.String, int, android.os.Bundle);
method public abstract void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
+ method public void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>, android.os.Bundle);
method public void onLoadItem(java.lang.String, android.service.media.MediaBrowserService.Result<android.media.browse.MediaBrowser.MediaItem>);
method public void setSessionToken(android.media.session.MediaSession.Token);
field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
@@ -35261,6 +35285,7 @@
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
@@ -35401,6 +35426,7 @@
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
@@ -36415,10 +36441,15 @@
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 {
@@ -36430,32 +36461,49 @@
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();
@@ -36466,6 +36514,7 @@
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();
@@ -36474,9 +36523,11 @@
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";
@@ -37407,9 +37458,23 @@
public class Html {
method public static java.lang.String escapeHtml(java.lang.CharSequence);
- method public static android.text.Spanned fromHtml(java.lang.String);
- method public static android.text.Spanned fromHtml(java.lang.String, android.text.Html.ImageGetter, android.text.Html.TagHandler);
- method public static java.lang.String toHtml(android.text.Spanned);
+ method public static deprecated android.text.Spanned fromHtml(java.lang.String);
+ method public static android.text.Spanned fromHtml(java.lang.String, int);
+ method public static deprecated android.text.Spanned fromHtml(java.lang.String, android.text.Html.ImageGetter, android.text.Html.TagHandler);
+ method public static android.text.Spanned fromHtml(java.lang.String, int, android.text.Html.ImageGetter, android.text.Html.TagHandler);
+ method public static deprecated java.lang.String toHtml(android.text.Spanned);
+ method public static java.lang.String toHtml(android.text.Spanned, int);
+ field public static final int FROM_HTML_MODE_COMPACT = 63; // 0x3f
+ field public static final int FROM_HTML_MODE_LEGACY = 0; // 0x0
+ field public static final int FROM_HTML_OPTION_USE_CSS_COLORS = 256; // 0x100
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_BLOCKQUOTE = 32; // 0x20
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_DIV = 16; // 0x10
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_HEADING = 2; // 0x2
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST = 8; // 0x8
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM = 4; // 0x4
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH = 1; // 0x1
+ field public static final int TO_HTML_PARAGRAPH_LINES_CONSECUTIVE = 0; // 0x0
+ field public static final int TO_HTML_PARAGRAPH_LINES_INDIVIDUAL = 1; // 0x1
}
public static abstract interface Html.ImageGetter {
@@ -50942,6 +51007,7 @@
method public java.lang.Object invoke(java.lang.Object, java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.reflect.InvocationTargetException;
method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
method public boolean isBridge();
+ method public boolean isDefault();
method public boolean isSynthetic();
method public boolean isVarArgs();
method public java.lang.String toGenericString();
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 1d9e3bb..35695c4 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -430,47 +430,46 @@
}
/**
- * 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 @@
/**
* 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 @@
}
/**
- * 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><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>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.
*
- * <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 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 @@
* @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 @@
* <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 @@
* @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 @@
/**
* 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 @@
* <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 @@
* {@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 f3e1fc3..1e7457c 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3328,17 +3328,6 @@
}
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 @@
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 @@
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 @@
}
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 @@
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 @@
/*
* 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/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index fe8e228..eda9603 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -105,6 +105,27 @@
void doMeasureFullBackup(int token, IBackupManager callbackBinder);
/**
+ * Tells the application agent that the backup data size exceeded current transport quota.
+ * Later calls to {@link #onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)}
+ * and {@link #onFullBackup(FullBackupDataOutput)} could use this information
+ * to reduce backup size under the limit.
+ * However, the quota can change, so do not assume that the value passed in here is absolute,
+ * similarly all subsequent backups should not be restricted to this size.
+ * This callback will be invoked before data has been put onto the wire in a preflight check,
+ * so it is relatively inexpensive to hit your quota.
+ * Apps that hit quota repeatedly without dealing with it can be subject to having their backup
+ * schedule reduced.
+ * The {@code quotaBytes} is a loose guideline b/c of metadata added by the backupmanager
+ * so apps should be more aggressive in trimming their backup set.
+ *
+ * @param backupDataBytes Expected or already processed amount of data.
+ * Could be less than total backup size if backup process was interrupted
+ * before finish of processing all backup data.
+ * @param quotaBytes Current amount of backup data that is allowed for the app.
+ */
+ void doQuotaExceeded(long backupDataBytes, long quotaBytes);
+
+ /**
* Restore a single "file" to the application. The file was typically obtained from
* a full-backup dataset. The agent reads 'size' bytes of file content
* from the provided file descriptor.
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 368b8ef..3288cd9 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -48,13 +48,12 @@
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 260216c..94e584e 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -35,6 +35,9 @@
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 @@
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 @@
? 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 @@
+ " key=" + key + " overrideConfig=" + overrideConfiguration);
return r;
}
+ findSystemLocales = (mSystemLocales.length == 0);
+ hasNonSystemLocales = mHasNonSystemLocales;
}
//if (r != null) {
@@ -243,6 +254,18 @@
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 @@
// 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 @@
| 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 @@
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 @@
if (tmpConfig == null) {
tmpConfig = new Configuration();
}
- tmpConfig.setTo(config);
+ tmpConfig.setTo(localeAdjustedConfig);
if (!isDefaultDisplay) {
dm = getDisplayMetricsLocked(displayId);
applyNonDefaultDisplayMetricsToConfigurationLocked(dm, tmpConfig);
@@ -320,7 +377,7 @@
}
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 f3b1175..8035c56 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -82,8 +82,6 @@
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 @@
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/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index aa4a631..63f1425 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -382,6 +382,28 @@
}
/**
+ * Tells the application agent that the backup data size exceeded current transport quota.
+ * Later calls to {@link #onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)}
+ * and {@link #onFullBackup(FullBackupDataOutput)} could use this information
+ * to reduce backup size under the limit.
+ * However, the quota can change, so do not assume that the value passed in here is absolute,
+ * similarly all subsequent backups should not be restricted to this size.
+ * This callback will be invoked before data has been put onto the wire in a preflight check,
+ * so it is relatively inexpensive to hit your quota.
+ * Apps that hit quota repeatedly without dealing with it can be subject to having their backup
+ * schedule reduced.
+ * The {@code quotaBytes} is a loose guideline b/c of metadata added by the backupmanager
+ * so apps should be more aggressive in trimming their backup set.
+ *
+ * @param backupDataBytes Expected or already processed amount of data.
+ * Could be less than total backup size if backup process was interrupted
+ * before finish of processing all backup data.
+ * @param quotaBytes Current amount of backup data that is allowed for the app.
+ */
+ public void onQuotaExceeded(long backupDataBytes, long quotaBytes) {
+ }
+
+ /**
* Check whether the xml yielded any <include/> tag for the provided <code>domainToken</code>.
* If so, perform a {@link #fullBackupFileTree} which backs up the file or recurses if the path
* is a directory.
@@ -955,6 +977,21 @@
public void fail(String message) {
getHandler().post(new FailRunnable(message));
}
+
+ @Override
+ public void doQuotaExceeded(long backupDataBytes, long quotaBytes) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ BackupAgent.this.onQuotaExceeded(backupDataBytes, quotaBytes);
+ } catch (Exception e) {
+ Log.d(TAG, "onQuotaExceeded(" + BackupAgent.this.getClass().getName() + ") threw",
+ e);
+ throw e;
+ } finally {
+ waitForSharedPrefs();
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
static class FailRunnable implements Runnable {
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index c27eaa4f..ff62b7c 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -110,6 +110,16 @@
BackupTransport.TRANSPORT_PACKAGE_REJECTED;
/**
+ * Returned when the transport reject the attempt to backup because
+ * backup data size exceeded current quota limit for this package.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_TRANSPORT_QUOTA_EXCEEDED =
+ BackupTransport.TRANSPORT_QUOTA_EXCEEDED;
+
+ /**
* The {@link BackupAgent} for the requested package failed for some reason
* and didn't provide appropriate backup data.
*
diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java
index aca115f..ac23b68 100644
--- a/core/java/android/app/backup/BackupTransport.java
+++ b/core/java/android/app/backup/BackupTransport.java
@@ -49,6 +49,7 @@
public static final int TRANSPORT_PACKAGE_REJECTED = -1002;
public static final int AGENT_ERROR = -1003;
public static final int AGENT_UNKNOWN = -1004;
+ public static final int TRANSPORT_QUOTA_EXCEEDED = -1005;
// Indicates that operation was initiated by user, not a scheduled one.
// Transport should ignore its own moratoriums for call with this flag set.
@@ -494,6 +495,18 @@
return true;
}
+ /**
+ * Ask the transport about current quota for backup size of the package.
+ *
+ * @param packageName ID of package to provide the quota.
+ * @param isFullBackup If set, transport should return limit for full data backup, otherwise
+ * for key-value backup.
+ * @return Current limit on full data backup size in bytes.
+ */
+ public long getBackupQuota(String packageName, boolean isFullBackup) {
+ return Long.MAX_VALUE;
+ }
+
// ------------------------------------------------------------------------------------
// Full restore interfaces
@@ -677,6 +690,11 @@
}
@Override
+ public long getBackupQuota(String packageName, boolean isFullBackup) {
+ return BackupTransport.this.getBackupQuota(packageName, isFullBackup);
+ }
+
+ @Override
public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) {
return BackupTransport.this.getNextFullRestoreDataChunk(socket);
}
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 74cb0f6..9cd7d05 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -104,8 +104,6 @@
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 0b80f8a..6dfefac 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 @@
// 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 48d0196..9df7a28 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2561,7 +2561,6 @@
//@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 @@
/**
* 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 ed64ead..e404429 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -162,10 +162,6 @@
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 @@
}
/**
- * 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 @@
private PluralRules getPluralRule() {
synchronized (sSync) {
if (mPluralRule == null) {
- mPluralRule = PluralRules.forLocale(mResolvedLocale);
+ mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().getPrimary());
}
return mPluralRule;
}
@@ -457,7 +443,7 @@
@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 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 @@
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 @@
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 @@
}
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 2695dfd..2aa6af6 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2787,22 +2787,39 @@
/**
* <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 @@
* ({@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 @@
* @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 63bcb31..28bb22a 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -314,7 +314,7 @@
* </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 @@
* </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 @@
* </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 @@
* #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 @@
* </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 @@
* </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 f61892e..d58ad22 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -964,23 +964,102 @@
//
/**
- * <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 c3cae65..79eff4f 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1599,13 +1599,13 @@
* <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 7b9d1a3..5748726 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2106,13 +2106,13 @@
* <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/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 7cb086f..92c721b 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1611,7 +1611,7 @@
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 777baab..66181e0 100644
--- a/core/java/android/print/PrintJob.java
+++ b/core/java/android/print/PrintJob.java
@@ -19,6 +19,8 @@
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 @@
*/
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 @@
*
* @return The id.
*/
- public @NonNull PrintJobId getId() {
+ public @Nullable PrintJobId getId() {
return mCachedInfo.getId();
}
@@ -58,7 +60,7 @@
*
* @return The print job info.
*/
- public @Nullable PrintJobInfo getInfo() {
+ public @NonNull PrintJobInfo getInfo() {
if (isInImmutableState()) {
return mCachedInfo;
}
@@ -193,11 +195,17 @@
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 a2ee02b..186ae9b 100644
--- a/core/java/android/print/PrintJobId.java
+++ b/core/java/android/print/PrintJobId.java
@@ -19,7 +19,8 @@
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 @@
* 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 @@
*
* @hide
*/
- public PrintJobId(String value) {
+ public PrintJobId(@NonNull String value) {
mValue = value;
}
@@ -53,7 +54,7 @@
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 @@
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 @@
*
* @hide
*/
- public static PrintJobId unflattenFromString(String string) {
+ public static @NonNull PrintJobId unflattenFromString(@NonNull String string) {
return new PrintJobId(string);
}
@@ -112,7 +113,7 @@
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 21836b3..7e3a72f 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -244,7 +244,7 @@
*
* @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 3eb4874..58f260c 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -535,7 +535,10 @@
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 @@
}
}
- 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 abb441b..c587edd 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 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 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 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 @@
}
}
- 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 3104492..62d214e 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 @@
* @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 1bfb3c0..330fcf6 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -8704,6 +8704,120 @@
"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/security/net/config/CertificateSource.java b/core/java/android/security/net/config/CertificateSource.java
index 7e3601e..f3272e4 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 ff728ef..742d430 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 @@
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 bf29efa..b2c068c 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.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 @@
});
}
+ @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 b6105cd..ba5dd83 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.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 @@
}
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 0a2edff..ebe14691 100644
--- a/core/java/android/security/net/config/NetworkSecurityConfig.java
+++ b/core/java/android/security/net/config/NetworkSecurityConfig.java
@@ -145,6 +145,15 @@
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 e489c2c..8803c4b 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.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 @@
}
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 4a90f82..c2f29be 100644
--- a/core/java/android/security/net/config/TrustedCertificateStoreAdapter.java
+++ b/core/java/android/security/net/config/TrustedCertificateStoreAdapter.java
@@ -42,6 +42,11 @@
}
@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/text/Html.java b/core/java/android/text/Html.java
index 3d8ab1e..7e73b0c 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -16,7 +16,6 @@
package android.text;
-import android.graphics.Color;
import com.android.internal.util.ArrayUtils;
import org.ccil.cowan.tagsoup.HTMLSchema;
import org.ccil.cowan.tagsoup.Parser;
@@ -29,10 +28,12 @@
import android.content.res.ColorStateList;
import android.content.res.Resources;
+import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.AlignmentSpan;
+import android.text.style.BulletSpan;
import android.text.style.CharacterStyle;
import android.text.style.ForegroundColorSpan;
import android.text.style.ImageSpan;
@@ -85,18 +86,104 @@
Editable output, XMLReader xmlReader);
}
+ /**
+ * Option for {@link #toHtml(Spanned, int)}: Wrap consecutive lines of text delimited by '\n'
+ * inside <p> elements. {@link BulletSpan}s are ignored.
+ */
+ public static final int TO_HTML_PARAGRAPH_LINES_CONSECUTIVE = 0x00000000;
+
+ /**
+ * Option for {@link #toHtml(Spanned, int)}: Wrap each line of text delimited by '\n' inside a
+ * <p> or a <li> element. This allows {@link ParagraphStyle}s attached to be
+ * encoded as CSS styles within the corresponding <p> or <li> element.
+ */
+ public static final int TO_HTML_PARAGRAPH_LINES_INDIVIDUAL = 0x00000001;
+
+ /**
+ * Flag indicating that texts inside <p> elements will be separated from other texts with
+ * one newline character by default.
+ */
+ public static final int FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH = 0x00000001;
+
+ /**
+ * Flag indicating that texts inside <h1>~<h6> elements will be separated from
+ * other texts with one newline character by default.
+ */
+ public static final int FROM_HTML_SEPARATOR_LINE_BREAK_HEADING = 0x00000002;
+
+ /**
+ * Flag indicating that texts inside <li> elements will be separated from other texts
+ * with one newline character by default.
+ */
+ public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM = 0x00000004;
+
+ /**
+ * Flag indicating that texts inside <ul> elements will be separated from other texts
+ * with one newline character by default.
+ */
+ public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST = 0x00000008;
+
+ /**
+ * Flag indicating that texts inside <div> elements will be separated from other texts
+ * with one newline character by default.
+ */
+ public static final int FROM_HTML_SEPARATOR_LINE_BREAK_DIV = 0x00000010;
+
+ /**
+ * Flag indicating that texts inside <blockquote> elements will be separated from other
+ * texts with one newline character by default.
+ */
+ public static final int FROM_HTML_SEPARATOR_LINE_BREAK_BLOCKQUOTE = 0x00000020;
+
+ /**
+ * Flag indicating that CSS color values should be used instead of those defined in
+ * {@link Color}.
+ */
+ public static final int FROM_HTML_OPTION_USE_CSS_COLORS = 0x00000100;
+
+ /**
+ * Flags for {@link #fromHtml(String, int, ImageGetter, TagHandler)}: Separate block-level
+ * elements with blank lines (two newline characters) in between. This is the legacy behavior
+ * prior to N.
+ */
+ public static final int FROM_HTML_MODE_LEGACY = 0x00000000;
+
+ /**
+ * Flags for {@link #fromHtml(String, int, ImageGetter, TagHandler)}: Separate block-level
+ * elements with line breaks (single newline character) in between. This inverts the
+ * {@link Spanned} to HTML string conversion done with the option
+ * {@link #TO_HTML_PARAGRAPH_LINES_INDIVIDUAL}.
+ */
+ public static final int FROM_HTML_MODE_COMPACT =
+ FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH
+ | FROM_HTML_SEPARATOR_LINE_BREAK_HEADING
+ | FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM
+ | FROM_HTML_SEPARATOR_LINE_BREAK_LIST
+ | FROM_HTML_SEPARATOR_LINE_BREAK_DIV
+ | FROM_HTML_SEPARATOR_LINE_BREAK_BLOCKQUOTE;
+
private Html() { }
/**
- * Returns displayable styled text from the provided HTML string.
- * Any <img> tags in the HTML will display as a generic
- * replacement image which your program can then go through and
+ * Returns displayable styled text from the provided HTML string with the legacy flags
+ * {@link #FROM_HTML_MODE_LEGACY}.
+ *
+ * @deprecated use {@link #fromHtml(String, int)} instead.
+ */
+ @Deprecated
+ public static Spanned fromHtml(String source) {
+ return fromHtml(source, FROM_HTML_MODE_LEGACY, null, null);
+ }
+
+ /**
+ * Returns displayable styled text from the provided HTML string. Any <img> tags in the
+ * HTML will display as a generic replacement image which your program can then go through and
* replace with real images.
*
* <p>This uses TagSoup to handle real HTML, including all of the brokenness found in the wild.
*/
- public static Spanned fromHtml(String source) {
- return fromHtml(source, null, null);
+ public static Spanned fromHtml(String source, int flags) {
+ return fromHtml(source, flags, null, null);
}
/**
@@ -109,16 +196,26 @@
}
/**
- * Returns displayable styled text from the provided HTML string.
- * Any <img> tags in the HTML will use the specified ImageGetter
- * to request a representation of the image (use null if you don't
- * want this) and the specified TagHandler to handle unknown tags
- * (specify null if you don't want this).
+ * Returns displayable styled text from the provided HTML string with the legacy flags
+ * {@link #FROM_HTML_MODE_LEGACY}.
+ *
+ * @deprecated use {@link #fromHtml(String, int, ImageGetter, TagHandler)} instead.
+ */
+ @Deprecated
+ public static Spanned fromHtml(String source, ImageGetter imageGetter, TagHandler tagHandler) {
+ return fromHtml(source, FROM_HTML_MODE_LEGACY, imageGetter, tagHandler);
+ }
+
+ /**
+ * Returns displayable styled text from the provided HTML string. Any <img> tags in the
+ * HTML will use the specified ImageGetter to request a representation of the image (use null
+ * if you don't want this) and the specified TagHandler to handle unknown tags (specify null if
+ * you don't want this).
*
* <p>This uses TagSoup to handle real HTML, including all of the brokenness found in the wild.
*/
- public static Spanned fromHtml(String source, ImageGetter imageGetter,
- TagHandler tagHandler) {
+ public static Spanned fromHtml(String source, int flags, ImageGetter imageGetter,
+ TagHandler tagHandler) {
Parser parser = new Parser();
try {
parser.setProperty(Parser.schemaProperty, HtmlParser.schema);
@@ -131,20 +228,29 @@
}
HtmlToSpannedConverter converter =
- new HtmlToSpannedConverter(source, imageGetter, tagHandler,
- parser);
+ new HtmlToSpannedConverter(source, imageGetter, tagHandler, parser, flags);
return converter.convert();
}
/**
+ * @deprecated use {@link #toHtml(Spanned, int)} instead.
+ */
+ @Deprecated
+ public static String toHtml(Spanned text) {
+ return toHtml(text, TO_HTML_PARAGRAPH_LINES_CONSECUTIVE);
+ }
+
+ /**
* Returns an HTML representation of the provided Spanned text. A best effort is
* made to add HTML tags corresponding to spans. Also note that HTML metacharacters
* (such as "<" and "&") within the input text are escaped.
*
* @param text input text to convert
+ * @param option one of {@link #TO_HTML_PARAGRAPH_LINES_CONSECUTIVE} or
+ * {@link #TO_HTML_PARAGRAPH_LINES_INDIVIDUAL}
* @return string containing input converted to HTML
*/
- public static String toHtml(Spanned text) {
+ public static String toHtml(Spanned text, int option) {
StringBuilder out = new StringBuilder();
withinHtml(out, text);
return out.toString();
@@ -431,15 +537,16 @@
private SpannableStringBuilder mSpannableStringBuilder;
private Html.ImageGetter mImageGetter;
private Html.TagHandler mTagHandler;
+ private int mFlags;
- public HtmlToSpannedConverter(
- String source, Html.ImageGetter imageGetter, Html.TagHandler tagHandler,
- Parser parser) {
+ public HtmlToSpannedConverter( String source, Html.ImageGetter imageGetter,
+ Html.TagHandler tagHandler, Parser parser, int flags) {
mSource = source;
mSpannableStringBuilder = new SpannableStringBuilder();
mImageGetter = imageGetter;
mTagHandler = tagHandler;
mReader = parser;
+ mFlags = flags;
}
public Spanned convert() {
diff --git a/core/java/android/util/LocaleList.java b/core/java/android/util/LocaleList.java
index 8a2d015..90a20bc 100644
--- a/core/java/android/util/LocaleList.java
+++ b/core/java/android/util/LocaleList.java
@@ -26,6 +26,8 @@
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 @@
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 @@
*/
@Nullable
public Locale getFirstMatchWithEnglishSupported(String[] supportedLocales) {
- return computeFirstMatch(supportedLocales, true /* assume English is supported */);
+ return computeFirstMatch(Arrays.asList(supportedLocales),
+ true /* assume English is supported */);
}
/**
- * Returns true if the array of locale tags only contains empty locales and pseudolocales.
+ * {@hide}
+ */
+ public int getFirstMatchIndexWithEnglishSupported(Collection<String> supportedLocales) {
+ return computeFirstMatchIndex(supportedLocales, true /* assume English is supported */);
+ }
+
+ /**
+ * {@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 @@
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 @@
@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 @@
* 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 @@
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 @@
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 @@
}
/**
+ * 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 @@
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 8c948a9..5411dad 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 @@
private final IDropPermissions mDropPermissions;
+ private IBinder mPermissionOwnerToken;
+
private final CloseGuard mCloseGuard = CloseGuard.get();
/**
@@ -80,11 +83,29 @@
}
/**
+ * 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 67473c6..617d3dd 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -77,6 +77,7 @@
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 @@
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 5ac786a..71c2c21 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.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 @@
* 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 @@
*
* <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 2191c58a..210adce 100644
--- a/core/java/com/android/internal/app/LocaleStore.java
+++ b/core/java/com/android/internal/app/LocaleStore.java
@@ -179,6 +179,34 @@
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 @@
/**
* 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 2f855c6..c4ec714 100644
--- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
+++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
@@ -45,7 +45,7 @@
* 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 @@
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 @@
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 @@
// 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 @@
}
@Override
- @SuppressWarnings("unchecked")
protected void publishResults(CharSequence constraint, FilterResults results) {
mLocaleOptions = (ArrayList<LocaleStore.LocaleInfo>) results.values;
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index 444dbfa..98f5bea 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -249,6 +249,16 @@
*/
boolean isAppEligibleForBackup(in PackageInfo targetPackage, boolean isFullBackup);
+ /**
+ * Ask the transport about current quota for backup size of the package.
+ *
+ * @param packageName ID of package to provide the quota.
+ * @param isFullBackup If set, transport should return limit for full data backup, otherwise
+ * for key-value backup.
+ * @return Current limit on full data backup size in bytes.
+ */
+ long getBackupQuota(String packageName, boolean isFullBackup);
+
// full restore stuff
/**
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 22d35f2..8cdfc64 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -71,6 +71,9 @@
// The currently-active restore set always has the same (nonzero!) token
private static final long CURRENT_SET_TOKEN = 1;
+ // Full backup size quota is set to reasonable value.
+ private static final long FULL_BACKUP_SIZE_QUOTA = 25 * 1024 * 1024;
+
private Context mContext;
private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
private File mCurrentSetDir = new File(mDataDir, Long.toString(CURRENT_SET_TOKEN));
@@ -90,6 +93,7 @@
private FileInputStream mSocketInputStream;
private BufferedOutputStream mFullBackupOutputStream;
private byte[] mFullBackupBuffer;
+ private long mFullBackupSize;
private FileInputStream mCurFullRestoreStream;
private FileOutputStream mFullRestoreSocketStream;
@@ -314,8 +318,13 @@
@Override
public int checkFullBackupSize(long size) {
+ int result = TRANSPORT_OK;
// Decline zero-size "backups"
- final int result = (size > 0) ? TRANSPORT_OK : TRANSPORT_PACKAGE_REJECTED;
+ if (size <= 0) {
+ result = TRANSPORT_PACKAGE_REJECTED;
+ } else if (size > FULL_BACKUP_SIZE_QUOTA) {
+ result = TRANSPORT_QUOTA_EXCEEDED;
+ }
if (result != TRANSPORT_OK) {
if (DEBUG) {
Log.v(TAG, "Declining backup of size " + size);
@@ -339,6 +348,7 @@
// sure to dup() our own copy of the socket fd. Transports which run in
// their own processes must not do this.
try {
+ mFullBackupSize = 0;
mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
mSocketInputStream = new FileInputStream(mSocket.getFileDescriptor());
} catch (IOException e) {
@@ -359,6 +369,11 @@
return TRANSPORT_ERROR;
}
+ mFullBackupSize += numBytes;
+ if (mFullBackupSize > FULL_BACKUP_SIZE_QUOTA) {
+ return TRANSPORT_QUOTA_EXCEEDED;
+ }
+
if (numBytes > mFullBackupBuffer.length) {
mFullBackupBuffer = new byte[numBytes];
}
@@ -699,4 +714,8 @@
return TRANSPORT_OK;
}
+ @Override
+ public long getBackupQuota(String packageName, boolean isFullBackup) {
+ return isFullBackup ? FULL_BACKUP_SIZE_QUOTA : Long.MAX_VALUE;
+ }
}
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index 381e71f..2f26e92 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -42,6 +42,23 @@
* @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 @@
*
* @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 2438bda..74ff4b4 100644
--- a/core/java/com/android/internal/view/IDropPermissions.aidl
+++ b/core/java/com/android/internal/view/IDropPermissions.aidl
@@ -24,5 +24,6 @@
*/
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 0b23ee6..3f468ac 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1425,25 +1425,32 @@
*/
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;
}
/**
@@ -1454,7 +1461,7 @@
* @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);
}
/**
@@ -1484,7 +1491,7 @@
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 61dc6e4..2018e76 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 @@
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 @@
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 34877e0..35b5016 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -86,20 +86,20 @@
}
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 @@
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 @@
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 3d4b8b4..56c3fc8 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 0000000..aaa2dbc
--- /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 ba79ecd..0000000
--- 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 655a5fa..507925b7 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 28c76bb..a276854 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 d5349b2..b660277 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1397,6 +1397,16 @@
<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 57ff243..81705b4 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 ee8921e..eb055de 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -32,7 +32,8 @@
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 eb0075b..bfa2b10 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 0000000..abbebda
--- /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 55c0031..0000000
--- 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 0000000..19ce44a
--- /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 0000000..5179b42
--- /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 0000000..86074a6
--- /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 0000000..9c11c22
--- /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 0000000..4e89207
--- /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 0000000..26b7cae
--- /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/core/tests/coretests/src/android/print/mockservice/SettingsActivity.java b/core/tests/coretests/src/android/print/mockservice/SettingsActivity.java
new file mode 100644
index 0000000..fb76e67
--- /dev/null
+++ b/core/tests/coretests/src/android/print/mockservice/SettingsActivity.java
@@ -0,0 +1,28 @@
+/*
+ * 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.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 0000000..b58b2735
--- /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 0000000..04683f2
--- /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 cfd9467..8c392aa 100644
--- a/docs/html/preview/support.jd
+++ b/docs/html/preview/support.jd
@@ -160,20 +160,21 @@
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 db9ab3a..4c399f9 100644
--- a/docs/html/training/id-auth/identify.jd
+++ b/docs/html/training/id-auth/identify.jd
@@ -15,8 +15,7 @@
<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 @@
<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 84ca546..1857345 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -19,7 +19,6 @@
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 @@
/** 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 @@
@Override
public void draw(Canvas canvas) {
mAnimatedVectorState.mVectorDrawable.draw(canvas);
+ if (isStarted()) {
+ invalidateSelf();
+ }
}
@Override
@@ -493,7 +486,6 @@
* 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 @@
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 @@
@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 483ccf7..fc40554 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -4,6 +4,11 @@
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 @@
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 @@
-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 00381ee..7ecc743 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -784,9 +784,7 @@
.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 a808b88..5736c70 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -49,7 +49,8 @@
// 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 @@
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 @@
// 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 @@
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 9bda233..005b399 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 @@
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 @@
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 0643a54..9dfe454 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 @@
// ----------------------------------------------------------------------------
// 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 cf2726b5..43ff33f 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 @@
}
freeAllSnapshots();
- mSnapshot = allocSnapshot(&mFirstSnapshot,
- SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ mSnapshot = allocSnapshot(&mFirstSnapshot, SaveFlags::MatrixClip);
mSnapshot->setRelativeLightCenter(Vector3());
mSaveCount = 1;
}
@@ -72,8 +70,7 @@
}
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 e98fa04..748edef 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 a1825c5..1b0f424 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 @@
* 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 @@
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 @@
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 @@
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 759c12a..384e64d 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -102,7 +102,7 @@
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 @@
}
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, 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 @@
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 @@
&& (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 @@
// 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 72fc100..f1cfa08 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -128,14 +128,14 @@
// ----------------------------------------------------------------------------
// 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 c4c655b..a457212 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 @@
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 @@
}
}
- // 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);
- int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+ 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);
+ }
+
deferNodePropsAndOps(*node);
mCanvasState.restoreToCount(count);
+ layer++;
}
}
@@ -327,7 +378,7 @@
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 @@
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::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 @@
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 @@
} else {
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
}
+*/
}
void FrameBuilder::deferBitmapMeshOp(const BitmapMeshOp& op) {
@@ -597,7 +653,7 @@
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 bd01850..dea9934 100644
--- a/libs/hwui/FrameBuilder.h
+++ b/libs/hwui/FrameBuilder.h
@@ -59,6 +59,11 @@
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 @@
// 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 0f219e4..3123e8e 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -407,7 +407,6 @@
renderState.bindFramebuffer(fbo);
glGenTextures(1, &texture);
- GL_CHECKPOINT();
caches.textureState().activateTexture(0);
caches.textureState().bindTexture(texture);
@@ -422,11 +421,9 @@
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 @@
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 @@
glReadPixels(0, 0, bitmap->width(), bitmap->height(), format,
type, bitmap->getPixels());
- GL_CHECKPOINT();
}
status = true;
@@ -460,6 +454,8 @@
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 db017fe..6c2e244 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 @@
}
if (!suppressErrorChecks()) {
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
#if DEBUG_MEMORY_USAGE
mCaches.dumpMemoryUsage();
@@ -472,7 +472,7 @@
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 @@
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 @@
* 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 @@
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 @@
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 @@
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 78855e5..328e291 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -92,7 +92,7 @@
// 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 @@
}
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 @@
// 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 @@
// 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 @@
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 @@
&& (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 8aa7506..786f96e 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -108,14 +108,14 @@
// ----------------------------------------------------------------------------
// 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 ae690fd..d4588ed 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 @@
(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 @@
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 @@
// 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 @@
// 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 @@
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 @@
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 @@
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 ce1bd6a..b848af4 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 @@
(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 96c1a7c..20e7c71 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 @@
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 @@
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 @@
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 @@
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 @@
}
}
+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 976f775..6530d4ed8 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -159,7 +159,21 @@
}
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 @@
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 c6d8977..27fea1f 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 @@
, 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 5046d37..c09b6dd 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -97,7 +97,7 @@
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 @@
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 3e20608..1d31c9e 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -420,7 +420,7 @@
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
similarity index 100%
rename from libs/hwui/tests/common/nullegl.cpp
rename to libs/hwui/debug/nullegl.cpp
diff --git a/libs/hwui/tests/common/nullgles.cpp b/libs/hwui/debug/nullgles.cpp
similarity index 99%
rename from libs/hwui/tests/common/nullgles.cpp
rename to libs/hwui/debug/nullgles.cpp
index f8e8c98..ffb0649 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 0000000..7716a73
--- /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 0000000..c4f2e35
--- /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 0000000..4a35374
--- /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 75dcf16..81363d9 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -241,7 +241,7 @@
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 @@
roundedOutRadius);
}
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
// --------------------------------
// ---------- Mesh setup ----------
@@ -339,7 +339,7 @@
// 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 @@
// ------------------------------------
blend().setFactors(glop.blend.src, glop.blend.dst);
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
// ------------------------------------
// ---------- Actual drawing ----------
@@ -379,7 +379,7 @@
glDrawArrays(mesh.primitiveMode, 0, mesh.elementCount);
}
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
// -----------------------------------
// ---------- Mesh teardown ----------
@@ -391,7 +391,7 @@
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 cdd2da0..e7cf3ec 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 @@
// 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 @@
#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 @@
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 @@
// ... 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 @@
// ... 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 @@
// ... 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 @@
}
} 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 @@
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 db6402c..a5fd712 100644
--- a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
@@ -32,7 +32,7 @@
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 @@
}
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 c899850..6904bec 100644
--- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
@@ -34,18 +34,18 @@
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 0cba3447..6d27c9d 100644
--- a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
@@ -69,7 +69,7 @@
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 @@
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 2e59eb4..83af148 100644
--- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
+++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
@@ -78,8 +78,8 @@
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 @@
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 @@
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 67c95e2..f9c2b67 100644
--- a/libs/hwui/tests/microbench/FrameBuilderBench.cpp
+++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
@@ -47,7 +47,7 @@
// 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 09bd302..44e6d3a 100644
--- a/libs/hwui/tests/unit/BufferPoolTests.cpp
+++ b/libs/hwui/tests/unit/BufferPoolTests.cpp
@@ -36,6 +36,7 @@
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 4df2687..68d74ee 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 @@
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 @@
}
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 b51bd2f..618df14 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -167,7 +167,7 @@
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 @@
// 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 @@
<< "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 @@
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 @@
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 @@
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 @@
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_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 @@
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 @@
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 @@
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 @@
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 @@
});
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 @@
[](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 @@
[](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 41e44fc..4a635fb 100644
--- a/libs/hwui/tests/unit/LeakCheckTests.cpp
+++ b/libs/hwui/tests/unit/LeakCheckTests.cpp
@@ -32,7 +32,7 @@
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 ff098c8..01bfc5a 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -35,7 +35,7 @@
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, 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 @@
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, 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_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_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_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 @@
// 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_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_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 55104de..33209759 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 @@
}
}
return errorObserved;
+#endif
}
}; // namespace uirenderer
diff --git a/libs/hwui/utils/GLUtils.h b/libs/hwui/utils/GLUtils.h
index 85a10f9..b49c1eb 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/MediaFile.java b/media/java/android/media/MediaFile.java
index 7d76e74..f7becf5 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -91,8 +91,10 @@
public static final int FILE_TYPE_RW2 = 305;
public static final int FILE_TYPE_ORF = 306;
public static final int FILE_TYPE_RAF = 307;
+ public static final int FILE_TYPE_PEF = 308;
+ public static final int FILE_TYPE_SRW = 309;
private static final int FIRST_RAW_IMAGE_FILE_TYPE = FILE_TYPE_DNG;
- private static final int LAST_RAW_IMAGE_FILE_TYPE = FILE_TYPE_RAF;
+ private static final int LAST_RAW_IMAGE_FILE_TYPE = FILE_TYPE_SRW;
// Playlist file types
public static final int FILE_TYPE_M3U = 41;
@@ -228,17 +230,19 @@
addFileType("GIF", FILE_TYPE_GIF, "image/gif", MtpConstants.FORMAT_GIF);
addFileType("PNG", FILE_TYPE_PNG, "image/png", MtpConstants.FORMAT_PNG);
addFileType("BMP", FILE_TYPE_BMP, "image/x-ms-bmp", MtpConstants.FORMAT_BMP);
- addFileType("WBMP", FILE_TYPE_WBMP, "image/vnd.wap.wbmp");
- addFileType("WEBP", FILE_TYPE_WEBP, "image/webp");
+ addFileType("WBMP", FILE_TYPE_WBMP, "image/vnd.wap.wbmp", MtpConstants.FORMAT_DEFINED);
+ addFileType("WEBP", FILE_TYPE_WEBP, "image/webp", MtpConstants.FORMAT_DEFINED);
addFileType("DNG", FILE_TYPE_DNG, "image/x-adobe-dng", MtpConstants.FORMAT_DNG);
- addFileType("CR2", FILE_TYPE_CR2, "image/x-canon-cr2");
- addFileType("NEF", FILE_TYPE_NEF, "image/x-nikon-nef");
- addFileType("NRW", FILE_TYPE_NRW, "image/x-nikon-nrw");
- addFileType("ARW", FILE_TYPE_ARW, "image/x-sony-arw");
- addFileType("RW2", FILE_TYPE_RW2, "image/x-panasonic-rw2");
- addFileType("ORF", FILE_TYPE_ORF, "image/x-olympus-orf");
- addFileType("RAF", FILE_TYPE_RAF, "image/x-fuji-raf");
+ addFileType("CR2", FILE_TYPE_CR2, "image/x-canon-cr2", MtpConstants.FORMAT_TIFF);
+ addFileType("NEF", FILE_TYPE_NEF, "image/x-nikon-nef", MtpConstants.FORMAT_TIFF_EP);
+ addFileType("NRW", FILE_TYPE_NRW, "image/x-nikon-nrw", MtpConstants.FORMAT_TIFF);
+ addFileType("ARW", FILE_TYPE_ARW, "image/x-sony-arw", MtpConstants.FORMAT_TIFF);
+ addFileType("RW2", FILE_TYPE_RW2, "image/x-panasonic-rw2", MtpConstants.FORMAT_TIFF);
+ addFileType("ORF", FILE_TYPE_ORF, "image/x-olympus-orf", MtpConstants.FORMAT_TIFF);
+ addFileType("RAF", FILE_TYPE_RAF, "image/x-fuji-raf", MtpConstants.FORMAT_DEFINED);
+ addFileType("PEF", FILE_TYPE_PEF, "image/x-pentax-pef", MtpConstants.FORMAT_TIFF);
+ addFileType("SRW", FILE_TYPE_SRW, "image/x-samsung-srw", MtpConstants.FORMAT_TIFF);
addFileType("M3U", FILE_TYPE_M3U, "audio/x-mpegurl", MtpConstants.FORMAT_M3U_PLAYLIST);
addFileType("M3U", FILE_TYPE_M3U, "application/x-mpegurl", MtpConstants.FORMAT_M3U_PLAYLIST);
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 504c6d0..9517387 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -389,6 +389,7 @@
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/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 4ca89a8..869512d 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -34,9 +34,9 @@
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ResultReceiver;
-import android.service.media.MediaBrowserService;
import android.service.media.IMediaBrowserService;
import android.service.media.IMediaBrowserServiceCallbacks;
+import android.service.media.MediaBrowserService;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -44,7 +44,9 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Map.Entry;
/**
* Browses media content offered by a link MediaBrowserService.
@@ -52,11 +54,39 @@
* This object is not thread-safe. All calls should happen on the thread on which the browser
* was constructed.
* </p>
+ * <h3>Standard Extra Data</h3>
+ *
+ * <p>These are the current standard fields that can be used as extra data via
+ * {@link #subscribe(String, Bundle, SubscriptionCallback)}, {@link #unsubscribe(String, Bundle)},
+ * and {@link SubscriptionCallback#onChildrenLoaded(String, List, Bundle)}.
+ *
+ * <ul>
+ * <li> {@link #EXTRA_PAGE}
+ * <li> {@link #EXTRA_PAGE_SIZE}
+ * </ul>
*/
public final class MediaBrowser {
private static final String TAG = "MediaBrowser";
private static final boolean DBG = false;
+ /**
+ * Used as an int extra field to denote the page number to subscribe.
+ * The value of {@code EXTRA_PAGE} should be greater than or equal to 1.
+ *
+ * @see android.service.media.MediaBrowserService.BrowserRoot
+ * @see #EXTRA_PAGE_SIZE
+ */
+ public static final String EXTRA_PAGE = "android.media.browse.extra.PAGE";
+
+ /**
+ * Used as an int extra field to denote the number of media items in a page.
+ * The value of {@code EXTRA_PAGE_SIZE} should be greater than or equal to 1.
+ *
+ * @see android.service.media.MediaBrowserService.BrowserRoot
+ * @see #EXTRA_PAGE
+ */
+ public static final String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
+
private static final int CONNECT_STATE_DISCONNECTED = 0;
private static final int CONNECT_STATE_CONNECTING = 1;
private static final int CONNECT_STATE_CONNECTED = 2;
@@ -67,8 +97,7 @@
private final ConnectionCallback mCallback;
private final Bundle mRootHints;
private final Handler mHandler = new Handler();
- private final ArrayMap<String,Subscription> mSubscriptions =
- new ArrayMap<String, MediaBrowser.Subscription>();
+ private final ArrayMap<String, Subscription> mSubscriptions = new ArrayMap<>();
private int mState = CONNECT_STATE_DISCONNECTED;
private MediaServiceConnection mServiceConnection;
@@ -291,7 +320,7 @@
* the specified id and subscribes to receive updates when they change.
* <p>
* The list of subscriptions is maintained even when not connected and is
- * restored after reconnection. It is ok to subscribe while not connected
+ * restored after the reconnection. It is ok to subscribe while not connected
* but the results will not be returned until the connection completes.
* </p>
* <p>
@@ -305,34 +334,37 @@
* @param callback The callback to receive the list of children.
*/
public void subscribe(@NonNull String parentId, @NonNull SubscriptionCallback callback) {
- // Check arguments.
- if (parentId == null) {
- throw new IllegalArgumentException("parentId is null");
- }
- if (callback == null) {
- throw new IllegalArgumentException("callback is null");
- }
+ subscribeInternal(parentId, null, callback);
+ }
- // Update or create the subscription.
- Subscription sub = mSubscriptions.get(parentId);
- boolean newSubscription = sub == null;
- if (newSubscription) {
- sub = new Subscription(parentId);
- mSubscriptions.put(parentId, sub);
+ /**
+ * Queries with service-specific arguments for information about the media items
+ * that are contained within the specified id and subscribes to receive updates
+ * when they change.
+ * <p>
+ * The list of subscriptions is maintained even when not connected and is
+ * restored after the reconnection. It is ok to subscribe while not connected
+ * but the results will not be returned until the connection completes.
+ * </p>
+ * <p>
+ * If the id is already subscribed with a different callback then the new
+ * callback will replace the previous one and the child data will be
+ * reloaded.
+ * </p>
+ *
+ * @param parentId The id of the parent media item whose list of children
+ * will be subscribed.
+ * @param options A bundle of service-specific arguments to send to the media
+ * browse service. The contents of this bundle may affect the
+ * information returned when browsing.
+ * @param callback The callback to receive the list of children.
+ */
+ public void subscribe(@NonNull String parentId, @NonNull Bundle options,
+ @NonNull SubscriptionCallback callback) {
+ if (options == null) {
+ throw new IllegalArgumentException("options are null");
}
- sub.callback = callback;
-
- // If we are connected, tell the service that we are watching. If we aren't
- // connected, the service will be told when we connect.
- if (mState == CONNECT_STATE_CONNECTED) {
- try {
- mServiceBinder.addSubscription(parentId, mServiceCallbacks);
- } catch (RemoteException ex) {
- // Process is crashing. We will disconnect, and upon reconnect we will
- // automatically reregister. So nothing to do here.
- Log.d(TAG, "addSubscription failed with RemoteException parentId=" + parentId);
- }
- }
+ subscribeInternal(parentId, options, callback);
}
/**
@@ -343,27 +375,28 @@
* </p>
*
* @param parentId The id of the parent media item whose list of children
- * will be unsubscribed.
+ * will be unsubscribed.
*/
public void unsubscribe(@NonNull String parentId) {
- // Check arguments.
- if (TextUtils.isEmpty(parentId)) {
- throw new IllegalArgumentException("parentId is empty.");
- }
+ unsubscribeInternal(parentId, null);
+ }
- // Remove from our list.
- final Subscription sub = mSubscriptions.remove(parentId);
-
- // Tell the service if necessary.
- if (mState == CONNECT_STATE_CONNECTED && sub != null) {
- try {
- mServiceBinder.removeSubscription(parentId, mServiceCallbacks);
- } catch (RemoteException ex) {
- // Process is crashing. We will disconnect, and upon reconnect we will
- // automatically reregister. So nothing to do here.
- Log.d(TAG, "removeSubscription failed with RemoteException parentId=" + parentId);
- }
+ /**
+ * Unsubscribes for changes to the children of the specified media id.
+ * <p>
+ * The query callback will no longer be invoked for results associated with
+ * this id once this method returns.
+ * </p>
+ *
+ * @param parentId The id of the parent media item whose list of children
+ * will be unsubscribed.
+ * @param options A bundle sent to the media browse service to subscribe.
+ */
+ public void unsubscribe(@NonNull String parentId, @NonNull Bundle options) {
+ if (options == null) {
+ throw new IllegalArgumentException("options are null");
}
+ unsubscribeInternal(parentId, options);
}
/**
@@ -420,6 +453,73 @@
}
}
+ private void subscribeInternal(String parentId, Bundle options, SubscriptionCallback callback) {
+ // Check arguments.
+ if (parentId == null) {
+ throw new IllegalArgumentException("parentId is null");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("callback is null");
+ }
+ // Update or create the subscription.
+ Subscription sub = mSubscriptions.get(parentId);
+ if (sub == null) {
+ sub = new Subscription();
+ mSubscriptions.put(parentId, sub);
+ }
+ sub.add(callback, options);
+
+ // If we are connected, tell the service that we are watching. If we aren't connected,
+ // the service will be told when we connect.
+ if (mState == CONNECT_STATE_CONNECTED) {
+ try {
+ // NOTE: In order not to break the behavior of the support library, call
+ // addSubscription instead of addSubscriptionWithOptions when the options are null.
+ if (options == null) {
+ mServiceBinder.addSubscription(parentId, mServiceCallbacks);
+ } else {
+ mServiceBinder.addSubscriptionWithOptions(parentId, options, mServiceCallbacks);
+ }
+ } catch (RemoteException ex) {
+ // Process is crashing. We will disconnect, and upon reconnect we will
+ // automatically reregister. So nothing to do here.
+ Log.d(TAG, "addSubscription failed with RemoteException parentId=" + parentId);
+ }
+ }
+ }
+
+ private void unsubscribeInternal(String parentId, Bundle options) {
+ // Check arguments.
+ if (TextUtils.isEmpty(parentId)) {
+ throw new IllegalArgumentException("parentId is empty.");
+ }
+
+ // Remove from our list.
+ Subscription sub = mSubscriptions.get(parentId);
+
+ // Tell the service if necessary.
+ if (sub != null && sub.remove(options) && mState == CONNECT_STATE_CONNECTED) {
+ try {
+ // NOTE: In order not to break the behavior of the support library, call
+ // removeSubscription instead of removeSubscriptionWithOptions when the options
+ // are null.
+ if (options == null) {
+ mServiceBinder.removeSubscription(parentId, mServiceCallbacks);
+ } else {
+ mServiceBinder.removeSubscriptionWithOptions(
+ parentId, options, mServiceCallbacks);
+ }
+ } catch (RemoteException ex) {
+ // Process is crashing. We will disconnect, and upon reconnect we will
+ // automatically reregister. So nothing to do here.
+ Log.d(TAG, "removeSubscription failed with RemoteException parentId=" + parentId);
+ }
+ }
+ if (sub != null && sub.isEmpty()) {
+ mSubscriptions.remove(parentId);
+ }
+ }
+
/**
* For debugging.
*/
@@ -467,13 +567,26 @@
// we may receive some subscriptions before we are connected, so re-subscribe
// everything now
- for (String id : mSubscriptions.keySet()) {
- try {
- mServiceBinder.addSubscription(id, mServiceCallbacks);
- } catch (RemoteException ex) {
- // Process is crashing. We will disconnect, and upon reconnect we will
- // automatically reregister. So nothing to do here.
- Log.d(TAG, "addSubscription failed with RemoteException parentId=" + id);
+ for (Entry<String, Subscription> subscriptionEntry : mSubscriptions.entrySet()) {
+ String id = subscriptionEntry.getKey();
+ Subscription sub = subscriptionEntry.getValue();
+ for (Bundle options : sub.getOptionsList()) {
+ try {
+ // NOTE: In order not to break the behavior of the support library,
+ // call addSubscription instead of addSubscriptionWithOptions when
+ // the options are null.
+ if (options == null) {
+ mServiceBinder.addSubscription(id, mServiceCallbacks);
+ } else {
+ mServiceBinder.addSubscriptionWithOptions(
+ id, options, mServiceCallbacks);
+ }
+ } catch (RemoteException ex) {
+ // Process is crashing. We will disconnect, and upon reconnect we will
+ // automatically reregister. So nothing to do here.
+ Log.d(TAG, "addSubscription failed with RemoteException parentId="
+ + id);
+ }
}
}
}
@@ -508,7 +621,7 @@
}
private final void onLoadChildren(final IMediaBrowserServiceCallbacks callback,
- final String parentId, final ParceledListSlice list) {
+ final String parentId, final ParceledListSlice list, final Bundle options) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -525,16 +638,21 @@
// Check that the subscription is still subscribed.
final Subscription subscription = mSubscriptions.get(parentId);
- if (subscription == null) {
- if (DBG) {
- Log.d(TAG, "onLoadChildren for id that isn't subscribed id="
- + parentId);
+ if (subscription != null) {
+ // Tell the app.
+ SubscriptionCallback subscriptionCallback = subscription.getCallback(options);
+ if (subscriptionCallback != null) {
+ if (options == null) {
+ subscriptionCallback.onChildrenLoaded(parentId, data);
+ } else {
+ subscriptionCallback.onChildrenLoaded(parentId, data, options);
+ }
+ return;
}
- return;
}
-
- // Tell the app.
- subscription.callback.onChildrenLoaded(parentId, data);
+ if (DBG) {
+ Log.d(TAG, "onLoadChildren for id that isn't subscribed id=" + parentId);
+ }
}
});
}
@@ -697,7 +815,6 @@
}
}
-
/**
* Callbacks for connection related events.
*/
@@ -735,6 +852,19 @@
}
/**
+ * Called when the list of children is loaded or updated.
+ *
+ * @param parentId The media id of the parent media item.
+ * @param children The children which were loaded, or null if the id is invalid.
+ * @param options A bundle of service-specific arguments to send to the media
+ * browse service. The contents of this bundle may affect the
+ * information returned when browsing.
+ */
+ public void onChildrenLoaded(@NonNull String parentId, List<MediaItem> children,
+ @NonNull Bundle options) {
+ }
+
+ /**
* Called when the id doesn't exist or other errors in subscribing.
* <p>
* If this is called, the subscription remains until {@link MediaBrowser#unsubscribe}
@@ -742,10 +872,25 @@
* </p>
*
* @param parentId The media id of the parent media item whose children could
- * not be loaded.
+ * not be loaded.
*/
public void onError(@NonNull String parentId) {
}
+
+ /**
+ * Called when the id doesn't exist or other errors in subscribing.
+ * <p>
+ * If this is called, the subscription remains until {@link MediaBrowser#unsubscribe}
+ * called, because some errors may heal themselves.
+ * </p>
+ *
+ * @param parentId The media id of the parent media item whose children could
+ * not be loaded.
+ * @param options A bundle of service-specific arguments sent to the media
+ * browse service.
+ */
+ public void onError(@NonNull String parentId, @NonNull Bundle options) {
+ }
}
/**
@@ -909,20 +1054,65 @@
}
@Override
- public void onLoadChildren(final String parentId, final ParceledListSlice list) {
+ public void onLoadChildren(final String parentId, final ParceledListSlice list,
+ final Bundle options) {
MediaBrowser mediaBrowser = mMediaBrowser.get();
if (mediaBrowser != null) {
- mediaBrowser.onLoadChildren(this, parentId, list);
+ mediaBrowser.onLoadChildren(this, parentId, list, options);
}
}
}
private static class Subscription {
- final String id;
- SubscriptionCallback callback;
+ private final List<SubscriptionCallback> mCallbacks;
+ private final List<Bundle> mOptionsList;
- Subscription(String id) {
- this.id = id;
+ public Subscription() {
+ mCallbacks = new ArrayList<>();
+ mOptionsList = new ArrayList<>();
+ }
+
+ public boolean isEmpty() {
+ return mCallbacks.isEmpty();
+ }
+
+ public List<Bundle> getOptionsList() {
+ return mOptionsList;
+ }
+
+ public List<SubscriptionCallback> getCallbacks() {
+ return mCallbacks;
+ }
+
+ public void add(SubscriptionCallback callback, Bundle options) {
+ for (int i = 0; i < mOptionsList.size(); ++i) {
+ if (MediaBrowserUtils.areSameOptions(mOptionsList.get(i), options)) {
+ mCallbacks.set(i, callback);
+ return;
+ }
+ }
+ mCallbacks.add(callback);
+ mOptionsList.add(options);
+ }
+
+ public boolean remove(Bundle options) {
+ for (int i = 0; i < mOptionsList.size(); ++i) {
+ if (MediaBrowserUtils.areSameOptions(mOptionsList.get(i), options)) {
+ mCallbacks.remove(i);
+ mOptionsList.remove(i);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public SubscriptionCallback getCallback(Bundle options) {
+ for (int i = 0; i < mOptionsList.size(); ++i) {
+ if (MediaBrowserUtils.areSameOptions(mOptionsList.get(i), options)) {
+ return mCallbacks.get(i);
+ }
+ }
+ return null;
}
}
}
diff --git a/media/java/android/media/browse/MediaBrowserUtils.java b/media/java/android/media/browse/MediaBrowserUtils.java
new file mode 100644
index 0000000..4f198ac
--- /dev/null
+++ b/media/java/android/media/browse/MediaBrowserUtils.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.browse;
+
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+public class MediaBrowserUtils {
+ public static boolean areSameOptions(Bundle options1, Bundle options2) {
+ if (options1 == options2) {
+ return true;
+ } else if (options1 == null) {
+ return options2.getInt(MediaBrowser.EXTRA_PAGE, -1) == -1
+ && options2.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1) == -1;
+ } else if (options2 == null) {
+ return options1.getInt(MediaBrowser.EXTRA_PAGE, -1) == -1
+ && options1.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1) == -1;
+ } else {
+ return options1.getInt(MediaBrowser.EXTRA_PAGE, -1)
+ == options2.getInt(MediaBrowser.EXTRA_PAGE, -1)
+ && options1.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1)
+ == options2.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1);
+ }
+ }
+
+ public static boolean hasDuplicatedItems(Bundle options1, Bundle options2) {
+ int page1 = options1.getInt(MediaBrowser.EXTRA_PAGE, -1);
+ int page2 = options2.getInt(MediaBrowser.EXTRA_PAGE, -1);
+ int pageSize1 = options1.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1);
+ int pageSize2 = options2.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1);
+
+ int startIndex1, startIndex2, endIndex1, endIndex2;
+ if (page1 == -1 || pageSize1 == -1) {
+ startIndex1 = 0;
+ endIndex1 = Integer.MAX_VALUE;
+ } else {
+ startIndex1 = pageSize1 * (page1 - 1);
+ endIndex1 = startIndex1 + pageSize1 - 1;
+ }
+
+ if (page2 == -1 || pageSize2 == -1) {
+ startIndex2 = 0;
+ endIndex2 = Integer.MAX_VALUE;
+ } else {
+ startIndex2 = pageSize2 * (page2 - 1);
+ endIndex2 = startIndex2 + pageSize2 - 1;
+ }
+
+ if (startIndex1 <= startIndex2 && startIndex2 <= endIndex1) {
+ return true;
+ } else if (startIndex1 <= endIndex2 && endIndex2 <= endIndex1) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 3960230..671a86f 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -120,6 +120,8 @@
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 @@
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 @@
}
/**
- * 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 @@
* {@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 @@
* @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 @@
* {@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 @@
* @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();
}
- 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 {
+ 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 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 @@
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 @@
"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 @@
}
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 @@
parser.close();
}
}
+ return input;
}
/**
@@ -330,15 +355,23 @@
* @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 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 @@
* 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 @@
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 @@
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/mtp/MtpConstants.java b/media/java/android/mtp/MtpConstants.java
index bdd8643..ef2cf2b 100644
--- a/media/java/android/mtp/MtpConstants.java
+++ b/media/java/android/mtp/MtpConstants.java
@@ -182,6 +182,13 @@
public static final int FORMAT_MPEG = 0x300B;
/** Format code for ASF files */
public static final int FORMAT_ASF = 0x300C;
+ /**
+ * Format code for unknown image files.
+ * <p>
+ * Will be used for the formats which are not specified in PTP specification.
+ * For instance, WEBP and WBMP.
+ */
+ public static final int FORMAT_DEFINED = 0x3800;
/** Format code for JPEG image files */
public static final int FORMAT_EXIF_JPEG = 0x3801;
/** Format code for TIFF EP image files */
@@ -272,12 +279,12 @@
public static final int FORMAT_MS_POWERPOINT_PRESENTATION = 0xBA86;
/**
- * Returns true if the object is abstract (that is, it has no representation
- * in the underlying file system).
- *
- * @param format the format of the object
- * @return true if the object is abstract
- */
+ * Returns true if the object is abstract (that is, it has no representation
+ * in the underlying file system).
+ *
+ * @param format the format of the object
+ * @return true if the object is abstract
+ */
public static boolean isAbstractObject(int format) {
switch (format) {
case FORMAT_ABSTRACT_MULTIMEDIA_ALBUM:
diff --git a/media/java/android/service/media/IMediaBrowserService.aidl b/media/java/android/service/media/IMediaBrowserService.aidl
index f01fc07..fe7ebfa 100644
--- a/media/java/android/service/media/IMediaBrowserService.aidl
+++ b/media/java/android/service/media/IMediaBrowserService.aidl
@@ -14,10 +14,19 @@
* @hide
*/
oneway interface IMediaBrowserService {
+
+ // Warning: DO NOT CHANGE the methods signature and order of methods.
+ // The change of the order or the method signatures could break the support library.
+
void connect(String pkg, in Bundle rootHints, IMediaBrowserServiceCallbacks callbacks);
void disconnect(IMediaBrowserServiceCallbacks callbacks);
void addSubscription(String uri, IMediaBrowserServiceCallbacks callbacks);
void removeSubscription(String uri, IMediaBrowserServiceCallbacks callbacks);
void getMediaItem(String uri, in ResultReceiver cb);
-}
\ No newline at end of file
+
+ void addSubscriptionWithOptions(String uri, in Bundle options,
+ IMediaBrowserServiceCallbacks callbacks);
+ void removeSubscriptionWithOptions(String uri, in Bundle options,
+ IMediaBrowserServiceCallbacks callbacks);
+}
diff --git a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
index 2a37ada..dadb025 100644
--- a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
+++ b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
@@ -22,5 +22,5 @@
*/
void onConnect(String root, in MediaSession.Token session, in Bundle extras);
void onConnectFailed();
- void onLoadChildren(String mediaId, in ParceledListSlice list);
+ void onLoadChildren(String mediaId, in ParceledListSlice list, in Bundle options);
}
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index 8edccac..6cf90d5 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -25,11 +25,12 @@
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.media.browse.MediaBrowser;
+import android.media.browse.MediaBrowserUtils;
import android.media.session.MediaSession;
import android.os.Binder;
import android.os.Bundle;
-import android.os.IBinder;
import android.os.Handler;
+import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.service.media.IMediaBrowserService;
@@ -40,7 +41,8 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.HashSet;
+import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
/**
@@ -82,7 +84,9 @@
*/
public static final String KEY_MEDIA_ITEM = "media_item";
- private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap();
+ private static final int RESULT_FLAG_OPTION_NOT_HANDLED = 0x00000001;
+
+ private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>();
private final Handler mHandler = new Handler();
private ServiceBinder mBinder;
MediaSession.Token mSession;
@@ -95,7 +99,7 @@
Bundle rootHints;
IMediaBrowserServiceCallbacks callbacks;
BrowserRoot root;
- HashSet<String> subscriptions = new HashSet();
+ HashMap<String, List<Bundle>> subscriptions = new HashMap<>();
}
/**
@@ -115,6 +119,7 @@
private Object mDebug;
private boolean mDetachCalled;
private boolean mSendResultCalled;
+ private int mFlag;
Result(Object debug) {
mDebug = debug;
@@ -128,7 +133,7 @@
throw new IllegalStateException("sendResult() called twice for: " + mDebug);
}
mSendResultCalled = true;
- onResultSent(result);
+ onResultSent(result, mFlag);
}
/**
@@ -151,11 +156,15 @@
return mDetachCalled || mSendResultCalled;
}
+ void setFlag(int flag) {
+ mFlag = flag;
+ }
+
/**
* Called when the result is sent, after assertions about not being called twice
* have happened.
*/
- void onResultSent(T result) {
+ void onResultSent(T result, int flag) {
}
}
@@ -228,9 +237,15 @@
});
}
+ @Override
+ public void addSubscription(final String id,
+ final IMediaBrowserServiceCallbacks callbacks) {
+ addSubscriptionWithOptions(id, null, callbacks);
+ }
@Override
- public void addSubscription(final String id, final IMediaBrowserServiceCallbacks callbacks) {
+ public void addSubscriptionWithOptions(final String id, final Bundle options,
+ final IMediaBrowserServiceCallbacks callbacks) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -244,7 +259,7 @@
return;
}
- MediaBrowserService.this.addSubscription(id, connection);
+ MediaBrowserService.this.addSubscription(id, connection, options);
}
});
}
@@ -252,6 +267,12 @@
@Override
public void removeSubscription(final String id,
final IMediaBrowserServiceCallbacks callbacks) {
+ removeSubscriptionWithOptions(id, null, callbacks);
+ }
+
+ @Override
+ public void removeSubscriptionWithOptions(final String id, final Bundle options,
+ final IMediaBrowserServiceCallbacks callbacks) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -263,7 +284,7 @@
+ id);
return;
}
- if (!connection.subscriptions.remove(id)) {
+ if (!MediaBrowserService.this.removeSubscription(id, connection, options)) {
Log.w(TAG, "removeSubscription called for " + id
+ " which is not subscribed");
}
@@ -345,6 +366,33 @@
@NonNull Result<List<MediaBrowser.MediaItem>> result);
/**
+ * Called to get information about the children of a media item.
+ * <p>
+ * Implementations must call {@link Result#sendResult result.sendResult}
+ * with the list of children. If loading the children will be an expensive
+ * operation that should be performed on another thread,
+ * {@link Result#detach result.detach} may be called before returning from
+ * this function, and then {@link Result#sendResult result.sendResult}
+ * called when the loading is complete.
+ *
+ * @param parentId The id of the parent media item whose children are to be
+ * queried.
+ * @param result The Result to send the list of children to, or null if the
+ * id is invalid.
+ * @param options A bundle of service-specific arguments sent from the media
+ * browse. The information returned through the result should be
+ * affected by the contents of this bundle.
+ */
+ public void onLoadChildren(@NonNull String parentId,
+ @NonNull Result<List<MediaBrowser.MediaItem>> result, @NonNull Bundle options) {
+ // To support backward compatibility, when the implementation of MediaBrowserService doesn't
+ // override onLoadChildren() with options, onLoadChildren() without options will be used
+ // instead, and the options will be applied in the implementation of result.onResultSent().
+ result.setFlag(RESULT_FLAG_OPTION_NOT_HANDLED);
+ onLoadChildren(parentId, result);
+ }
+
+ /**
* Called to get information about a specific media item.
* <p>
* Implementations must call {@link Result#sendResult result.sendResult}. If
@@ -413,7 +461,29 @@
* @param parentId The id of the parent media item whose
* children changed.
*/
- public void notifyChildrenChanged(@NonNull final String parentId) {
+ public void notifyChildrenChanged(@NonNull String parentId) {
+ notifyChildrenChangedInternal(parentId, null);
+ }
+
+ /**
+ * Notifies all connected media browsers that the children of
+ * the specified parent id have changed in some way.
+ * This will cause browsers to fetch subscribed content again.
+ *
+ * @param parentId The id of the parent media item whose
+ * children changed.
+ * @param options A bundle of service-specific arguments to send
+ * to the media browse. The contents of this bundle may
+ * contain the information about the change.
+ */
+ public void notifyChildrenChanged(@NonNull String parentId, @NonNull Bundle options) {
+ if (options == null) {
+ throw new IllegalArgumentException("options cannot be null in notifyChildrenChanged");
+ }
+ notifyChildrenChangedInternal(parentId, options);
+ }
+
+ private void notifyChildrenChangedInternal(final String parentId, final Bundle options) {
if (parentId == null) {
throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged");
}
@@ -422,8 +492,13 @@
public void run() {
for (IBinder binder : mConnections.keySet()) {
ConnectionRecord connection = mConnections.get(binder);
- if (connection.subscriptions.contains(parentId)) {
- performLoadChildren(parentId, connection);
+ List<Bundle> optionsList = connection.subscriptions.get(parentId);
+ if (optionsList != null) {
+ for (Bundle bundle : optionsList) {
+ if (MediaBrowserUtils.hasDuplicatedItems(options, bundle)) {
+ performLoadChildren(parentId, connection, bundle);
+ }
+ }
}
}
}
@@ -451,12 +526,42 @@
/**
* Save the subscription and if it is a new subscription send the results.
*/
- private void addSubscription(String id, ConnectionRecord connection) {
+ private void addSubscription(String id, ConnectionRecord connection, Bundle options) {
// Save the subscription
- connection.subscriptions.add(id);
-
+ List<Bundle> optionsList = connection.subscriptions.get(id);
+ if (optionsList == null) {
+ optionsList = new ArrayList<>();
+ }
+ for (Bundle bundle : optionsList) {
+ if (MediaBrowserUtils.areSameOptions(options, bundle)) {
+ return;
+ }
+ }
+ optionsList.add(options);
+ connection.subscriptions.put(id, optionsList);
// send the results
- performLoadChildren(id, connection);
+ performLoadChildren(id, connection, options);
+ }
+
+ /**
+ * Remove the subscription.
+ */
+ private boolean removeSubscription(String id, ConnectionRecord connection, Bundle options) {
+ boolean removed = false;
+ List<Bundle> optionsList = connection.subscriptions.get(id);
+ if (optionsList != null) {
+ for (Bundle bundle : optionsList) {
+ if (MediaBrowserUtils.areSameOptions(options, bundle)) {
+ removed = true;
+ optionsList.remove(bundle);
+ break;
+ }
+ }
+ if (optionsList.size() == 0) {
+ connection.subscriptions.remove(id);
+ }
+ }
+ return removed;
}
/**
@@ -464,11 +569,12 @@
* <p>
* Callers must make sure that this connection is still connected.
*/
- private void performLoadChildren(final String parentId, final ConnectionRecord connection) {
+ private void performLoadChildren(final String parentId, final ConnectionRecord connection,
+ final Bundle options) {
final Result<List<MediaBrowser.MediaItem>> result
= new Result<List<MediaBrowser.MediaItem>>(parentId) {
@Override
- void onResultSent(List<MediaBrowser.MediaItem> list) {
+ void onResultSent(List<MediaBrowser.MediaItem> list, int flag) {
if (mConnections.get(connection.callbacks.asBinder()) != connection) {
if (DBG) {
Log.d(TAG, "Not sending onLoadChildren result for connection that has"
@@ -477,10 +583,13 @@
return;
}
+ List<MediaBrowser.MediaItem> filteredList =
+ (flag & RESULT_FLAG_OPTION_NOT_HANDLED) != 0
+ ? applyOptions(list, options) : list;
final ParceledListSlice<MediaBrowser.MediaItem> pls =
- list == null ? null : new ParceledListSlice(list);
+ filteredList == null ? null : new ParceledListSlice<>(filteredList);
try {
- connection.callbacks.onLoadChildren(parentId, pls);
+ connection.callbacks.onLoadChildren(parentId, pls, options);
} catch (RemoteException ex) {
// The other side is in the process of crashing.
Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId
@@ -489,7 +598,11 @@
}
};
- onLoadChildren(parentId, result);
+ if (options == null) {
+ onLoadChildren(parentId, result);
+ } else {
+ onLoadChildren(parentId, result, options);
+ }
if (!result.isDone()) {
throw new IllegalStateException("onLoadChildren must call detach() or sendResult()"
@@ -497,11 +610,29 @@
}
}
+ private List<MediaBrowser.MediaItem> applyOptions(List<MediaBrowser.MediaItem> list,
+ final Bundle options) {
+ int page = options.getInt(MediaBrowser.EXTRA_PAGE, -1);
+ int pageSize = options.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1);
+ if (page == -1 && pageSize == -1) {
+ return list;
+ }
+ int fromIndex = pageSize * (page - 1);
+ int toIndex = fromIndex + pageSize;
+ if (page < 1 || pageSize < 1 || fromIndex >= list.size()) {
+ return null;
+ }
+ if (toIndex > list.size()) {
+ toIndex = list.size();
+ }
+ return list.subList(fromIndex, toIndex);
+ }
+
private void performLoadItem(String itemId, final ResultReceiver receiver) {
final Result<MediaBrowser.MediaItem> result =
new Result<MediaBrowser.MediaItem>(itemId) {
@Override
- void onResultSent(MediaBrowser.MediaItem item) {
+ void onResultSent(MediaBrowser.MediaItem item, int flag) {
Bundle bundle = new Bundle();
bundle.putParcelable(KEY_MEDIA_ITEM, item);
receiver.send(0, bundle);
diff --git a/media/jni/android_media_ExifInterface.cpp b/media/jni/android_media_ExifInterface.cpp
index 463c17e..42deab4 100644
--- a/media/jni/android_media_ExifInterface.cpp
+++ b/media/jni/android_media_ExifInterface.cpp
@@ -16,92 +16,25 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "ExifInterface_JNI"
-#include <utils/Log.h>
-#include <utils/String8.h>
-#include <utils/KeyedVector.h>
-#include <android_runtime/AndroidRuntime.h>
-
-#include <jni.h>
-#include <JNIHelp.h>
-
-#include <nativehelper/ScopedLocalRef.h>
+#include "android_media_Utils.h"
#include "src/piex_types.h"
#include "src/piex.h"
+#include <jni.h>
+#include <JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <nativehelper/ScopedLocalRef.h>
+
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/KeyedVector.h>
+
// ----------------------------------------------------------------------------
using namespace android;
-class FileStream : public piex::StreamInterface {
-private:
- FILE *mFile;
- size_t mPosition;
- size_t mSize;
-
-public:
- FileStream(const String8 filename)
- : mPosition(0),
- mSize(0) {
- mFile = fopen(filename.string(), "r");
- if (mFile == NULL) {
- return;
- }
- // Get the size.
- fseek(mFile, 0l, SEEK_END);
- mSize = ftell(mFile);
- fseek(mFile, 0l, SEEK_SET);
- }
-
- ~FileStream() {
- if (mFile != NULL) {
- fclose(mFile);
- mFile = NULL;
- }
- }
-
- // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
- // provided by the caller, guaranteed to be at least "length" bytes long.
- // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
- // 'offset' bytes from the start of the stream.
- // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
- // change the contents of 'data'.
- piex::Error GetData(
- const size_t offset, const size_t length, std::uint8_t* data)
- override {
- if (mFile == NULL) {
- return piex::Error::kFail;
- }
-
- // Seek first.
- if (mPosition != offset) {
- fseek(mFile, offset, SEEK_SET);
- }
-
- // Read bytes.
- size_t size = fread((void*)data, sizeof(std::uint8_t), length, mFile);
- mPosition += size;
-
- // Handle errors.
- if (ferror(mFile)) {
- return piex::Error::kFail;
- }
- if (size == 0 && feof(mFile)) {
- return piex::Error::kFail;
- }
- return piex::Error::kOk;
- }
-
- bool exists() {
- return mFile != NULL;
- }
-
- size_t size() {
- return mSize;
- }
-};
-
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
LOG_FATAL_IF(! var, "Unable to find class " className);
@@ -159,31 +92,10 @@
env->ReleaseStringUTFChars(jfilename, filenameChars);
piex::PreviewImageData image_data;
- memset(&image_data, 0, sizeof(image_data));
std::unique_ptr<FileStream> stream(new FileStream(filename));
- if (!stream.get()->exists()) {
- // File is not exists.
- ALOGI("File is not exists: %s", filename.string());
- return NULL;
- }
-
- if (!piex::IsRaw(stream.get())) {
- // Format not supported.
- ALOGI("Format not supported: %s", filename.string());
- return NULL;
- }
-
- piex::Error err = piex::GetPreviewImageData(stream.get(), &image_data);
- if (err != piex::Error::kOk) {
- // The input data seems to be broken.
- ALOGI("Raw image not detected: %s (error code: %d)", filename.string(), (int32_t)err);
- return NULL;
- }
-
- if (image_data.thumbnail_offset + image_data.thumbnail_length > stream.get()->size()) {
- // Corrupted file.
- ALOGI("Corrupted file: %s", filename.string());
+ if (!GetExifFromRawImage(stream.get(), filename, image_data)) {
+ ALOGI("Raw image not detected: %s", filename.string());
return NULL;
}
diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index e101709..a5d0303 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -28,6 +28,91 @@
namespace android {
+FileStream::FileStream(const String8 filename)
+ : mPosition(0),
+ mSize(0) {
+ mFile = fopen(filename.string(), "r");
+ if (mFile == NULL) {
+ return;
+ }
+ // Get the size.
+ fseek(mFile, 0l, SEEK_END);
+ mSize = ftell(mFile);
+ fseek(mFile, 0l, SEEK_SET);
+}
+
+FileStream::~FileStream() {
+ if (mFile != NULL) {
+ fclose(mFile);
+ mFile = NULL;
+ }
+}
+
+piex::Error FileStream::GetData(
+ const size_t offset, const size_t length, std::uint8_t* data) {
+ if (mFile == NULL) {
+ return piex::Error::kFail;
+ }
+
+ // Seek first.
+ if (mPosition != offset) {
+ fseek(mFile, offset, SEEK_SET);
+ }
+
+ // Read bytes.
+ size_t size = fread((void*)data, sizeof(std::uint8_t), length, mFile);
+ mPosition += size;
+
+ // Handle errors.
+ if (ferror(mFile) || (size == 0 && feof(mFile))) {
+ ALOGV("GetData read failed: (offset: %zu, length: %zu)", offset, length);
+ return piex::Error::kFail;
+ }
+ return piex::Error::kOk;
+}
+
+bool FileStream::exists() const {
+ return mFile != NULL;
+}
+
+size_t FileStream::size() const {
+ return mSize;
+}
+
+bool GetExifFromRawImage(
+ FileStream::FileStream* stream, const String8& filename,
+ piex::PreviewImageData& image_data) {
+ memset(&image_data, 0, sizeof(image_data));
+
+ if (!stream->exists()) {
+ // File is not exists.
+ ALOGV("File is not exists: %s", filename.string());
+ return false;
+ }
+
+ if (!piex::IsRaw(stream)) {
+ // Format not supported.
+ ALOGV("Format not supported: %s", filename.string());
+ return false;
+ }
+
+ piex::Error err = piex::GetPreviewImageData(stream, &image_data);
+
+ if (err != piex::Error::kOk) {
+ // The input data seems to be broken.
+ ALOGV("Raw image not detected: %s (piex error code: %d)", filename.string(), (int32_t)err);
+ return false;
+ }
+
+ if (image_data.thumbnail_offset + image_data.thumbnail_length > stream->size()) {
+ // Corrupted image.
+ ALOGV("Corrupted file: %s", filename.string());
+ return false;
+ }
+
+ return true;
+}
+
bool ConvertKeyValueArraysToKeyedVector(
JNIEnv *env, jobjectArray keys, jobjectArray values,
KeyedVector<String8, String8>* keyedVector) {
diff --git a/media/jni/android_media_Utils.h b/media/jni/android_media_Utils.h
index 635bceb..762c904 100644
--- a/media/jni/android_media_Utils.h
+++ b/media/jni/android_media_Utils.h
@@ -17,21 +17,48 @@
#ifndef _ANDROID_MEDIA_UTILS_H_
#define _ANDROID_MEDIA_UTILS_H_
-#include "jni.h"
-#include "JNIHelp.h"
-#include "android_runtime/AndroidRuntime.h"
+#include "src/piex_types.h"
+#include "src/piex.h"
+#include <android_runtime/AndroidRuntime.h>
+#include <jni.h>
+#include <JNIHelp.h>
#include <utils/KeyedVector.h>
#include <utils/String8.h>
namespace android {
-/**
- * Returns true if the conversion is successful; otherwise, false.
- */
+class FileStream : public piex::StreamInterface {
+private:
+ FILE *mFile;
+ size_t mPosition;
+ size_t mSize;
+
+public:
+ FileStream(const String8 filename);
+ ~FileStream();
+
+ // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
+ // provided by the caller, guaranteed to be at least "length" bytes long.
+ // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
+ // 'offset' bytes from the start of the stream.
+ // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
+ // change the contents of 'data'.
+ piex::Error GetData(
+ const size_t offset, const size_t length, std::uint8_t* data) override;
+ bool exists() const;
+ size_t size() const;
+};
+
+// Reads EXIF metadata from a given raw image via piex.
+// And returns true if the operation is successful; otherwise, false.
+bool GetExifFromRawImage(
+ FileStream* stream, const String8& filename, piex::PreviewImageData& image_data);
+
+// Returns true if the conversion is successful; otherwise, false.
bool ConvertKeyValueArraysToKeyedVector(
- JNIEnv *env, jobjectArray keys, jobjectArray values,
- KeyedVector<String8, String8>* vector);
+ JNIEnv *env, jobjectArray keys, jobjectArray values,
+ KeyedVector<String8, String8>* vector);
struct AMessage;
status_t ConvertMessageToMap(
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index ec2f98a..556f2c7 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -17,25 +17,17 @@
#define LOG_TAG "MtpDatabaseJNI"
#include "utils/Log.h"
-#include <assert.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdio.h>
-#include <unistd.h>
-
-#include "jni.h"
-#include "JNIHelp.h"
-#include "android_runtime/AndroidRuntime.h"
-#include "android_runtime/Log.h"
-
+#include "android_media_Utils.h"
+#include "mtp.h"
#include "MtpDatabase.h"
#include "MtpDataPacket.h"
#include "MtpObjectInfo.h"
#include "MtpProperty.h"
#include "MtpStringBuffer.h"
#include "MtpUtils.h"
-#include "mtp.h"
+
+#include "src/piex_types.h"
+#include "src/piex.h"
extern "C" {
#include "libexif/exif-content.h"
@@ -44,6 +36,19 @@
#include "libexif/exif-utils.h"
}
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
+#include <jni.h>
+#include <JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
+
+#include <assert.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <unistd.h>
+
using namespace android;
// ----------------------------------------------------------------------------
@@ -823,24 +828,48 @@
env->ReleaseCharArrayElements(mStringBuffer, str, 0);
// read EXIF data for thumbnail information
- if (info.mFormat == MTP_FORMAT_EXIF_JPEG || info.mFormat == MTP_FORMAT_JFIF) {
+ switch (info.mFormat) {
+ case MTP_FORMAT_EXIF_JPEG:
+ case MTP_FORMAT_JFIF: {
+ ExifData *exifdata = exif_data_new_from_file(path);
+ if (exifdata) {
+ if ((false)) {
+ exif_data_foreach_content(exifdata, foreachcontent, NULL);
+ }
- ExifData *exifdata = exif_data_new_from_file(path);
- if (exifdata) {
- if ((false)) {
- exif_data_foreach_content(exifdata, foreachcontent, NULL);
+ ExifEntry *w = exif_content_get_entry(
+ exifdata->ifd[EXIF_IFD_EXIF], EXIF_TAG_PIXEL_X_DIMENSION);
+ ExifEntry *h = exif_content_get_entry(
+ exifdata->ifd[EXIF_IFD_EXIF], EXIF_TAG_PIXEL_Y_DIMENSION);
+ info.mThumbCompressedSize = exifdata->data ? exifdata->size : 0;
+ info.mThumbFormat = MTP_FORMAT_EXIF_JPEG;
+ info.mImagePixWidth = w ? getLongFromExifEntry(w) : 0;
+ info.mImagePixHeight = h ? getLongFromExifEntry(h) : 0;
+ exif_data_unref(exifdata);
+ }
+ break;
+ }
+
+ // Except DNG, all supported RAW image formats are not defined in PTP 1.2 specification.
+ // Most of RAW image formats are based on TIFF or TIFF/EP. To render Fuji's RAF format,
+ // it checks MTP_FORMAT_DEFINED case since it's designed as a custom format.
+ case MTP_FORMAT_DNG:
+ case MTP_FORMAT_TIFF:
+ case MTP_FORMAT_TIFF_EP:
+ case MTP_FORMAT_DEFINED: {
+ std::unique_ptr<FileStream> stream(new FileStream(path));
+ piex::PreviewImageData image_data;
+ if (!GetExifFromRawImage(stream.get(), path, image_data)) {
+ // Couldn't parse EXIF data from a image file via piex.
+ break;
}
- // XXX get this from exif, or parse jpeg header instead?
- ExifEntry *w = exif_content_get_entry(
- exifdata->ifd[EXIF_IFD_EXIF], EXIF_TAG_PIXEL_X_DIMENSION);
- ExifEntry *h = exif_content_get_entry(
- exifdata->ifd[EXIF_IFD_EXIF], EXIF_TAG_PIXEL_Y_DIMENSION);
- info.mThumbCompressedSize = exifdata->data ? exifdata->size : 0;
+ info.mThumbCompressedSize = image_data.thumbnail_length;
info.mThumbFormat = MTP_FORMAT_EXIF_JPEG;
- info.mImagePixWidth = w ? getLongFromExifEntry(w) : 0;
- info.mImagePixHeight = h ? getLongFromExifEntry(h) : 0;
- exif_data_unref(exifdata);
+ info.mImagePixWidth = image_data.full_width;
+ info.mImagePixHeight = image_data.full_height;
+
+ break;
}
}
@@ -855,19 +884,55 @@
void* result = NULL;
outThumbSize = 0;
- if (getObjectFilePath(handle, path, length, format) == MTP_RESPONSE_OK
- && (format == MTP_FORMAT_EXIF_JPEG || format == MTP_FORMAT_JFIF)) {
-
- ExifData *exifdata = exif_data_new_from_file(path);
- if (exifdata) {
- if (exifdata->data) {
- result = malloc(exifdata->size);
- if (result) {
- memcpy(result, exifdata->data, exifdata->size);
- outThumbSize = exifdata->size;
+ if (getObjectFilePath(handle, path, length, format) == MTP_RESPONSE_OK) {
+ switch (format) {
+ case MTP_FORMAT_EXIF_JPEG:
+ case MTP_FORMAT_JFIF: {
+ ExifData *exifdata = exif_data_new_from_file(path);
+ if (exifdata) {
+ if (exifdata->data) {
+ result = malloc(exifdata->size);
+ if (result) {
+ memcpy(result, exifdata->data, exifdata->size);
+ outThumbSize = exifdata->size;
+ }
+ }
+ exif_data_unref(exifdata);
}
+ break;
}
- exif_data_unref(exifdata);
+
+ // See the above comment on getObjectInfo() method.
+ case MTP_FORMAT_DNG:
+ case MTP_FORMAT_TIFF:
+ case MTP_FORMAT_TIFF_EP:
+ case MTP_FORMAT_DEFINED: {
+ std::unique_ptr<FileStream> stream(new FileStream(path));
+ piex::PreviewImageData image_data;
+ if (!GetExifFromRawImage(stream.get(), path, image_data)) {
+ // Couldn't parse EXIF data from a image file via piex.
+ break;
+ }
+
+ if (image_data.thumbnail_length == 0) {
+ // No thumbnail.
+ break;
+ }
+
+ result = malloc(image_data.thumbnail_length);
+ if (result) {
+ piex::Error err = stream.get()->GetData(
+ image_data.thumbnail_offset,
+ image_data.thumbnail_length,
+ (std::uint8_t *)result);
+ if (err == piex::Error::kOk) {
+ outThumbSize = image_data.thumbnail_length;
+ } else {
+ free(result);
+ }
+ }
+ break;
+ }
}
}
diff --git a/native/android/choreographer.cpp b/native/android/choreographer.cpp
index cc2fd77..e35c85b 100644
--- a/native/android/choreographer.cpp
+++ b/native/android/choreographer.cpp
@@ -82,7 +82,7 @@
};
-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 9c01f4f..f37ec58 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -1291,6 +1291,7 @@
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 @@
}
mRequestRender = false;
sGLThreadManager.notifyAll();
+ if (mWantRenderNotification) {
+ wantRenderNotification = true;
+ }
break;
}
}
@@ -1574,8 +1578,9 @@
break;
}
- if (mWantRenderNotification) {
+ if (wantRenderNotification) {
doRenderNotification = true;
+ wantRenderNotification = false;
}
}
@@ -1625,11 +1630,21 @@
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 @@
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 a08e029..b0331be 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/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index f43b81c..ad8b0d1 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -107,6 +107,8 @@
? 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/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index 58537ee..5abe7f6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -282,6 +282,7 @@
}
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 0000000..eb90b75
--- /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 beff196..c2aeb86 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -180,10 +180,14 @@
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 809db31..74fa8d0 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 @@
FileOperations.start(
getActivity(),
getDisplayState().selectedDocumentsForCopy,
+ getDisplayState().stack.peek(),
(DocumentStack) data.getParcelableExtra(Shared.EXTRA_STACK),
operationType);
}
@@ -782,20 +783,21 @@
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());
+ 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, getDisplayState().stack,
- DELETE_JOB_DELAY);
- showDeleteSnackbar(hidden, operationId);
- }
- }.execute(selected);
+ 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 f3195a7..1d8eba5 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 @@
"Copying " + srcInfo.displayName + " (" + srcInfo.derivedUri + ")"
+ " to " + dstInfo.displayName + " (" + dstInfo.derivedUri + ")");
- processDocument(srcInfo, dstInfo);
+ processDocument(srcInfo, null, dstInfo);
}
}
@@ -220,11 +220,15 @@
* 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 @@
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 @@
.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 6a2a794..eef696a 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 @@
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 @@
*
* @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 @@
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 @@
.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 aca2d7a..5d74cdc 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 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 @@
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 @@
*/
@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 @@
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 f59a32a..9d017ee 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_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.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 @@
* 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 @@
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 @@
}
/**
- * 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 @@
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 @@
* @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 @@
* @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,7 +184,7 @@
}
/**
- * 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.
@@ -179,13 +192,35 @@
* @return Id of the job.
*/
public static Intent createBaseIntent(
- @OpType int operationType, Context context, String jobId,
- List<DocumentInfo> srcDocs, DocumentStack localeStack) {
+ @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.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, 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.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 f351df9..9534d6c 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 @@
}
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 2a0262c..9b72077 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java
@@ -33,9 +33,11 @@
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}.
@@ -45,10 +47,12 @@
* @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
@@ -76,8 +80,8 @@
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!
@@ -86,7 +90,8 @@
if (src.authority.equals(dest.authority)) {
if ((src.flags & Document.FLAG_SUPPORTS_MOVE) != 0) {
if (DocumentsContract.moveDocument(getClient(src), src.derivedUri,
- Uri.EMPTY /* Not used yet */, dest.derivedUri) == null) {
+ srcParent != null ? srcParent.derivedUri : mSrcParent.derivedUri,
+ dest.derivedUri) == null) {
onFileFailed(src);
return false;
}
@@ -106,6 +111,7 @@
// 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);
}
@@ -115,7 +121,8 @@
.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/services/AbstractCopyJobTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/services/AbstractCopyJobTest.java
index ec21150..eb8a061 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.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 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 @@
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 @@
}
/**
- * 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 691af6a..e559503 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 @@
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 @@
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 1acf2dc..543396e 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 @@
}
@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 d6d10239..722df75 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 @@
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 @@
/**
* 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 69d2db7..749264a 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 @@
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 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 @@
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/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index b4f9b9f..a8419bf 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -188,7 +188,7 @@
/** 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 StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
+ public StrongAuthTracker(Context context) {
+ super(context);
+ }
public boolean isUnlockingWithFingerprintAllowed() {
int userId = getCurrentUser();
@@ -981,6 +984,7 @@
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/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
index cd30e26..46a2098 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 @@
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 811adda..743df99 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 @@
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 @@
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 cbc568a..6d60bb8 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 @@
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 15b2a97..3e1fc4a 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 6dfa9ad..d3c8416 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 d69250b..d3c1364 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.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.view.MenuItem;
import android.widget.TextView;
+import com.android.internal.widget.LockPatternUtils;
+
import java.util.List;
/**
@@ -60,15 +63,18 @@
*
* @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 @@
}
/**
- * 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 @@
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 @@
}
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 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 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 @@
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 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 13a46d0..810f6eb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
@@ -65,6 +65,10 @@
}
}
+ 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 06aba96..9bd4eb1 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.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 @@
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 @@
}
}
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 @@
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 84e2bff..6cae8aa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -65,6 +65,10 @@
}
}
+ 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 58a477e..1859207 100644
--- a/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java
+++ b/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java
@@ -17,6 +17,7 @@
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 @@
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 @@
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 @@
}
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 @@
}
}
+ 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 2fd043f..dc6002d 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 @@
@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 3fc0c22..56c4edb 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 @@
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 @@
private SettingsDrawerAdapter mDrawerAdapter;
private DrawerLayout mDrawerLayout;
+ private boolean mShowingMenu;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -94,7 +97,7 @@
@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 @@
new CategoriesUpdater().execute();
}
+ if (getIntent() != null && getIntent().getBooleanExtra(EXTRA_SHOW_MENU, false)) {
+ showMenuIcon();
+ }
}
@Override
@@ -171,13 +177,17 @@
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 @@
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 5d8668b..e53dd2f 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 @@
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 52e1b56..5908d02 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 @@
*/
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 @@
*/
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 fadfe63..e901e40 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 6a0277f..ae45663 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 6581942..b6a8b70 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 686df2c..1bf7d4c 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 3662e5a..31c0162 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 f09c01b..e98bfb8 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 e226805..0000000
--- 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 9ef743d..6e4b213 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 479f18d..7677646 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 53273cf..dad7cea 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 f430fa5..e56431b 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 2377684..6784695 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 12cf137..89abe2d 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 5eca471..dd75dbf 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 e220a16..0000000
--- 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 4c80b48..3dca77d 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 7cbc55c..34796cd 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 b2190ec..5e25d2c 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 aedc2c5..6df31ff 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 46a0f2a..e245c24 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 ecdccee..04402b7 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 0bd350b..87aedab 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 faf36ec..02158ac 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 9193754..67a7a23 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 99028a6c..001d1f2 100644
--- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
@@ -32,7 +32,7 @@
/**
* 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 fd4161f..5b955a4 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.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 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 @@
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 @@
}
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 c9ba885..25f8bb0 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.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 @@
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 @@
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 @@
}
});
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 c3a8f2e..92cd027 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.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 @@
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 67017db..34770c4 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.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 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 @@
.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 @@
.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 a3372a8..2d933f6 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.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 @@
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 @@
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 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 @@
@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 543a2f3..8ae2d7b 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 @@
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 b36b95a..37085c7 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 @@
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 6b49195..cb8f0e7 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 @@
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 2baefd5..b8310f2 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 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 @@
}
@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 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 @@
@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 3a3b19d..189e8d3 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.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.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 @@
// 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 @@
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 @@
EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
} else {
EventBus.getDefault().send(
- new FocusNextTaskViewEvent(false /* showTimerIndicator */));
+ new FocusNextTaskViewEvent(0 /* timerIndicatorDuration */));
}
mLastTabKeyEventTime = SystemClock.elapsedRealtime();
@@ -550,7 +564,7 @@
}
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 @@
@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 @@
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 @@
}
}
+ // Focus the next task
+ EventBus.getDefault().send(new FocusNextTaskViewEvent(timerIndicatorDuration));
+
MetricsLogger.action(this, MetricsLogger.ACTION_OVERVIEW_PAGE);
}
}
@@ -648,6 +662,9 @@
} else {
dismissRecentsToHome(true /* animateTaskViews */);
}
+
+ // Cancel any pending dozes
+ EventBus.getDefault().send(mUserInteractionEvent);
} else {
// Do nothing
}
@@ -670,6 +687,21 @@
}
}
+ 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 4ab0740..0afa1f6 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 @@
* 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 5cd540b..49f2ab0 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 @@
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 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 @@
}
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 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 @@
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 5f11bee..b78fd22 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.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.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 @@
* 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 @@
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 @@
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 0000000..86ec98a
--- /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 fb21500..ae0051c 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.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 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 0000000..264c2c4
--- /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/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsTaskStackAnimationCompletedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsTaskStackAnimationCompletedEvent.java
new file mode 100644
index 0000000..ee0df87
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsTaskStackAnimationCompletedEvent.java
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+/**
+ * This is sent when the in-app animations into Recents completes.
+ */
+public class EnterRecentsTaskStackAnimationCompletedEvent extends EventBus.AnimatedEvent {
+ // Simple event
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/RecentsActivityStartingEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/RecentsActivityStartingEvent.java
new file mode 100644
index 0000000..a2ecfe2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/RecentsActivityStartingEvent.java
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+/**
+ * Called after recents activity is being started, i.e. startActivity has just been called.
+ */
+public class RecentsActivityStartingEvent extends EventBus.Event{
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/RecentsDrawnEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/RecentsDrawnEvent.java
new file mode 100644
index 0000000..5483166
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/RecentsDrawnEvent.java
@@ -0,0 +1,26 @@
+/*
+ * 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.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 {
+
+}
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 5a132c2..39e4c1d 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 @@
/**
* 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 def4ae1..a1e4957 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 @@
*/
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 dc8f547..244c0df 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 @@
}
/**
+ * 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 87cfcff1..2882cec 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 @@
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 ccc8581..7423b78 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.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 @@
GradientDrawable mFreeformWorkspaceBackground;
ObjectAnimator mFreeformWorkspaceBackgroundAnimator;
ViewPool<TaskView, Task> mViewPool;
- boolean mStartTimerIndicator;
ArrayList<TaskView> mTaskViews = new ArrayList<>();
ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>();
@@ -127,6 +128,7 @@
int mTaskCornerRadiusPx;
private int mDividerSize;
+ private int mStartTimerIndicatorDuration;
boolean mTaskViewsClipDirty = true;
boolean mAwaitingFirstLayout = true;
@@ -796,7 +798,7 @@
*/
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 @@
* @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 @@
// 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 @@
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 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 @@
* 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 @@
}
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 @@
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 @@
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 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 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 @@
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 cb108da..d5aea9d 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 @@
Interpolator mLinearOutSlowInInterpolator;
private CountDownTimer mFocusTimerCountDown;
- private long mFocusTimerDuration;
public TaskViewHeader(Context context) {
this(context, null);
@@ -216,7 +215,6 @@
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 @@
}
/** 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 @@
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 08793e8..c0e1e44 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.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 @@
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 @@
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 @@
}
@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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
display.getDisplayInfo(info);
mDisplayWidth = info.logicalWidth;
mDisplayHeight = info.logicalHeight;
+ mSnapAlgorithm = null;
}
private int calculatePosition(int touchX, int touchY) {
@@ -607,4 +660,33 @@
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 38d24ce..01bfcea 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 @@
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.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 @@
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 @@
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 @@
mBackgroundDimmed.setCustomBackground(R.drawable.notification_material_bg_dim);
updateBackground();
updateBackgroundTint();
+ updateOutlineAlpha();
}
private final Runnable mTapTimeoutRunnable = new Runnable() {
@@ -272,7 +291,7 @@
}
}
- private void startActivateAnimation(boolean reverse) {
+ private void startActivateAnimation(final boolean reverse) {
if (!isAttachedToWindow()) {
return;
}
@@ -291,8 +310,8 @@
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 @@
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 @@
} 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 @@
.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 @@
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 @@
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 @@
mBackgroundAnimator = null;
}
});
+ mBackgroundAnimator.addUpdateListener(mBackgroundVisibilityUpdater);
mBackgroundAnimator.start();
}
@@ -504,6 +561,8 @@
mBackgroundNormal.setAlpha(1f);
removeCallbacks(mTapTimeoutRunnable);
}
+ setNormalBackgroundVisibilityAmount(
+ mBackgroundNormal.getVisibility() == View.VISIBLE ? 1.0f : 0.0f);
}
protected boolean shouldHideBackground() {
@@ -577,16 +636,16 @@
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 @@
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 2592486..efa56bc 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 @@
protected DevicePolicyManager mDevicePolicyManager;
protected IDreamManager mDreamManager;
- PowerManager mPowerManager;
+ protected PowerManager mPowerManager;
protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
// public mode, private notifications, etc
@@ -219,8 +219,6 @@
// which notification is currently being longpress-examined by the user
private NotificationGuts mNotificationGutsExposed;
- private TimeInterpolator mLinearOutSlowIn, mFastOutLinearIn;
-
private KeyboardShortcuts mKeyboardShortcuts;
/**
@@ -638,11 +636,6 @@
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 @@
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 @@
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 f71f092..24cd948 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 @@
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 @@
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 8570198..5c83f5f 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.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 @@
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 @@
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 @@
}
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 @@
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 93be009..6fae3ab 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 @@
}
@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 a6fc4bb..44c6a5d 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.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 @@
getWidth(),
Math.max(getActualHeight(), mClipTopAmount));
} else {
- outline.setRoundRect(mOutlineRect, mRoundedRectCornerRadius);
+ outline.setRect(mOutlineRect);
}
outline.setAlpha(mOutlineAlpha);
}
@@ -69,8 +64,10 @@
}
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 c190864..a0fb34a 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 @@
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 0fa088b..c4ffd7f 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 @@
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 @@
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 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 @@
// 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 @@
// 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 0000000..5979468
--- /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 8058933..841b9d0 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.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 36cf906..635e66d 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 @@
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 @@
}
};
- 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 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 @@
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 @@
mExpandedChild.addOnLayoutChangeListener(mLayoutUpdater);
mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child);
selectLayout(false /* animate */, true /* force */);
- updateRoundRectClipping();
}
public void setHeadsUpChild(View child) {
@@ -281,7 +265,6 @@
mHeadsUpChild.addOnLayoutChangeListener(mLayoutUpdater);
mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child);
selectLayout(false /* animate */, true /* force */);
- updateRoundRectClipping();
}
@Override
@@ -344,27 +327,6 @@
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 @@
boolean singleLineVisible = visibleType == VISIBLE_TYPE_SINGLELINE;
mSingleLineView.setVisible(singleLineVisible);
}
- updateRoundRectClipping();
}
private void animateToVisibleType(int visibleType) {
@@ -426,7 +387,6 @@
hiddenView.setVisible(false);
}
});
- updateRoundRectClipping();
}
/**
@@ -553,7 +513,6 @@
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 e4cd7d9..5abd1d5 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 @@
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 682676b..4e3ecb1 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.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 @@
*/
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 @@
mAlphaAnimator = null;
}
};
+ private Runnable mChangeRunnable;
public ScrimView(Context context) {
this(context, null);
@@ -72,15 +79,43 @@
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 @@
mIsEmpty = Color.alpha(color) == 0;
mScrimColor = color;
invalidate();
+ if (mChangeRunnable != null) {
+ mChangeRunnable.run();
+ }
}
}
@@ -105,8 +143,13 @@
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 @@
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 1fc8744..0000000
--- 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 2f66c41..c836637 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.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 @@
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 3e2c4c6..bed64a3 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 @@
* 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 @@
private List<CarNavigationButton> mNavButtons = new ArrayList<CarNavigationButton>();
private int mCurrentFacetIndex;
+ private String mCurrentPackageName;
public CarNavigationBarController(Context context,
CarNavigationBarView navBar,
@@ -75,6 +80,7 @@
}
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 @@
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 @@
}
}
- 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 @@
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- setCurrentFacet(id);
onFacetClicked(id);
}
});
@@ -245,13 +251,13 @@
@Override
public boolean onLongClick(View v) {
onFacetLongClicked(id);
- setCurrentFacet(id);
return true;
}
});
} else {
button.setLongClickable(false);
}
+
return button;
}
@@ -262,14 +268,35 @@
}
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 36b3a8a..504f059 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 @@
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 @@
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 fd65aac..97bf4b4 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 @@
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 4fd4cab..f43a5d0 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 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 @@
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 @@
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 @@
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 a959e07..3475d13 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.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 @@
- 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 @@
.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 0ceba78..a1cf07e 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 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 388ba0e..3e1c40a 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.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 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 @@
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 @@
}
}
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 @@
.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 a3f404a..37e5558 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.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 @@
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 @@
mWarning = context.getColor(com.android.internal.R.color.battery_saver_mode_color);
}
mGradient = context.getDrawable(gradientResourceId);
- mInterpolator = new LinearInterpolator();
}
@Override
@@ -222,7 +221,8 @@
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 3ff69c9..b5dba18 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.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 @@
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 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 @@
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 @@
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 @@
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 d2bec7c..0000000
--- 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 26abc48..e7064e5 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 @@
private boolean mCollapseSnoozes;
private NotificationPanelView mPanel;
private ExpandableNotificationRow mPickedChild;
- private final int mNotificationsTopPadding;
public HeadsUpTouchHelper(HeadsUpManager headsUpManager,
NotificationStackScrollLayout stackScroller,
@@ -54,8 +53,6 @@
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 @@
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 @@
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 41adeb5..c220efe 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.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 @@
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 @@
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 @@
}
}
});
- animator.setInterpolator(mAppearInterpolator);
+ animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
animator.setDuration(HINT_PHASE1_DURATION);
animator.start();
mSwipeAnimator = animator;
@@ -292,7 +285,7 @@
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 14176a6..94d3829 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.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 @@
private AccessibilityController mAccessibilityController;
private PhoneStatusBar mPhoneStatusBar;
- private final Interpolator mLinearOutSlowInInterpolator;
private boolean mUserSetupComplete;
private boolean mPrewarmBound;
private Messenger mPrewarmMessenger;
@@ -146,8 +146,6 @@
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 @@
mIndicationText.setAlpha(0f);
mIndicationText.animate()
.alpha(1f)
- .setInterpolator(mLinearOutSlowInInterpolator)
+ .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
}
@@ -610,7 +608,7 @@
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 f41e47b..e67aa84 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.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 @@
private KeyguardUserSwitcher mKeyguardUserSwitcher;
private int mSystemIconsSwitcherHiddenExpandedMargin;
- private Interpolator mFastOutSlowInInterpolator;
public KeyguardStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -73,8 +71,6 @@
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 @@
.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 @@
.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 @@
.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 71267cd..8717a15 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 @@
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 ed6d940..0000000
--- 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 76a9798..0000000
--- 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 abe357a..92288a3 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 @@
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 @@
< 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 beeee0b..7395a33 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 @@
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 @@
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 @@
(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 839b579..d86629f 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 @@
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 @@
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 ba20679..8e89efd 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.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.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 @@
private View mQsNavbarScrim;
private NotificationsQuickSettingsContainer mNotificationContainerParent;
private NotificationStackScrollLayout mNotificationStackScroller;
- private int mNotificationTopPadding;
private boolean mAnimateNextTopPaddingChange;
private int mTrackingPointer;
@@ -151,9 +151,6 @@
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 @@
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 @@
@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 @@
}
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 @@
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 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 @@
if (!(child instanceof ExpandableNotificationRow)) {
continue;
}
- availableSpace -= child.getMinHeight() + keyguardPadding;
+ availableSpace -= child.getMinHeight() + notificationPadding;
if (availableSpace >= 0 && count < maximum) {
count++;
} else {
@@ -471,7 +460,7 @@
}
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 @@
.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 @@
- 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 @@
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 @@
.y(-mQsContainer.getHeight())
.setStartDelay(0)
.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
- .setInterpolator(mFastOutSlowInInterpolator)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.start();
}
@@ -1174,7 +1163,7 @@
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 @@
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 @@
.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 @@
.alpha(0f)
.setStartDelay(0)
.setDuration(160)
- .setInterpolator(PhoneStatusBar.ALPHA_OUT)
+ .setInterpolator(Interpolators.ALPHA_OUT)
.withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable);
if (keyguardFadingAway) {
mKeyguardStatusView.animate()
@@ -1263,7 +1252,7 @@
.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 @@
// 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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
*/
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 f0b7894..f036d04 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.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.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 @@
private float mInitialTouchX;
private boolean mTouchDisabled;
- private Interpolator mLinearOutSlowInInterpolator;
- private Interpolator mFastOutSlowInInterpolator;
private Interpolator mBounceInterpolator;
protected KeyguardBottomAreaView mKeyguardBottomArea;
@@ -161,7 +159,7 @@
}
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 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 @@
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 @@
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 e2a6184..6fa1f5df 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.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.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.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.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.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 @@
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 @@
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 @@
@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 @@
}
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 @@
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 @@
// 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 @@
// behind.
.setDuration(mKeyguardFadingAwayDuration / 2)
.setStartDelay(mKeyguardFadingAwayDelay)
- .setInterpolator(mLinearInterpolator)
+ .setInterpolator(Interpolators.LINEAR)
.start();
}
}
@@ -3740,7 +3758,7 @@
mNotificationPanel.setBarState(mState, mKeyguardFadingAway, goingToFullShade);
updateDozingState();
updatePublicMode();
- updateStackScrollerState(goingToFullShade);
+ updateStackScrollerState(goingToFullShade, fromShadeLocked);
updateNotifications();
checkBarModes();
updateMediaMetaData(false);
@@ -3761,11 +3779,11 @@
!= 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 5b44f0a..f18c341 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.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 @@
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 @@
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 @@
? 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 @@
setupHost(qsPanel.getHost());
if (mQsPanel != null) {
mQsPanel.setCallback(mQsPanelCallback);
+ mMultiUserSwitch.setQsPanel(qsPanel);
}
}
@@ -254,7 +267,12 @@
@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 403199a..f310c2c2 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.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 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 512af1b..7247b57 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.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 @@
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 64fb066..c6537e8 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.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 @@
private Context mContext;
private PhoneStatusBar mPhoneStatusBar;
- private Interpolator mLinearOutSlowIn;
- private Interpolator mFastOutSlowIn;
private DemoStatusIcons mDemoStatusIcons;
private LinearLayout mSystemIconArea;
@@ -129,10 +127,6 @@
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 @@
.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 @@
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 @@
if (mPhoneStatusBar.isKeyguardFadingAway()) {
v.animate()
.setDuration(mPhoneStatusBar.getKeyguardFadingAwayDuration())
- .setInterpolator(mLinearOutSlowIn)
+ .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
.setStartDelay(mPhoneStatusBar.getKeyguardFadingAwayDelay())
.start();
}
@@ -419,7 +413,7 @@
});
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 56c1e10..1aae496 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.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 @@
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 @@
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 @@
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 4ae0321..59d54ff 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 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 @@
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 @@
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 dd7bd56..f065522 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 @@
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 @@
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 @@
}
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 3f63b5f..d739d6c1 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.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 @@
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 @@
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 @@
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 5cf6156..0959b0c 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.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 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 @@
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 @@
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 @@
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 dbb0295..107a904 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 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 56f6564..561b18a 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 @@
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 @@
return this;
}
- public AnimationFilter animateScale() {
- animateScale = true;
- return this;
- }
-
public AnimationFilter animateHeight() {
animateHeight = true;
return this;
@@ -87,6 +82,11 @@
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 @@
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 @@
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 dc40fb0..409dac1 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 @@
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 @@
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 @@
* @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 36b2810..d5b57ac 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.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.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 @@
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 @@
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 @@
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 @@
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 @@
*/
private float mMinTopOverScrollToEscape;
private int mIntrinsicPadding;
- private int mNotificationTopPadding;
private float mStackTranslation;
private float mTopPaddingOverflow;
private boolean mDontReportNextOverScroll;
@@ -234,6 +242,45 @@
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 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 @@
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 @@
}
}
+ 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 @@
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 @@
@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 @@
mRequestViewResizeAnimationOnLayout = false;
}
requestChildrenUpdate();
+ updateFirstAndLastBackgroundViews();
}
private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
@@ -378,18 +427,6 @@
}
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 @@
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 @@
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 @@
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;
+ int padding = needsIncreasedPaddings || previousNeedsIncreasedPaddings
+ ? mIncreasedPaddingBetweenElements
+ : mPaddingBetweenElements;
+ height += padding;
}
- if (child instanceof ExpandableView) {
- ExpandableView expandableView = (ExpandableView) child;
- height += expandableView.getIntrinsicHeight();
- }
+ 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 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 @@
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 @@
}
((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 @@
*
* @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 @@
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 @@
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 @@
if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
mGoToFullShadeDelay);
+ setAnimationRunning(true);
mAnimationEvents.clear();
+ updateBackground();
} else {
applyCurrentState();
}
@@ -2396,6 +2684,7 @@
}
public void onChildAnimationFinished() {
+ setAnimationRunning(false);
requestChildrenUpdate();
runAnimationFinishedRunnables();
clearViewOverlays();
@@ -2418,16 +2707,38 @@
* 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 @@
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 @@
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 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 void setScrimController(ScrimController scrimController) {
mScrimController = scrimController;
+ mScrimController.setScrimBehindChangeRunnable(new Runnable() {
+ @Override
+ public void run() {
+ updateBackgroundDimming();
+ }
+ });
}
public void forceNoOverlappingRendering(boolean force) {
@@ -2892,6 +3197,17 @@
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 @@
// ANIMATION_TYPE_ADD
new AnimationFilter()
- .animateAlpha()
+ .animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
@@ -2946,7 +3262,7 @@
// ANIMATION_TYPE_REMOVE
new AnimationFilter()
- .animateAlpha()
+ .animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
@@ -2955,7 +3271,7 @@
// ANIMATION_TYPE_REMOVE_SWIPED_OUT
new AnimationFilter()
- .animateAlpha()
+ .animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
@@ -2964,37 +3280,35 @@
// 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 @@
// ANIMATION_TYPE_GO_TO_FULL_SHADE
new AnimationFilter()
- .animateAlpha()
+ .animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
.animateDimmed()
- .animateScale()
.animateZ()
.hasDelays(),
@@ -3022,7 +3335,7 @@
// ANIMATION_TYPE_VIEW_RESIZE
new AnimationFilter()
- .animateAlpha()
+ .animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
@@ -3031,6 +3344,7 @@
// ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
new AnimationFilter()
.animateAlpha()
+ .animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
@@ -3038,7 +3352,7 @@
// ANIMATION_TYPE_HEADS_UP_APPEAR
new AnimationFilter()
- .animateAlpha()
+ .animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
@@ -3046,7 +3360,7 @@
// ANIMATION_TYPE_HEADS_UP_DISAPPEAR
new AnimationFilter()
- .animateAlpha()
+ .animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
@@ -3054,7 +3368,7 @@
// ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
new AnimationFilter()
- .animateAlpha()
+ .animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
@@ -3063,7 +3377,7 @@
// ANIMATION_TYPE_HEADS_UP_OTHER
new AnimationFilter()
- .animateAlpha()
+ .animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
@@ -3072,8 +3386,8 @@
// 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 822012d..f6959f0 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.ExpandableView;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
/**
@@ -41,15 +41,13 @@
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 @@
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 @@
}
private void updatePadding() {
- mPaddingBetweenElements = mDimmed && mScaleDimmed
- ? mPaddingBetweenElementsDimmed
- : mPaddingBetweenElementsNormal;
mTopStackTotalSize = mTopStackSlowDownLength + mPaddingBetweenElements
+ mTopStackPeekSize;
mTopStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
@@ -104,35 +94,25 @@
}
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 @@
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 @@
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 @@
} 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 @@
} else {
previousNotificationIsSwiped = ambientState.getDraggedViews().contains(child);
previousNotificationEnd = newNotificationEnd;
- previousNotificationStart = newYTranslation + state.clipTopAmount * state.scale;
+ previousNotificationStart = newYTranslation + state.clipTopAmount;
}
}
}
@@ -276,13 +246,13 @@
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 @@
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 @@
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 @@
}
/**
- * 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 @@
}
}
}
+ lastView = v;
}
}
}
@@ -414,15 +392,17 @@
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 @@
// 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 @@
// 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 @@
// 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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 void setDimmed(boolean dimmed) {
- mDimmed = dimmed;
- updatePadding();
- }
-
public void onReset(ExpandableView view) {
if (view.equals(mFirstChildWhileExpanding)) {
updateFirstChildMaxSizeToMaxHeight();
@@ -969,6 +955,11 @@
* 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 e155d70..1fedc1e 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.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 @@
// 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 @@
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 @@
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 @@
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 @@
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 9d5e072..e75e8e8 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.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 @@
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 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 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 @@
}
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 @@
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 @@
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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
+ /**
+ * 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 55ef440..41824ea 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 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 @@
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 3e538df..5beaac3 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 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 b237400..e947ed5 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 @@
}
}
};
+ private final Runnable mOnPinnedActivityRestartAttempt = new Runnable() {
+ @Override
+ public void run() {
+ movePipToFullscreen();
+ }
+ };
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -315,6 +321,8 @@
@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 9438af1..0000000
--- 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 ed6fc9e..b005a2b 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 @@
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 f51e8ff..0000000
--- 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 d63dd0c..0513e53 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 @@
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 @@
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 @@
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);
}
@@ -3155,8 +3168,9 @@
ParcelFileDescriptor backupData = null;
mStatus = BackupTransport.TRANSPORT_OK;
+ long size = 0;
try {
- int size = (int) mBackupDataName.length();
+ size = mBackupDataName.length();
if (size > 0) {
if (mStatus == BackupTransport.TRANSPORT_OK) {
backupData = ParcelFileDescriptor.open(mBackupDataName,
@@ -3201,6 +3215,10 @@
sendBackupOnResult(mObserver, pkgName,
BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
EventLogTags.writeBackupAgentFailure(pkgName, "Transport rejected");
+ } else if (mStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
+ sendBackupOnResult(mObserver, pkgName,
+ BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED);
+ EventLog.writeEvent(EventLogTags.BACKUP_QUOTA_EXCEEDED, pkgName);
} else {
// Actual transport-level failure to communicate the data to the backend
sendBackupOnResult(mObserver, pkgName, BackupManager.ERROR_TRANSPORT_ABORTED);
@@ -3221,6 +3239,20 @@
// Success or single-package rejection. Proceed with the next app if any,
// otherwise we're done.
nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
+ } else if (mStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Package " + mCurrentPackage.packageName +
+ " hit quota limit on k/v backup");
+ }
+ if (mAgentBinder != null) {
+ try {
+ long quota = mTransport.getBackupQuota(mCurrentPackage.packageName, false);
+ mAgentBinder.doQuotaExceeded(size, quota);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to contact backup agent for quota exceeded");
+ }
+ }
+ nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
} else {
// Any other error here indicates a transport-level failure. That means
// we need to halt everything and reschedule everything for next time.
@@ -3463,6 +3495,7 @@
OutputStream mOutput;
FullBackupPreflight mPreflightHook;
IFullBackupRestoreObserver mObserver;
+ IBackupAgent mAgent;
File mFilesDir;
File mManifestFile;
File mMetadataFile;
@@ -3549,14 +3582,14 @@
int result = BackupTransport.TRANSPORT_OK;
Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
- IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
+ mAgent = bindToAgentSynchronous(pkg.applicationInfo,
IApplicationThread.BACKUP_MODE_FULL);
- if (agent != null) {
+ if (mAgent != null) {
ParcelFileDescriptor[] pipes = null;
try {
// Call the preflight hook, if any
if (mPreflightHook != null) {
- result = mPreflightHook.preflightFullBackup(pkg, agent);
+ result = mPreflightHook.preflightFullBackup(pkg, mAgent);
if (MORE_DEBUG) {
Slog.v(TAG, "preflight returned " + result);
}
@@ -3579,7 +3612,7 @@
UserHandle.USER_SYSTEM);
final int token = generateToken();
- FullBackupRunner runner = new FullBackupRunner(pkg, agent, pipes[1],
+ FullBackupRunner runner = new FullBackupRunner(pkg, mAgent, pipes[1],
token, sendApk, !isSharedStorage, widgetBlob);
pipes[1].close(); // the runner has dup'd it
pipes[1] = null;
@@ -3627,6 +3660,16 @@
return result;
}
+ public void sendQuotaExceeded(final long backupDataBytes, final long quotaBytes) {
+ if (mAgent != null) {
+ try {
+ mAgent.doQuotaExceeded(backupDataBytes, quotaBytes);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception while telling agent about quota exceeded");
+ }
+ }
+ }
+
private void writeApkToBackup(PackageInfo pkg, FullBackupDataOutput output) {
// Forward-locked apps, system-bundled .apks, etc are filtered out before we get here
// TODO: handle backing up split APKs
@@ -4190,11 +4233,21 @@
// 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
@@ -4321,6 +4374,15 @@
}
} while (nRead > 0 && result == BackupTransport.TRANSPORT_OK);
+ if (result == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
+ long quota = transport.getBackupQuota(currentPackage.packageName, true);
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Package hit quota limit " + currentPackage.packageName
+ + ": " + totalRead + " of " + quota);
+ }
+ backupRunner.sendQuotaExceeded(totalRead, quota);
+ }
+
// If we've lost our running criteria, tell the transport to cancel
// and roll back this (partial) backup payload; otherwise tell it
// that we've reached the clean finish state.
@@ -4364,6 +4426,8 @@
}
if (result == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
+ sendBackupOnResult(mBackupObserver, currentPackage.packageName,
+ BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
if (DEBUG) {
Slog.i(TAG, "Transport rejected backup of "
+ currentPackage.packageName
@@ -4371,35 +4435,40 @@
}
EventLog.writeEvent(EventLogTags.FULL_BACKUP_AGENT_FAILURE,
currentPackage.packageName, "transport rejected");
- sendBackupOnResult(mBackupObserver, currentPackage.packageName,
- BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
// do nothing, clean up, and continue looping
+ } else if (result == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
+ sendBackupOnResult(mBackupObserver, currentPackage.packageName,
+ BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED);
+ Slog.w(TAG, "Transport quota exceeded; aborting backup: " + result);
+ EventLog.writeEvent(EventLogTags.FULL_BACKUP_QUOTA_EXCEEDED,
+ currentPackage.packageName);
+ return;
} else if (result != BackupTransport.TRANSPORT_OK) {
- Slog.w(TAG, "Transport failed; aborting backup: " + result);
- EventLog.writeEvent(EventLogTags.FULL_BACKUP_TRANSPORT_FAILURE);
sendBackupOnResult(mBackupObserver, currentPackage.packageName,
BackupManager.ERROR_TRANSPORT_ABORTED);
+ Slog.w(TAG, "Transport failed; aborting backup: " + result);
+ EventLog.writeEvent(EventLogTags.FULL_BACKUP_TRANSPORT_FAILURE);
return;
} else {
// Success!
+ sendBackupOnResult(mBackupObserver, currentPackage.packageName,
+ BackupManager.SUCCESS);
EventLog.writeEvent(EventLogTags.FULL_BACKUP_SUCCESS,
currentPackage.packageName);
logBackupComplete(currentPackage.packageName);
- sendBackupOnResult(mBackupObserver, currentPackage.packageName,
- BackupManager.SUCCESS);
}
cleanUpPipes(transportPipes);
cleanUpPipes(enginePipes);
currentPackage = null;
}
+ sendBackupFinished(mBackupObserver, BackupManager.SUCCESS);
if (DEBUG) {
Slog.i(TAG, "Full backup completed.");
}
- sendBackupFinished(mBackupObserver, BackupManager.SUCCESS);
} catch (Exception e) {
- Slog.w(TAG, "Exception trying full transport backup", e);
sendBackupFinished(mBackupObserver, BackupManager.ERROR_TRANSPORT_ABORTED);
+ Slog.w(TAG, "Exception trying full transport backup", e);
} finally {
cleanUpPipes(transportPipes);
cleanUpPipes(enginePipes);
@@ -4480,6 +4549,14 @@
}
result = mTransport.checkFullBackupSize(totalSize);
+ if (result == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
+ final long quota = mTransport.getBackupQuota(pkg.packageName, true);
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Package hit quota limit on preflight " +
+ pkg.packageName + ": " + totalSize + " of " + quota);
+ }
+ agent.doQuotaExceeded(totalSize, quota);
+ }
} catch (Exception e) {
Slog.w(TAG, "Exception preflighting " + pkg.packageName + ": " + e.getMessage());
result = BackupTransport.AGENT_ERROR;
@@ -4527,6 +4604,7 @@
final PackageInfo mTarget;
final FullBackupPreflight mPreflight;
final CountDownLatch mLatch;
+ private FullBackupEngine mEngine;
SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target,
IBackupTransport transport, CountDownLatch latch) throws IOException {
@@ -4540,9 +4618,9 @@
public void run() {
try {
FileOutputStream out = new FileOutputStream(mOutput.getFileDescriptor());
- FullBackupEngine engine = new FullBackupEngine(out, mTarget.packageName,
+ mEngine = new FullBackupEngine(out, mTarget.packageName,
mPreflight, false);
- engine.backupOnePackage(mTarget);
+ mEngine.backupOnePackage(mTarget);
} catch (Exception e) {
Slog.e(TAG, "Exception during full package backup of " + mTarget);
} finally {
@@ -4555,6 +4633,10 @@
}
}
+ public void sendQuotaExceeded(final long backupDataBytes, final long quotaBytes) {
+ mEngine.sendQuotaExceeded(backupDataBytes, quotaBytes);
+ }
+
long expectedSize() {
return mPreflight.expectedSize();
}
@@ -4592,21 +4674,28 @@
}
/**
+ * 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 +4789,24 @@
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 +4836,19 @@
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 +4867,6 @@
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 c1a082b..499b706 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -114,13 +114,6 @@
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 @@
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 @@
mContext = context;
mBluetooth = null;
+ mBluetoothBinder = null;
mBluetoothGatt = null;
mBinding = false;
mUnbinding = false;
@@ -678,6 +673,7 @@
}
if (DBG) Log.d(TAG, "Sending unbind request.");
+ mBluetoothBinder = null;
mBluetooth = null;
//Unbind
mContext.unbindService(mConnection);
@@ -1166,6 +1162,7 @@
mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
mBinding = false;
+ mBluetoothBinder = service;
mBluetooth = IBluetooth.Stub.asInterface(service);
try {
@@ -1674,44 +1671,15 @@
}
@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/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 7bf1dea..c59ecec 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -108,6 +108,7 @@
2826 backup_reset (Transport|3)
2827 backup_initialize
2828 backup_requested (Total|1|1),(Key-Value|1|1),(Full|1|1)
+2829 backup_quota_exceeded (Package|3)
2830 restore_start (Transport|3),(Source|2|5)
2831 restore_transport_failure
2832 restore_agent_failure (Package|3),(Message|3)
@@ -119,6 +120,7 @@
2842 full_backup_transport_failure
2843 full_backup_success (Package|3)
2844 full_restore_package (Package|3)
+2845 full_backup_quota_exceeded (Package|3)
2850 backup_transport_lifecycle (Transport|3),(Bound|1|1)
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 377d52f..f6f05fe 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -78,7 +78,7 @@
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 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 0e4d5a7..551ceb8 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.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 @@
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 @@
}
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 @@
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 dd19c6a..95f5734 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -186,7 +186,6 @@
private final Handler mFgHandler;
private final Handler mDaemonHandler;
- private final PhoneStateListener mPhoneStateListener;
private IBatteryStats mBatteryStats;
@@ -283,22 +282,6 @@
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 19a4851..820551d 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.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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
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 2683be6..d0006aa 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 @@
}
}
- 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 @@
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 7ba6338..8c0ec78 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 @@
}
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 b8327c1..2c55ee2 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.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 @@
// 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 @@
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 @@
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 @@
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 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 @@
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 @@
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 @@
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 @@
// 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 @@
}
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 @@
}
}
- 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 @@
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 @@
}
}
- 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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);
- crashApplication(r, crashInfo);
+ mAppErrors.crashApplication(r, crashInfo);
}
public void handleApplicationStrictModeViolation(
@@ -13320,7 +12829,7 @@
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 @@
}
}
- /**
- * 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 @@
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 @@
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 @@
}
}
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 @@
}
}
- 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 4bac2d6..ef8d230 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 @@
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 1fc674b..8db2f8f 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 @@
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 c87eae0..b746a4b 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 @@
@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 @@
}
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 0000000..58d9f45
--- /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 f4c1664..4587b72 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 @@
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 622aa16..37b0af1 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 @@
@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 3f0674d..9c139d5 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_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.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.Set;
/**
* Class for managing the recent tasks list.
@@ -188,6 +191,21 @@
}
}
+ 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 @@
// 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 fda1ec1..6da84bd 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 @@
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 4ce8b2f..fd787df 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 @@
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 @@
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 @@
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 @@
voiceSession = null;
voiceInteractor = null;
realActivity = _realActivity;
+ realActivitySuspended = realActivitySuspended;
origActivity = _origActivity;
rootWasReset = _rootWasReset;
isAvailable = true;
@@ -1027,6 +1031,7 @@
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 @@
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 @@
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 @@
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/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2ee74db..7f783ec 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 @@
}
@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 490e890..484b0e9 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.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 @@
mCreationTimeMs = sbn.getPostTime();
mUpdateTimeMs = mCreationTimeMs;
mContext = context;
+ stats = new NotificationUsageStats.SingleNotificationStats();
mImportance = defaultImportance();
}
@@ -133,27 +135,22 @@
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 1cdc6db..e75324f 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 @@
* 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 @@
* 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 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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
}
}
+ 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 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 @@
@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 @@
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 @@
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 @@
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 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 @@
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 @@
@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 @@
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 @@
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 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 @@
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 @@
}
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 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 9b10ef2..7f85e1f 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 @@
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 ce4ecd3..827482f 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 @@
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 @@
}
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 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;
- updateConfig();
- }
-
- @Override
- public int getTopicVisibilityOverride(String packageName, int uid, Notification.Topic topic) {
- final Record r = getOrCreateRecord(packageName, uid);
- return getOrCreateTopic(r, topic).visibility;
- }
-
- @Override
- public void setTopicVisibilityOverride(String pkgName, int uid, Notification.Topic topic,
- int visibility) {
- final Record r = getOrCreateRecord(pkgName, uid);
- getOrCreateTopic(r, topic).visibility = visibility;
- updateConfig();
- }
-
- @Override
- public int getTopicImportance(String packageName, int uid, Notification.Topic topic) {
- final Record r = getOrCreateRecord(packageName, uid);
- 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;
+ if (topic == null) {
+ r.priority = priority;
+ for (Topic t : r.topics.values()) {
+ t.priority = priority;
+ }
+ } else {
+ getOrCreateTopic(r, topic).priority = priority;
+ }
updateConfig();
}
/**
- * Sets the default importance for all new topics that appear in the future, and resets
+ * Gets visual override. If a topic is given, returns the override of that topic. Otherwise, the
+ * override of the app.
+ */
+ @Override
+ 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 setVisibilityOverride(String pkgName, int uid, Notification.Topic topic,
+ int visibility) {
+ final Record r = getOrCreateRecord(pkgName, uid);
+ 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 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;
+ }
+
+ /**
+ * 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 @@
} 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 @@
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 @@
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 01770d0..c6b3e0f 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 @@
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 5bf989ae..1df5c2b 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 @@
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 e053382..eaa3ed3 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 @@
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 3452f41..b54e866 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 @@
@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 @@
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 @@
}
};
- 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 3ca99d4..62e7fb4 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 @@
}
} 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 68cfaab..5889fb8 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 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 @@
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 @@
@Override
public void take(IBinder activityToken) throws RemoteException {
- if (mActivityToken != null) {
+ if (mActivityToken != null || mPermissionOwnerToken != null) {
return;
}
mActivityToken = activityToken;
@@ -61,6 +62,10 @@
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 @@
}
@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 @@
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 5adf627..93a1015 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5349,9 +5349,31 @@
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 */
boolean isCurrentProfileLocked(int userId) {
if (userId == mCurrentUserId) return true;
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 5abb6e7..e6f4177 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.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.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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
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 @@
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 5ee4066..9b99c67 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 @@
}
}
- 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 @@
* @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 @@
}
}
- 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 78edc4d..fcf2fc8 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_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 @@
}
@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 @@
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 @@
* 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 @@
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 @@
mSpooler.setPrintJobCancelling(printJobId, true);
if (printJobInfo.getState() != PrintJobInfo.STATE_FAILED) {
- ComponentName printServiceName = printJobInfo.getPrinterId().getServiceName();
- RemotePrintService printService = null;
- synchronized (mLock) {
- printService = mActiveServices.get(printServiceName);
+ 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);
}
- if (printService == null) {
- return;
- }
- printService.onRequestCancelPrintJob(printJobInfo);
} else {
// If the print job is failed we do not need cooperation
// from the print service.
@@ -329,7 +334,7 @@
}
}
- 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 @@
}
}
- public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer) {
+ public void createPrinterDiscoverySession(@NonNull IPrinterDiscoveryObserver observer) {
synchronized (mLock) {
throwIfDestroyedLocked();
@@ -386,7 +391,7 @@
}
}
- 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 @@
}
}
- 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 @@
}
}
- public void stopPrinterDiscovery(IPrinterDiscoveryObserver observer) {
+ public void stopPrinterDiscovery(@NonNull IPrinterDiscoveryObserver observer) {
synchronized (mLock) {
throwIfDestroyedLocked();
// No services - nothing to do.
@@ -431,7 +436,7 @@
}
}
- 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 @@
}
}
- public void startPrinterStateTracking(PrinterId printerId) {
+ public void startPrinterStateTracking(@NonNull PrinterId printerId) {
synchronized (mLock) {
throwIfDestroyedLocked();
// No services - nothing to do.
@@ -479,7 +484,7 @@
}
}
- public void addPrintJobStateChangeListener(IPrintJobStateChangeListener listener,
+ public void addPrintJobStateChangeListener(@NonNull IPrintJobStateChangeListener listener,
int appId) throws RemoteException {
synchronized (mLock) {
throwIfDestroyedLocked();
@@ -497,7 +502,7 @@
}
}
- public void removePrintJobStateChangeListener(IPrintJobStateChangeListener listener) {
+ public void removePrintJobStateChangeListener(@NonNull IPrintJobStateChangeListener listener) {
synchronized (mLock) {
throwIfDestroyedLocked();
if (mPrintJobStateChangeListenerRecords == null) {
@@ -613,7 +618,7 @@
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 @@
}
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 @@
.sendToTarget();
}
- public void addObserverLocked(IPrinterDiscoveryObserver observer) {
+ public void addObserverLocked(@NonNull IPrinterDiscoveryObserver observer) {
// Add the observer.
mDiscoveryObservers.register(observer);
@@ -1058,7 +1063,7 @@
}
}
- 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 @@
}
}
- 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 @@
.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 @@
.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 @@
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 @@
}
}
- 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 @@
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/notification/RankingHelperTest.java b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
index 2640889..31182fc 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 @@
@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 aa95e1d..3859294 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -24,7 +24,6 @@
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 @@
* 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 @@
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 deb98f4..fa7a59d 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -255,8 +255,23 @@
*/
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 @@
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 320d274..630dacc 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -746,7 +746,7 @@
*/
@Nullable
public PersistableBundle getConfig() {
- return getConfigForSubId(SubscriptionManager.getDefaultSubId());
+ return getConfigForSubId(SubscriptionManager.getDefaultSubscriptionId());
}
/**
diff --git a/telephony/java/android/telephony/DataConnectionRealTimeInfo.java b/telephony/java/android/telephony/DataConnectionRealTimeInfo.java
index 96069213..f71f58d 100644
--- a/telephony/java/android/telephony/DataConnectionRealTimeInfo.java
+++ b/telephony/java/android/telephony/DataConnectionRealTimeInfo.java
@@ -28,10 +28,10 @@
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 553221d..b089387 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -2082,7 +2082,7 @@
* 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 @@
* 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 16472c8..ae130d4 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -194,10 +194,12 @@
* {@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 9998937..ff8c71c 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -53,7 +53,6 @@
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 @@
}
/**
- * 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 @@
}
/**
- * @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 @@
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 @@
// ignore it
}
- if (VDBG) logd("getDefaultVoiceSubId, sub id = " + subId);
+ if (VDBG) logd("getDefaultVoiceSubscriptionId, sub id = " + subId);
return subId;
}
@@ -949,23 +958,31 @@
}
}
- /** @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 @@
// ignore it
}
- if (VDBG) logd("getDefaultSmsSubId, sub id = " + subId);
+ if (VDBG) logd("getDefaultSmsSubscriptionId, sub id = " + subId);
return subId;
}
@@ -994,18 +1011,31 @@
}
}
- /** @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 @@
// ignore it
}
- if (VDBG) logd("getDefaultDataSubId, sub id = " + subId);
+ if (VDBG) logd("getDefaultDataSubscriptionId, sub id = " + subId);
return subId;
}
@@ -1034,14 +1064,21 @@
}
}
- /** @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 @@
//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 7eaa6a6..fcb42a4 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1232,7 +1232,6 @@
* 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 @@
*
* @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 @@
* @param phoneId
* @hide
**/
- public String getNetworkOperatorForPhone(int phoneId) {
+ public String getNetworkOperatorForPhone(int phoneId) {
return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, "");
}
@@ -1298,7 +1296,6 @@
*
* @param subId
*/
- /** {@hide} */
public boolean isNetworkRoaming(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
return Boolean.parseBoolean(getTelephonyProperty(phoneId,
@@ -1327,8 +1324,7 @@
*
* @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 @@
* 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 @@
* 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 @@
* 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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* 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 @@
* 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 @@
*
* @param subId whose subscriber id is returned
*/
- /** {@hide} */
public String getSubscriberId(int subId) {
try {
IPhoneSubInfo info = getSubscriberInfo();
@@ -2089,9 +2065,8 @@
* 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 @@
* The default SMS app can also use this.
*/
public String getLine1Number() {
- return getLine1NumberForSubscriber(getDefaultSubscription());
+ return getLine1Number(getDefaultSubscription());
}
/**
@@ -2134,8 +2109,7 @@
*
* @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 @@
* @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 @@
* @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 @@
* nobody seems to call this.
*/
public String getLine1AlphaTag() {
- return getLine1AlphaTagForSubscriber(getDefaultSubscription());
+ return getLine1AlphaTag(getDefaultSubscription());
}
/**
@@ -2226,8 +2199,7 @@
* @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 @@
* {@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 @@
* @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 @@
* @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 @@
*
* @param subId whose call state is returned
*/
- /** {@hide} */
public int getCallState(int subId) {
try {
ITelephony telephony = getITelephony();
@@ -3225,19 +3193,19 @@
* 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 @@
/** @hide */
@SystemApi
public void setDataEnabled(boolean enable) {
- setDataEnabled(SubscriptionManager.getDefaultDataSubId(), enable);
+ setDataEnabled(SubscriptionManager.getDefaultDataSubscriptionId(), enable);
}
/** @hide */
@@ -4201,7 +4169,7 @@
/** @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 b9d7297..5f3f773 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -327,7 +327,7 @@
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 @@
// 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 c754068..05cb31e 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
@@ -387,7 +387,7 @@
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 76b69ce..907d76e 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.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 @@
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 0c36063..4c12c2d 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 @@
}
}
+ @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 @@
return anchor.getTrustedCert();
}
+ @Override
public X509Certificate findByIssuerAndSignature(X509Certificate cert) {
java.security.cert.TrustAnchor anchor = mIndex.findByIssuerAndSignature(cert);
if (anchor == null) {
@@ -52,4 +56,13 @@
}
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/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 6e42391..605e8c0 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 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 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 @@
dest.writeString(entry.getValue());
}
- writeCertificate(dest, mCaCert);
+ writeCertificates(dest, mCaCerts);
if (mClientPrivateKey != null) {
String algorithm = mClientPrivateKey.getAlgorithm();
@@ -151,6 +162,17 @@
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 @@
enterpriseConfig.mFields.put(key, value);
}
- enterpriseConfig.mCaCert = readCertificate(in);
+ enterpriseConfig.mCaCerts = readCertificates(in);
PrivateKey userKey = null;
int len = in.readInt();
@@ -199,6 +221,18 @@
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 @@
}
/**
+ * 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 @@
}
/**
+ * 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 @@
}
/**
+ * 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 @@
* @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 50bec33..0000000
--- 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.aidl b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.aidl
deleted file mode 100644
index cfd3605..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.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 WifiPasspointCredential;
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 a100aed..0000000
--- 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.aidl b/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.aidl
deleted file mode 100644
index 6a88b2e..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.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 WifiPasspointDmTree;
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 427c84c7..0000000
--- 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.aidl b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.aidl
deleted file mode 100644
index 27f23bc..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.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 WifiPasspointInfo;
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 33db3f5..0000000
--- 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 0245a3d..0000000
--- 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.aidl b/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.aidl
deleted file mode 100644
index 088136f..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.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 WifiPasspointOsuProvider;
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 b54b70c..0000000
--- 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 1d61da0..0000000
--- 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 c08877e..0000000
--- 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];
- }
- };
-}